Search code examples
xquerymarklogicfalse-positive

MarkLogic cts:element-query false positives?


Given this document :-

<items>
  <item><type>T1</type><value>V1</value></item>
  <item><type>T2</type><value>V2</value></item>
</items>

unsurprisingly, I find that this will pull back the page in a cts:uris() :-

cts:and-query((
  cts:element-query(xs:QName('item'),
    cts:element-value-query(xs:QName('type'),'T1')
    ),
  cts:element-query(xs:QName('item'),
    cts:element-value-query(xs:QName('value'),'V2')
    )
  ))

but somewhat surprisingly (to me at least) I also find that this will too :-

cts:element-query(xs:QName('item'),
  cts:and-query((
    cts:element-value-query(xs:QName('type'),'T1'),
    cts:element-value-query(xs:QName('value'),'V2')
    ))
  )

This doesn't seem right, as there is no single item with type=T1 and value=V2. To me this seems like a false positive.

Have I misunderstood how cts:element-query works? (I have to say that the documentation isn't particularly clear in this area).

Or is this something where MarkLogic strives to give me the result I expect, and had I had more or better indexes in place, I would be less likely to get a false positive match.


Solution

  • In addition to the answer by @wst, you only need to enable element value positions to get accurate results from unfiltered search. Here some code to show this:

    xdmp:document-insert("/items.xml", <items>
      <item><type>T1</type><value>V1</value></item>
      <item><type>T2</type><value>V2</value></item>
    </items>);
    
    cts:search(collection(),
      cts:element-query(xs:QName('item'),
        cts:and-query((
          cts:element-value-query(xs:QName('type'),'T1'),
          cts:element-value-query(xs:QName('value'),'V2')
        ))
      ), 'unfiltered'
    )
    

    Without element value positions enabled this returns the test document. After enabling the positions, the query returns nothing.

    As said by @wst, cts:search() runs filtered by default, whereas cts:uris() (and for instance xdmp:estimate() only runs unfiltered.

    HTH!