0
votes

When using MarkLogic 7 with the Java Client API, I am currently in the process of moving my query definitions from a StringQueryDefinition to a StructuredQueryDefinition allowing me to construct and manipulate the query programmatically.

With the string query I was able to use the sort operator with sort:{my-sort-order} successfully which in turn referred to names of predefined orders as specified in the query options (https://docs.marklogic.com/guide/search-dev/query-options#id_30002), but cannot find the API docs a related method allowing me to specify the sort order with the structured query builder.

What is the recommended way on how to specify the sort order when using a StructuredQueryDefinition?

UPDATE Based on Erik's proposal, this is how the code snippet currently looks like, but it doesn't solve the problem, since the operator-state has to go as child on the query element and not as child on the search element:

    RawStructuredQueryDefinition queryDef = qb.build(qb.and(qb.term(..), qb.rangeConstraint(...)));
    String sorting = "<operator-state><operator-name>sort</operator-name><state-name>" + orderBy + "</state-name></operator-state>";
    String combi = "<search xmlns='http://marklogic.com/appservices/search'>" + queryDef.toString() + sorting + "</search>";
    RawCombinedQueryDefinition combinedQueryDef = queryManager.newRawCombinedQueryDefinition(new StringHandle(combi), OPTIONS);
    // DOES NOT WORK, but will lead to MarkLogicIOException "Could not construct search results: parser error"
    // Possible solution is to modify the queryDef DOM your own
5

5 Answers

2
votes

We used the <options> tag of the <search> element sent to the server. This requires an ugly string concatenation, but it does not require anything server-side other than an index on the sort property.

For the format of the XML, see this link or the search.xsd : http://docs.marklogic.com/guide/rest-dev/appendixb#id_33716

The idea is to generate an XML like this one :

<search xmlns='http://marklogic.com/appservices/search'>
  <query xmlns="http://marklogic.com/appservices/search">
    <collection-query>
      <uri>a_collection</uri>
    </collection-query>
  </query>
  <options>
    <sort-order type="xs:dateTime" direction="descending">
      <json-property>a_field</json-property>
    </sort-order>
  </options>
</search>

We did it like this :

  1. First, build your StructuredQueryDefinition

    StructuredQueryDefinition queryDef = sb.collection("my_collection);
    
  2. Build the <options> element seen above

    String xmlSortNode =
            "  <options>" +
            "    <sort-order type=\"xs:dateTime\" direction=\"descending\">" +
            "      <json-property>a_field</json-property>" +
            "    </sort-order>" +
            "  </options>";
    
  3. Build the full search element

    String searchXml="<search xmlns='http://marklogic.com/appservices/search'>"
      + queryDef.serialize()
      + xmlSortNode
      + "</search>";
    
  4. Execute the query

    queryManager.newRawCombinedQueryDefinition(new StringHandle(searchXml),"all");
    
1
votes

At present, StructuredQueryBuilder only builds criteria. You would have to refer to persisted query options with the sort order or send a combined search with both the criteria and options.

1
votes

We’re tracking this on GitHub. Please feel free to add information there.

1
votes

This is the solution I ended up with, basically I use the programmatic way on building a query with StructuredQueryBuilder and then inject the operator-state, referring to a pre-defined sort order in my options (OPTIONS_ALL):

org.jdom2.Document doc = new SAXBuilder().build(new StringReader(queryDef.serialize()));
if (!StringUtils.isEmpty(orderBy)) {
    Element operatorState = new Element("operator-state", NAMESPACE_SEARCH);
    operatorState.addContent(new Element("operator-name", NAMESPACE_SEARCH).setText("sort"));
    operatorState.addContent(new Element("state-name", NAMESPACE_SEARCH).setText(orderBy));
    doc.getRootElement().addContent(operatorState);
}
RawStructuredQueryDefinition rawQueryDef =
                queryManager.newRawStructuredQueryDefinition(new JDOMHandle(doc), OPTIONS_ALL);
// ~~
SearchHandle resultsHandle = new SearchHandle();
queryManager.search(rawQueryDef, resultsHandle, start);

With

public static final Namespace NAMESPACE_SEARCH = Namespace.getNamespace("http://marklogic.com/appservices/search");
0
votes

Sort order is considered an operator. Take a look at operator-state and you'll find an example of sorting in a structured query.