[MarkLogic Dev General] Cts query for element attribute value matching another attribute value?

Michael Blakeley mike at blakeley.com
Sun Feb 12 10:04:25 PST 2012


No easy suggestions, but I have a couple.

If this is a common query and needs to be very efficient, then I would create a node that represents the fact that elem/@attr1 eq elem/@attr2. Then you could use something like:

    cts:element-attribute-value-query(xs:QName('elem'), xs:QName('attrs-equal'), '1').

If you prefer fewer XML changes and more database round-trips, you could put a range index on elem/@attr1 (or elem/@attr2, but let's use attr1 for now). Then you could write:

    cts:element-attribute-value-query(
      xs:QName('elem'), xs:QName('attr2'),
      cts:element-attribute-values(
        xs:QName('elem'), xs:QName('attr1')))

You could speed that query up even further by configuring a range index on both element-attribute pairs, and then using cts:element-attribute-range-query with '=' instead of cts:element-attribute-value-query. But the 'attrs-equal' approach will still outperform it most of the time, and won't use as much memory or disk space.

Why is any of this necessary? Because the cts:query constructors are designed to be searchable - that is, to allow efficient index lookups. But there is no index that would answer your question directly. There is an index entry for every value of //elem/@attr1[. eq VALUE] and for //elem/@attr2[. eq VALUE] but no index entry that says //elem/@attr1[. eq attr2]. So the most straightforward way to resolve the XPath is to retrieve every match for //elem[@attr1][@attr2] and then filter in memory to check the @attr1 eq @attr2 portion.

When xdmp:plan says "Path is fully searchable", I think it's only talking about any node-name and node-type step(s), not any predicates. When I call it on your XPath expression, it shows only one constraint, and I get exactly the same plan if I simply drop the predicate. So I think it's really only searching on //elem, and ignoring both attributes until the filtering phase (that's with 5.0-2).

Compare these plans:

xdmp:plan(//elem[@att1 = @att2]),
xdmp:plan(//elem),
xdmp:plan(//elem[@attr1])
=>
<qry:query-plan xmlns:qry="http://marklogic.com/cts/query">
  <qry:info-trace>xdmp:eval("xdmp:plan(//elem[@att1 = @att2]),&amp;#13;&amp;#10;xdmp:plan(//elem),&amp;#1...", (), &lt;options xmlns="xdmp:eval"&gt;&lt;database&gt;18400529833056734238&lt;/database&gt;&lt;root&gt;/Users/mblakele/S...&lt;/options&gt;)</qry:info-trace>
  <qry:info-trace>Analyzing path: fn:collection()/descendant::elem[@att1 = @att2]</qry:info-trace>
  <qry:info-trace>Step 1 is searchable: fn:collection()</qry:info-trace>
  <qry:info-trace>Step 2 is searchable: descendant::elem[@att1 = @att2]</qry:info-trace>
  <qry:info-trace>Path is fully searchable.</qry:info-trace>
  <qry:info-trace>Gathering constraints.</qry:info-trace>
  <qry:info-trace>Executing search.</qry:info-trace>
  <qry:final-plan>
    <qry:and-query>
      <qry:term-query weight="0">
	<qry:key>7128167059298760147</qry:key>
      </qry:term-query>
    </qry:and-query>
  </qry:final-plan>
  <qry:info-trace>Selected 0 fragments</qry:info-trace>
  <qry:result estimate="0"/>
</qry:query-plan>
<qry:query-plan xmlns:qry="http://marklogic.com/cts/query">
  <qry:info-trace>xdmp:eval("xdmp:plan(//elem[@att1 = @att2]),&amp;#13;&amp;#10;xdmp:plan(//elem),&amp;#1...", (), &lt;options xmlns="xdmp:eval"&gt;&lt;database&gt;18400529833056734238&lt;/database&gt;&lt;root&gt;/Users/mblakele/S...&lt;/options&gt;)</qry:info-trace>
  <qry:info-trace>Analyzing path: fn:collection()/descendant::elem</qry:info-trace>
  <qry:info-trace>Step 1 is searchable: fn:collection()</qry:info-trace>
  <qry:info-trace>Step 2 is searchable: descendant::elem</qry:info-trace>
  <qry:info-trace>Path is fully searchable.</qry:info-trace>
  <qry:info-trace>Gathering constraints.</qry:info-trace>
  <qry:info-trace>Executing search.</qry:info-trace>
  <qry:final-plan>
    <qry:and-query>
      <qry:term-query weight="0">
	<qry:key>7128167059298760147</qry:key>
      </qry:term-query>
    </qry:and-query>
  </qry:final-plan>
  <qry:info-trace>Selected 0 fragments</qry:info-trace>
  <qry:result estimate="0"/>
</qry:query-plan>
<qry:query-plan xmlns:qry="http://marklogic.com/cts/query">
  <qry:info-trace>xdmp:eval("xdmp:plan(//elem[@att1 = @att2]),&amp;#13;&amp;#10;xdmp:plan(//elem),&amp;#1...", (), &lt;options xmlns="xdmp:eval"&gt;&lt;database&gt;18400529833056734238&lt;/database&gt;&lt;root&gt;/Users/mblakele/S...&lt;/options&gt;)</qry:info-trace>
  <qry:info-trace>Analyzing path: fn:collection()/descendant::elem[@attr1]</qry:info-trace>
  <qry:info-trace>Step 1 is searchable: fn:collection()</qry:info-trace>
  <qry:info-trace>Step 2 is searchable: descendant::elem[@attr1]</qry:info-trace>
  <qry:info-trace>Path is fully searchable.</qry:info-trace>
  <qry:info-trace>Gathering constraints.</qry:info-trace>
  <qry:info-trace>Step 2 predicate 1 contributed 1 constraint: @attr1</qry:info-trace>
  <qry:partial-plan>
    <qry:term-query weight="0">
      <qry:key>11100480210632785569</qry:key>
    </qry:term-query>
  </qry:partial-plan>
  <qry:info-trace>Step 2 predicate 1 contributed 1 constraint: @attr1</qry:info-trace>
  <qry:partial-plan>
    <qry:term-query weight="0">
      <qry:key>11100480210632785569</qry:key>
    </qry:term-query>
  </qry:partial-plan>
  <qry:info-trace>Executing search.</qry:info-trace>
  <qry:final-plan>
    <qry:and-query>
      <qry:or-query>
	<qry:element-query>
	  <qry:key>7128167059298760147</qry:key>
	  <qry:and-query>
	    <qry:term-query weight="0">
	      <qry:key>11100480210632785569</qry:key>
	    </qry:term-query>
	  </qry:and-query>
	</qry:element-query>
	<qry:and-query>
	  <qry:term-query weight="0">
	    <qry:key>11397336598217694489</qry:key>
	  </qry:term-query>
	  <qry:term-query weight="0">
	    <qry:key>7128167059298760147</qry:key>
	  </qry:term-query>
	  <qry:term-query weight="0">
	    <qry:key>11100480210632785569</qry:key>
	  </qry:term-query>
	</qry:and-query>
      </qry:or-query>
    </qry:and-query>
  </qry:final-plan>
  <qry:info-trace>Selected 0 fragments</qry:info-trace>
  <qry:result estimate="0"/>
</qry:query-plan>

If attr1 or attr2 aren't ubiquitous, then I think the efficient way to write the XPath might be this odd-looking expression:

    //elem[@attr1][@attr2][@att1 = @att2]

-- Mike

On 12 Feb 2012, at 01:16 , Geert Josten wrote:

> I am trying to isolate some specific element with two attributes who's
> values are equal. I know I can use an expression like doc()//elem[@att1 =
> @att2], which is even fully searchable according to xdmp:plan, but I'd
> prefer a cts:query, which I could pass into cts:element-attribute-values.
> I need unique values, and I am trying to prevent using distinct-values on
> the above XPath expression..
> 
> Any suggestions?
> 
> Kind regards,
> Geert
> 
> drs. G.P.H. (Geert) Josten
> Senior Developer
> 
> 
> 
> Dayon B.V.
> Delftechpark 37b
> 2628 XJ Delft
> 
> T +31 (0)88 26 82 570
> 
> geert.josten at dayon.nl
> www.dayon.nl
> 
> De informatie - verzonden in of met dit e-mailbericht - is afkomstig van
> Dayon BV en is uitsluitend bestemd voor de geadresseerde. Indien u dit
> bericht onbedoeld hebt ontvangen, verzoeken wij u het te verwijderen. Aan
> dit bericht kunnen geen rechten worden ontleend.
> _______________________________________________
> General mailing list
> General at developer.marklogic.com
> http://developer.marklogic.com/mailman/listinfo/general
> 



More information about the General mailing list