如何重组文档进行优化

时间:2014-08-19 12:08:20

标签: marklogic

在我的previous question中,我遇到了许多具有相同属性名称的元素的问题(123)

我们清理了查询并评估了建议的解决方案,所有查询都保持相同的数量级(完整大小查询为15秒),对于应用程序来说太慢了。

为了完整性,我在这里重复查询:

原件:

xquery version "1.0-ml";
declare namespace html = "http://www.w3.org/1999/xhtml";
declare namespace p4ns       = "http://www.nvsp.nl/p4";
declare namespace wijkns     = "http://www.nvsp.nl/wijk";

let $segment := ("Bruto","Netto")
let $codes := ("9766","9765","2162","2161","2159") (: full query is 4000 codes:)


let $zoeker0 := cts:search(fn:doc(), cts:element-attribute-range-query(xs:QName("p4ns:postcode"), xs:QName("id"), "=", $codes)) 
(:
let $zoeker1 := cts:search(/p4ns:postcode, cts:element-attribute-range-query(xs:QName("p4ns:postcode"), xs:QName("id"), "=", $codes)) 
let $zoeker2 := cts:search(/p4ns:postcode, cts:element-attribute-value-query(xs:QName("p4ns:postcode"), xs:QName("id"), $codes)) 
:)

let $inhoud1 := $zoeker0//p4ns:segment[@name=$segment]
(:
let $inhoud2 := $zoeker1//p4ns:segment[@name=$segment]/text()

let $r1 := cts:search(/p4ns:postcode, cts:element-attribute-range-query(xs:QName("p4ns:segment"), xs:QName("name"), "=", $segment))
:)
return $inhoud1

接受的答案:

declare namespace p4ns="http://www.nvsp.nl/p4" ;

(: These might be external parameters. :)
let $segment := ("Bruto","Netto")
let $ids := ("9766","9765")
return collection()/p4ns:postcode/p4ns:category/p4ns:variable/p4ns:segment[
  @name = $segment]/string()

两个查询在大型文档中仍然需要昂贵的段名称属性查找。

接受的答案是说没有真正好的解决方案,只能对文件进行重组。

现在接下来的挑战是怎样的?

我正在思考的行:

  • 重建文件
  • 将文档拆分为两个(或更多)部分
  • 使用片段和片段根
  • 在同一文档中使用单独的命名空间来分割内容。

最初我们设计文档的方式是我们组织了[name =“Oplages”] / variable [name =“Oplage”] / segment [name =“Bruto”]层次结构中的所有内容。标识符位于“名称”属性中。这就是为什么我们有这么多具有​​name属性的段元素。

因此,一个选项是重建文档,如

Oplages / Oplage / Bruto

通过这种方式,我们需要为所有单个段(其中400个)构建索引,这就是我们不这样做的原因。

其他选项是使用片段,在片段元素中设置片段根是否有意义?不确定,因为问题仍然存在(在集合中搜索具有特定名称的一个段为400

所以我的问题是:如何重构原始文档,以便我的查询在子秒响应中执行。

3 个答案:

答案 0 :(得分:1)

看起来迈克在上一个问题中没有提到Path索引的选项。您可以在/postcode/category[name="Oplages"]/variable[name="Oplage"]/segment[name="Bruto"]上专门创建索引,方法是将其用作路径模式。这将有助于尽可能准确地定位相关片段。

但我不确定您对最终结果的期望。您是否需要Bruto段值列表,或者您是否在寻找包含匹配段的邮政编码?很大程度上取决于你想要的结果。从头到尾思考问题可能很有用。

HTH!

答案 1 :(得分:0)

我不喜欢使用子片段。编写一个意外地将所有片段加载到内存中的查询太容易了。这个用例可能是一个例外,碎片可能是最好的方式,但我会把它作为最后的手段。我的经验是,它一开始看起来效果很好,但随着时间的推移会出现越来越多的问题。

在上一个问题中,我提到了其他可能性。我会在这里回顾一下。主要技巧是要记住MarkLogic是一个面向文档的数据库。因此,文档URI是主键,文档的行为更像行而不是表。构建您的文档和URI,以便您最常见的查询将利用这些特征。

在MarkLogic中,文档URI是主键,主键查找只是doc($uri)。这是查询数据库的一种非常有效的方法。知道这一点,您应该构建内容,以便最常见的查找只是doc($uri)。你提到了像Oplages/Oplage/Bruto这样的结构。我不确定这意味着什么,但是如果你安排它是一个文档URI而不是XML结构呢?像/oplages/{ code }/{ segment }这样的东西?每个文档都非常小,只有一个段的数据。

如果您有经常不使用的元数据,请将其放在/oplages/{ code }/metadata.xml之类的特殊文档中。或者,如果每次都使用它,请将其复制到每个段文档中。您比我更了解您的内容,但单个文档的XML可能如下所示:

<segment xmlns="http://www.nvsp.nl/p4"
 postcode="9728"
 category="Oplages"
 variable="Oplage"
 name="Bruto"
 updated="2014-08-12+02:00">
  1234
</segment>

将该XML存储在/oplages/9728/Bruto,只需调用doc('/oplages/9728/Bruto')即可获得该值。您还可以使用多个URI序列调用fn:doc。您可能会看到围绕文档大小以及如何构建数据进行权衡。

我提到的另一种可能性是使用范围索引和https://docs.marklogic.com/cts:valueshttps://docs.marklogic.com/cts:value-co-occurrences来查询数据。此方法直接从值索引中提取数据,而不是检查XML树。然而,使用小型,定义明确的文档,这种方法仍然更容易。使用像原始样本的XML,每个文档包含许多值,您需要位置感知范围索引和精心编写的位置范围查询。

答案 2 :(得分:0)

我测试了Mike和Geert的建议,你可以通过以下方式实现你想要的东西(我相信它是为了获得邮政编码的bruto和Netto值): 创建两个路径范围索引,一个用于Bruto,另一个用于Netto: / P4:邮政编码/ P4:类别/ P4:可变/ P4:段[@名称= 'Bruto'] / P4:邮政编码/ P4:类别/ P4:可变/ P4:段[@名称= '内托'] 不要忘记为p4注册命名空间

如果您需要这些细分变量,这将失败。 然后使用以下代码,您可以从索引

获取bruto和netto的值
declare namespace p4 = "http://www.nvsp.nl/p4";
let $segment := ("Bruto","Netto")
let $ids := ("9729","9728")
for $id in $ids
return ($id,
  cts:value-co-occurrences(
    cts:path-reference("/p4:postcode/p4:category/p4:variable/p4:segment[@name='Bruto']"),
    cts:path-reference("/p4:postcode/p4:category/p4:variable/p4:segment[@name='Netto']"),(),
    cts:element-attribute-value-query(xs:QName("p4:postcode"),xs:QName("id"),$id)))

这将返回一系列带有bruto和netto值的id,如:

9729 (: id :)
<cts:co-occurrence xmlns:cts="http://marklogic.com/cts" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <cts:value xsi:type="xs:int">4321</cts:value> (: bruto value :)
  <cts:value xsi:type="xs:int">2000</cts:value> (: netto value :)
</cts:co-occurrence>

或者您可以在/ p4:postcode / @ id上创建路径范围索引并执行:

declare namespace p4 = "http://www.nvsp.nl/p4";
let $segments := ("Bruto","Netto")
for $segment in $segments
return ($segment,
  cts:value-co-occurrences(
    cts:path-reference("/p4:postcode/@id"),
    cts:path-reference(fn:concat("/p4:postcode/p4:category/p4:variable/p4:segment[@name='",$segment,"']")),()))

这将返回邮政编码加上给定的段值:

Bruto (: segment name :)
<cts:co-occurrence xmlns:cts="http://marklogic.com/cts" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <cts:value xsi:type="xs:int">9728</cts:value>  (: postcode :)
  <cts:value xsi:type="xs:int">1234</cts:value>  (: segment value :)
</cts:co-occurrence>