Search code examples
marklogic

Path expression in MarkLogic cts.search


I have the impression that the XQuery and the Server-side JavaScript APIs in MarkLogic are largely equivalent. But there seems to be a big difference in cts:search vs cts.search. In cts:search, I am able to specify an element to be searched and returned. For example, I can retrieve all recipes using cinnaomon as ingredient from a recipe book:

cts:search(//recipe, cts:element-word-query(xs:QName('ingredients'), 'cinnamon'))

Whereas cts.search doesn't accept a path expression and will return the whole recipe book document:

cts.search(cts.elementWordQuery(xs.QName('ingredients'), 'cinnamon'))

The same question has been asked in MarkLogic mailing list but I don't see an answer there: https://developer.marklogic.com/pipermail/general/2015-March/016508.html

Below is a minimal example:

<book>
  <recipe>
    <ingredients>cinnamon, peppermint</ingredients>
    <instruction/>
  </recipe>
  <recipe>
    <ingredients>sugar, peppermint</ingredients>
    <instruction/>
  </recipe>
  <recipe>
    <ingredients>coconut oil</ingredients>
    <instruction/>
  </recipe>
</book>

The xquery would be:

cts:search(//recipe, cts:element-word-query(xs:QName('ingredients'), 'cinnamon'))

and the response:

<recipe>
  <ingredients>cinnamon, peppermint</ingredients>
  <instruction></instruction>
</recipe>

Solution

  • Building up on DALDEI's answer you can use the search api to return only recipe elements:

    const search = require('/MarkLogic/appservices/search/search');
    
    let options = fn.head(xdmp.unquote(`
    <options xmlns="http://marklogic.com/appservices/search">
      <return-results>true</return-results>
      <searchable-expression>//recipe</searchable-expression>
      <extract-document-data>all</extract-document-data>
      <additional-query>
        <cts:element-word-query xmlns:cts="http://marklogic.com/cts">
          <cts:element>ingredients</cts:element>
          <cts:text xml:lang="en">cinnamon</cts:text>
        </cts:element-word-query>
      </additional-query>
    </options>`)).root;
    
    search.search("", options)        // returns a Sequence of search:response
      .toArray()[0]                   // get the first result
      .getElementsByTagName("recipe") // find all recipe elements 
    

    This code returns a NodeList of recipe elements. The result for your provided book would be this single element node:

    <recipe xmlns:search="http://marklogic.com/appservices/search">
      <ingredients>cinnamon, peppermint</ingredients>
      <instruction/>
    </recipe>
    

    This is not really a nice solution (in terms of quick and easy) but it might work as a workaround.

    I also tried using the jsearch functions but didn't find a way to pass a searchable-expression parameter. I might have missed that because I have not used this alot yet.

    Further readings:

    Query Options Reference search:search