在Marklogic自定义约束

时间:2016-12-05 20:43:41

标签: marklogic

有没有办法使用关系运算符来使用MarkLogic自定义约束进行搜索?

对于正常范围查询,我可以搜索完全匹配("事物:123")或使用关系运算符("事物LT 123")。但是,当我编写自定义约束时,只有完全匹配才有效。它永远不会进入解析'如果我使用关系运算符的方法。

一个完全无关紧要的例子,如果以任何方式触发约束,它就不会返回任何内容:

xquery库模块(它使用'多格式'签名here,但其他人做同样的事情):

xquery version "1.0-ml";
module namespace test = "http://test/search";
import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";

declare function test:parse(
  $query as item(), 
  $right-or-option as element())
as schema-element(cts:query) {
  <cts:false-query/> (: don't return anything :)
};

可以在qconsole中运行的XQuery:

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">
    <debug>true</debug>
    <constraint name="test">
        <custom facet="false">
            <parse apply="parse" ns="http://test/search" at="/ext/search/test-search.xqy"/>
        </custom>
    </constraint>
</options>;

search:search("test GT foo",$OPTIONS)

搜索&#34;测试:foo&#34;使用cts:false-query()创建正确的查询。正在搜索&#34;测试GT foo&#34;返回所有文件。

使用关系运算符时的响应包含:

<search:qtext>test GT foo</search:qtext>
<search:report id="SEARCH-FLWOR">(cts:search(fn:collection(), cts:and-query((), ()), ("score-logtfidf",cts:score-order("descending")), 1))[1 to 10]</search:report>
<search:warning id="SEARCH-IGNOREDQTEXT">[test GT foo]</search:warning>

它似乎认为它不仅仅是纯文本搜索,但它并没有将它发送到我的解析函数中。如何让它调用我的功能?

如果重要,真正的约束是使用三范围查询,它支持运算符(假设我可以将它们输入)。如果有更好的方法通过搜索进行三重查询:搜索可以解决我当前的问题,但我想知道如何使自定义约束识别关系运算符,无论如何。

3 个答案:

答案 0 :(得分:1)

简短版本:自定义约束不支持不等运算符(LT,GT等),但有很多方法可以解决这个问题。

更长的版本:

我原以为这有用了,所以我做了一些测试(记录中,我正在使用MarkLogic 8.0-5.8)。我相信你的搜索选项与默认选项混合在一起(否则“test:foo”情况不起作用)。这些选项包括:

<search:grammar>
  <search:joiner strength="50" apply="constraint" compare="LT" tokenize="word">LT</search:joiner>
</search:grammar>

这就是说,当你看到两个以“LT”分隔的代币时,apply the constraint function要弄清楚如何处理它们。这导致我查找constraint函数的作用。在挖掘了几个级别之后,我在Search API库中发现了这些代码行:

else if ($compare and (empty($matched-constraint/opt:range) or $is-bucketed)) 
then <search:annotation warning="SEARCH-IGNOREDQTEXT:[{concat($matched-constraint/@name/string()," ",$compare," ",$qtext-rhs)}]"/>

我确定你认识到这个注释。在我的测试中,$compare为“LT”,$matched-constraint是我的测试约束。此行要求解析器the constraint be a range type将LT应用于此。

变通方法

我看到两个选择。

  1. 实现您可以在Search API语法joiner部分中指定的其他函数(apply="my-constraint"),并使其适用于自定义约束。
  2. 实现解析函数的多个版本并设置多个约束:约束test-lt将应用parse-lt等。
  3. 注意:您可能想要深入研究Search API的实现并破解它适合您的情况(毕竟,它只是一个XQuery库)。别。如果这样做,您的更改将在升级MarkLogic后立即消失,如果该升级包含对该库的任何更改,您将使自己陷入困境。

    如果您与MarkLogic的某人有关系,您可以要求该人提交RFE以允许这些加入者使用自定义约束。如果你不这样做,请告诉我。

答案 1 :(得分:0)

关于您的测试约束,它被标记为facet =“false”。我还没有验证,但我怀疑这就是为什么它不是test GT foo,而是test:foo。实现一个假的facet finish(我认为start是可选的),并切换标志以查看你的测试约束是否适用于GT案例。

关于三范围查询,可以使用这种三范围约束,但最适合结构化查询(比如使用REST-api / v1 / search等)。它可能会给你一些灵感:

https://github.com/patrickmcelwee/triple-range-constraint

HTH!

答案 2 :(得分:0)

如果其他人需要这个,我有一个自定义连接函数来处理常规范围索引和自定义索引,因此用户可以使用相同的操作符。

在选项约束中有一些配置告诉它在搜索时要查找什么,以及一个语法部分,它将关系运算符从默认约束&#39;自定义函数:

<options xmlns="http://marklogic.com/appservices/search" xmlns:ps="http://my.namespace">
    <constraint name="quantity">
        <custom facet="false">
            <parse apply="parse" ns="http://test/search" at="/ext/search/test-search.xqy"/>
        </custom>
        <annotation>
            <ps:config compareSupported="true">
                <ps:triple-constraint type='xs:int' predicate='http://example.com/quantity'/>
            </ps:config>
        </annotation>
    </constraint>

    <grammar xmlns="http://marklogic.com/appservices/search">
      <quotation>"</quotation>
      <implicit>
        <cts:and-query strength="20" xmlns:cts="http://marklogic.com/cts"/>
      </implicit>
      <starter strength="30" apply="grouping" delimiter=")">(</starter>
      <starter strength="40" apply="prefix" element="cts:not-query">-</starter>
      <joiner strength="10" apply="infix" element="cts:or-query" tokenize="word">OR</joiner>
      <joiner strength="20" apply="infix" element="cts:and-query" tokenize="word">AND</joiner>
      <joiner strength="30" apply="infix" element="cts:near-query" tokenize="word">NEAR</joiner>
      <joiner strength="30" apply="near2" consume="2" element="cts:near-query">NEAR/</joiner>
      <joiner strength="32" apply="boost" element="cts:boost-query" tokenize="word">BOOST</joiner>
      <joiner strength="35" apply="not-in" element="cts:not-in-query" tokenize="word">NOT_IN</joiner>
      <joiner strength="50" apply="constraint">:</joiner>
      <joiner strength="50" apply="joiner-constraint" ns="http://test/search" at="/ext/search/test-search.xqy" compare="LT" tokenize="word">LT</joiner>
      <joiner strength="50" apply="joiner-constraint" ns="http://test/search" at="/ext/search/test-search.xqy" compare="LE" tokenize="word">LE</joiner>
      <joiner strength="50" apply="joiner-constraint" ns="http://test/search" at="/ext/search/test-search.xqy" compare="GT" tokenize="word">GT</joiner>
      <joiner strength="50" apply="joiner-constraint" ns="http://test/search" at="/ext/search/test-search.xqy" compare="GE" tokenize="word">GE</joiner>
      <joiner strength="50" apply="joiner-constraint" ns="http://test/search" at="/ext/search/test-search.xqy" compare="NE" tokenize="word">NE</joiner>
    </grammar>
</options>

接下来是自定义连接函数。它使用自定义查询选项配置来决定是否将其视为可以处理比较运算符的自定义约束。如果是这样,它将绕过内部MarkLogic代码并返回带有运算符的custom-constraint-query。如果没有,它允许MarkLogic处理它。顺便说一下,我还没有详尽地测试传递,但它适用于我使用的简单范围索引。它也不是特别面向未来,因为它直接调用了一些未记录的MarkLogic函数。

import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";
import module namespace searchimpl = "http://marklogic.com/appservices/search-impl" at "/MarkLogic/appservices/search/search-impl.xqy";
import module namespace ast = "http://marklogic.com/appservices/search-ast" at "/MarkLogic/appservices/search/ast.xqy";
import module namespace tdop     = "http://marklogic.com/parser/tdop" at "/MarkLogic/appservices/utils/tdop.xqy";

declare namespace ps = "http://my.namespace";
declare function test:joiner-constraint(
  $ps as map:map,
  $left as element()?,
  $opts as element()?
) as element()? {
    if ($left instance of element(cts:query)) then
        (: it doesn't seem to call this function with a cts:query for a custom constraint, but the pass-through for normal range indexes does :)
        searchimpl:joiner-constraint($ps, $left)
    else 
        let $term := ast:textonly($left)
        let $symbol := searchimpl:symbol-lookup($ps)
        let $compare := ($symbol/@compare/string(), "EQ")[1]
        let $constraint := $opts/search:constraint[@name=$term]
        let $is-custom := $constraint//search:custom and $constraint//ps:config[@compareSupported=fn:true()]

        return
        if ($is-custom) then 
            let $_ := tdop:advance($ps)
            let $right := tdop:expression($ps, $symbol/@strength - 1)
            let $value := ast:textonly($right)

            return
            <search:custom-constraint-query>
                <search:constraint-name>{$term}</search:constraint-name>
                <search:text>{$value}</search:text>
                <search:range-operator>{$compare}</search:range-operator>
            </search:custom-constraint-query>
        else
            ast:joiner-constraint($ps, $left)
};

在parse方法中,我切换到结构化查询参数,因为这将通过REST调用,并且如何使用字符串查询参数获取所有部分并不明显。由joiner函数创建的custom-constraint-query作为$query-elem参数传入。

declare function test:parse(
  $query-elem as element(),
  $options as element(search:options)
) as schema-element(cts:query) {
...
};