1

根据搜索开发人员指南:每个约束都被命名,并且名称在选项节点中的所有运算符和约束中必须是唯一的。

我们正在使用一个内容丰富包,它产生如下输出:

`<TM360:Measurements Measurements="Distance">
    <Measurements:Distance Amount="3" Unit="inches"/>
</TM360:Measurements>
<TM360:Measurements Measurements="Volume">
    <Measurements:Volume Amount="5.0" Unit="liters"/>
</TM360:Measurements>`

看“金额”:属性localName不是唯一的,但包含它的元素是唯一的。

有没有办法绕过约束名称唯一性限制来构建一个受约束的搜索,比如“数量:5.0”,它将包括上面两个条目的索引?

处理这种情况的最佳方法是什么?

4

3 回答 3

2

为了获得最佳结果,您必须重构或丰富该 XML。搜索 API 旨在充分利用 MarkLogic 基于 QName 的索引功能。您今天拥有的 XML 有一个元素 QName:TM360,和几个属性 QName,它们都不是强选择性的。

您可以使用 XSLT 或递归转换来重新格式化该 XML。我建议您针对以下内容:

<dist:inches xmlns:dist="ns://fubar.distance">3</dist:inches>
<vol:liters xmlns:vol="ns://fubar.volume">5.0</vol:liters>

作为副作用,这使您可以编写更简洁的 XPath 查询,并为您的节点提供更具体的模式类型。

此用例可能是人为设计的,但您可能还需要考虑将所有这些测量值归一化为 SI 单位的标准子集,例如http://en.wikipedia.org/wiki/Cgs,以便比较容易。

于 2011-11-10T16:50:52.007 回答
2

您可以创建一个自定义约束来实现这一点。我使用以下三个脚本成功地做到了这一点:

  • 设置-db.xqy
  • 搜索.xqy
  • 自定义约束.xqy

这是设置脚本,它创建了两个“金额”范围索引并添加了几个示例文档(test1.xml 和 test2.xml):

xquery version "1.0-ml";

import module namespace admin = "http://marklogic.com/xdmp/admin" at "/MarkLogic/admin.xqy" ;

declare namespace TM360        = "http://example.com/TM360";
declare namespace Measurements = "http://example.com/Measurements";

declare function local:make-amount-index($parent-name) {
  admin:database-range-element-attribute-index(
    (: data type       :) "decimal",
    (: parent name     :) "http://example.com/Measurements",$parent-name,
    (: attribute name  :) "", "Amount",
    (: collation       :) "",
    (: value positions :) false()
  )
};

(: Set up the indexes (or you can add these via the Admin UI) :)
let $dbid       := xdmp:database(),
    $rangespec1 := local:make-amount-index("Distance"),
    $rangespec2 := local:make-amount-index("Volume"),
    $config     := admin:get-configuration(),
    $config     := admin:database-add-range-element-attribute-index($config, $dbid, $rangespec1),
    $config     := admin:database-add-range-element-attribute-index($config, $dbid, $rangespec2)
return
  admin:save-configuration($config)

,

(: Add some sample docs :)
xdmp:document-insert("/test1.xml",
  <TM360:Measurements Measurements="Distance">
      <Measurements:Distance Amount="3" Unit="inches"/>
  </TM360:Measurements>),

xdmp:document-insert("/test2.xml",
  <TM360:Measurements Measurements="Volume">
      <Measurements:Volume Amount="5.0" Unit="liters"/>
  </TM360:Measurements>)

下面是 search.xqy,它进行了两次搜索:

  • search:search("Amount:3",$options)
  • search:search("Amount:5",$options)

特别注意 $options 节点,它定义了自定义约束:

xquery version "1.0-ml";

import module namespace search="http://marklogic.com/appservices/search"
       at "/MarkLogic/appservices/search/search.xqy";

declare variable $options :=
  <options xmlns="http://marklogic.com/appservices/search">
    <constraint name="Amount">
      <custom facet="false">
        <parse apply="parse-amount"
               ns="http://example.com/custom-constraint"
               at="/custom-constraint/custom-constraint.xqy">
        </parse>
      </custom>
    </constraint>
  </options>;

(: matches test1.xml :)
search:search("Amount:3",$options),

(: matches test2.xml :)
search:search("Amount:5",$options)

最后,这是 custom-constraint.xqy 代码,它将约束文本转换为跨两个 Amount 索引的 cts OR 查询:

xquery version "1.0-ml";

module namespace my = "http://example.com/custom-constraint";

declare namespace Measurements = "http://example.com/Measurements";

declare default function namespace "http://www.w3.org/2005/xpath-functions";

(: Convert the constraint text into an OR query against "Distance" and "Volume" :)
declare function my:parse-amount($constraint-qtext as xs:string,
                                 $right as schema-element(cts:query))
        as schema-element(cts:query)
{
  let $value := xs:decimal($right//cts:text)
  return
    <cts:or-query>{
      my:make-amount-query("Distance",$value),
      my:make-amount-query("Volume"  ,$value)
    }</cts:or-query>
};


declare function my:make-amount-query($parent-name, $value) {
  cts:element-attribute-range-query(
    (: parent name    :) QName("http://example.com/Measurements", $parent-name),
    (: attribute name :) xs:QName("Amount"),
    (: operator       :) "=",
    (: value          :) $value
  )
};

如果您希望您的约束也作为一个方面,那么您还需要实现 start-facet 和 finish-facet 函数(并相应地增加您的选项节点中的定义。搜索开发人员指南包括一个示例,说明如何做这个。

于 2011-11-10T18:16:39.177 回答
1

使用 cts 函数会相对简单。您可以执行以下操作:

declare namespace Measurements = "http://example.com/Measurements";

cts:search(doc(), cts:element-attribute-value-query(
  (xs:QName("Measures:Distance"), xs:QName("Measures:Volume")),
  xs:QName("Amount"),
  $myamount
))

幸运的是,从 MarkLogic 8.0-5 开始,您还可以将其表示为单个搜索约束,并将其与search:search或(现在)内置的 REST api 一起使用:

xquery version "1.0-ml";

declare namespace TM360        = "http://example.com/TM360";
declare namespace Measurements = "http://example.com/Measurements";

xdmp:document-insert(
  "/amount3.xml",
  <TM360:Measurements Measurements="Distance">
    <Measurements:Distance Amount="3" Unit="inches"/>
  </TM360:Measurements>
),
xdmp:document-insert(
  "/amount5.xml",
  <TM360:Measurements Measurements="Volume">
    <Measurements:Volume Amount="5.0" Unit="liters"/>
  </TM360:Measurements>
)

;

import module namespace search="http://marklogic.com/appservices/search"
       at "/MarkLogic/appservices/search/search.xqy";

declare variable $options :=
  <options xmlns="http://marklogic.com/appservices/search">
    <constraint name="Amount">
      <value>
        <element ns="http://example.com/Measurements" name="Distance"/>
        <element ns="http://example.com/Measurements" name="Volume"/>
        <attribute ns="" name="Amount"/>
      </value>
    </constraint>
  </options>;

(: matches /amount3.xml :)
search:search("Amount:3",$options),

(: matches /amount5.xml :)
search:search("Amount:5.0",$options)

如果您有所有元素属性组合的范围索引,您还可以使用范围约束,如 Evan 建议的那样。

于 2011-11-10T19:51:52.213 回答