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

时间:2014-12-07 05:44:42

标签: marklogic

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

cts:search(/medicalJournal,cts:and-query(()))

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

1 个答案:

答案 0 :(得分:8)

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

cts:search(collection(), cts:collection-query($journal-collection))

答案越长,您可以轻松地对此进行测试,并从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)

坐下来等待文件加载。您可以检查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>

现在,我更喜欢将第一个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>

请注意计划中与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'))

检查每个xdmp:plan输出,我们看到qry:final-plan显示前两个表单的两个term-query查找。这看起来很像element-query上的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()

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

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