空cts:and-query如何在性能方面做到

cal*_*low 1 marklogic

假设,我有一堆带有根元素的医学期刊作为medicalJournal.All医学期刊属于'mj'集合,'mj'集合只有医学期刊.其他类型的期刊也有自己的集合(physicsJournal - >'pj')为了检索所有医学期刊,我写了世界上最简单的查询:

cts:search(/medicalJournal,cts:and-query(()))
Run Code Online (Sandbox Code Playgroud)

但是,我的同行说为什么不对'pj'而不是空和查询放置一个集合查询,从而添加一个额外的约束.理由是,这可能会避免获取所有片段id的列表虽然当我第一次运行集合查询时,查询表显示了一些列表缓存未命中.请告诉我应该选择哪个选项?

mbl*_*ele 8

简短的回答是cts:and-query(())没有固有的成本,但是集合查询会比您的可搜索表达式更快.我完全避免使用可搜索的表达式,因此我将其写为:

cts:search(collection(), cts:collection-query($journal-collection))
Run Code Online (Sandbox Code Playgroud)

更长的答案是你可以很容易地测试它,并从xdmp:plan'和'xdmp:query-meters` 获得一些好的信息.您还可以使用查询分析.

让我们从插入一些测试文档开始.这使用https://github.com/mblakele/taskbot

(: insert 500k test documents. :)
import module namespace tb="ns://blakeley.com/taskbot"
  at "taskbot.xqm" ;

tb:list-segment-process(
  (: Total size of the job. :)
  1 to 500 * 1000,
  (: Size of each segment of work. :)
  500,
  "test/asset",
  (: This anonymous function will be called for each segment. :)
  function($list as item()+, $opts as map:map?) {
    (: Any chainsaw should have a safety. Check it here. :)
    tb:maybe-fatal(),
    let $type-list := ('mj', 'pj', 'aj', 'bj', 'cj', 'dj')
    let $type-count := count($type-list)
    for $i in $list
    let $idx := 1 + xdmp:random($type-count - 1)
    let $type as xs:string := subsequence($type-list, $idx, 1)
    return xdmp:document-insert(
      "test/"||$type||"/"||$i,
      element article {
        element id { $type||$i },
        element type { $type },
        element { $type } { $i },
        element issue { 1 + xdmp:random(99) },
        element article { 1 + xdmp:random(999) },
        (1 to xdmp:random(9)) ! element article-ref {
          xdmp:random(1000) } },
      xdmp:default-permissions(),
      ($type)),
    (: This is an update, so be sure to commit each segment. :)
    xdmp:commit() },
  (: options - not used in this example. :)
  map:new(map:entry('testing', '123...')),
  (: This is an update, so be sure to say so. :)
  $tb:OPTIONS-UPDATE)
Run Code Online (Sandbox Code Playgroud)

坐下来等待文件加载.您可以检查ErrorLog.txt以查看进度,或刷新数据库状态.或者只是看你的CPU.

加载后,这些测试文档包含大量冗余.这让我们可以测试不同的方法来检索文档.这是一个要查看的示例:

<?xml version="1.0" encoding="UTF-8"?>
<article>
  <id>mj192462</id>
  <type>mj</type>
  <mj>192462</mj>
  <issue>31</issue>
  <article>432</article>
  <article-ref>589</article-ref>
  <article-ref>812</article-ref>
  <article-ref>316</article-ref>
  <article-ref>512</article-ref>
  <article-ref>380</article-ref>
</article>
Run Code Online (Sandbox Code Playgroud)

现在,我更喜欢将第一个cts:search参数保留为collection()cts:query参数中的所有内容.它更具有可组合性,并且避免任何试图突破可搜索表达式的界限.所以我从测试开始cts:search(//mj, cts:and-query(()))就相当于cts:search(collection(), cts:element-query(xs:QName('mj'), cts:and-query(()))).使用7.0-4.1,xdmp:plan我发现它们都使用相同的查找,我们可能会将其缩写为OR(element(mj), link-child(descendant(element(mj)))).

<qry:final-plan>
  <qry:and-query>
    <qry:or-two-queries>
      <qry:term-query weight="0">
        <qry:key>213142789040258053</qry:key>
        <qry:annotation>element(mj)</qry:annotation>
      </qry:term-query>
      <qry:term-query weight="0">
        <qry:key>11205365121816230941</qry:key>
        <qry:annotation>link-child(descendant(element(mj)))</qry:annotation>
      </qry:term-query>
    </qry:or-two-queries>
  </qry:and-query>
</qry:final-plan>
Run Code Online (Sandbox Code Playgroud)

请注意计划中没有与您相对应的内容cts:and-query(())?那是因为它是一个noop.查询中的真正工作cts:search(/medicalJournal, cts:and-query(()))是通过处理可搜索的表达式来完成的/medicalJournal.这可能有助于解释为什么我更喜欢将可搜索的表达式保留为collection()并使用cts:query参数进行匹配.

这个link-child词很有意思,但我们现在不再讨论它.

相反,让我们看一些其他方式来获取mj文章.我们可以查询集合mj,或者在哪里type[.='mj']查询元素或查询目录test/mj/.

collection('mj')

cts:search(collection(), cts:collection-query('mj'))

cts:search(collection(), cts:element-value-query(xs:QName('type'), 'mj')

cts:search(collection(), cts:directory-query('test/mj/', 'infinity'))
Run Code Online (Sandbox Code Playgroud)

检查xdmp:plan每个输出,我们看到qry:final-plan显示term-query前两个表单的两个查找.这看起来很像element-queryon mj- 但并不总是相同的术语.然后我们看到最后三个一个.术语查找驱动查询复杂性,因此我们可以说元素查询的复杂性是集合查询的两倍.

我认为这几乎可以回答你的问题:cts:search(collection(), cts:collection-query('mj'))可以更快cts:search(collection(), cts:element-query(xs:QName('mj'), cts:and-query(()))),因为它的查找次数更少.

但是让我们继续前三,看看是否有任何理由使用最后三个替代品中的一个或另一个.集合查找和目录查找都分别使用URI:集合URI和目录URI.这些都经过了大量优化,因此我们可能期望它们比元素值查找更快.

让我们看看我们是否可以使用xdmp:query-meters.以下面的形式运行最后三个表达式中的每一个:

xdmp:describe(
  cts:search(collection(), cts:collection-query('mj')))
, xdmp:query-meters()
Run Code Online (Sandbox Code Playgroud)

对于本练习,重点关注xdmp:query-meters树缓存和列表缓存命中和未命中的内容.不要过多关注elapsed-time,因为这将在很大程度上取决于缓存了多少索引数据,我们无法控制它.无论如何,你应该看到相同的总树命中和错过所有三个查询.我看到每个都有9个,但重要的是没有区别因为结果是一样的.但是列表缓存命中和错过总数不同.对于集合或目录,它等于数据库中的站点数量:在我的情况下3.但是对于元素值,它是站点数量的两倍:在我的情况下6.所以其他所有相等,元素值查找冒险做两次很多I/O. 所有其他方面都不相同:URI查找根本不太可能进行任何I/O,因为它们的索引往往会一直保留在内存中.

我们可以得出结论,虽然其他方法都不是非常慢,但是按集合URI或目录URI查找是最好的.