Marklogic wildcard search(通配符搜索)笔记

来源:互联网 发布:lol淘宝半价点券 编辑:程序博客网 时间:2024/04/28 14:59

Marklogic是企业级的NOSQL数据库。存储基于XML的以文档为中心的数据。提供了强大高效的Search API。Marklogic 6之前主要是基于XQuery的API,6之后推出了相应的Java API。目前Java API还在评估中,项目中还主要是用XQuery实现。这个产品在美国用的比较多,在国内用户寥寥无几。所以网上很少能找到相关的资料。好在其官方文档相对完备,开发中大部分的问题也都可以解决。但有些问题要颇费周折才能找出其中的原因,接下来要说的wildcard search(通配符搜索)就是这样。故用此文记录下来,作为参考。

我们有个需求,需要根据数据库中某个元素的的值支持通配符搜索,而且是以输入的关键字为头结合通配符的尾部去匹配结果。例如:

数据库中有3个xml数据,其中title元素分别为:<title>Wildcard search test</title>,<title>FW: Wildcard search test</title>,<title>RE: Wildcard search test</title>;如果用户输入“Wildcard search *”关键字来查询,只能返回第一个xml,因为第二,三个不是以"Wildcard search"开头的。我在数据库中插入了三条测试数据,然后用Search API根据需求实现如下的Search逻辑。

(:insert test xml:)xdmp:document-insert("/test/test1.xml",<test id="test1"><from>user1</from><to>user2</to><title>Wildcard search test</title><time>2014-05-10T11:07:49.000Z</time></test>)xdmp:document-insert("/test/test2.xml",<test id="test2"><from>user2</from><to>user3</to><title>FW: Wildcard search test</title><time>2014-05-11T11:07:49.000Z</time></test>)xdmp:document-insert("/test/test3.xml",<test id="test3"><from>user3</from><to>user2</to><title>RE: Wildcard search test</title><time>2014-05-12T11:07:49.000Z</time></test>)


xquery version "1.0-ml";declare namespace html = "http://www.w3.org/1999/xhtml";import module namespace search = "http://marklogic.com/appservices/search" at "/MarkLogic/appservices/search/search.xqy";(: search logic implementation :)declare function local:query ($query as xs:string) {  let $final-query :=               cts:registered-query(cts:register(               cts:query(search:parse($query,                 <options xmlns="http://marklogic.com/appservices/search">                      <grammar>                          <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" tokenize="word">NOT</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="10" apply="infix" element="cts:or-query">,</joiner>                          <joiner strength="50" apply="constraint">:</joiner>                      </grammar>                      <constraint name="Title">                          <value>                              <element name="title" ns="" />                              <term-option>case-insensitive</term-option>                              <term-option>punctuation-insensitive</term-option>                              <term-option>whitespace-insensitive</term-option>                              <term-option>wildcarded</term-option>                          </value>                      </constraint>                      <constraint name="From"><value><element ns="" name="from"/></value></constraint>                      <constraint name="To"><value><element ns="" name="to"/></value></constraint>                      <return-query>true</return-query>                 </options>))                 ),'unfiltered', 0)  return $final-query};let $key-words := 'Title:"Wildcard search *"'let $final-query := local:query($key-words)return cts:search(/test, $final-query)

用以上的实现,理论上来说就可以满足这个需求,应该只会return test1.xml的内容,但实际情况是test1.xml, test2.xml, test3.xml全都返回了。这就让人很费解。也花了很多时间research官方文档,尝试找出问题到底出在哪里。起初把大部分的注意力集中在wildcard query option的使用是不是有问题。很多次的实验和官方文档都说明,这么用是对的。开发常用的谷歌、百度也都帮不上忙,用的人不多,更没人遇到类似的问题。只能在官网上继续研究。最后把注意力放到cts:registered-query上,这个方法是用来返回注册后的query的(出于性能考虑,可以将常用固定不变的query注册到数据库里)。文档对这个方法的第二个参数的描述是,可以传入“filtered”或"unfiltered"。主要的意思是“filtered”是用来去重的。“unfiltered”可能会返回重复的数据。文档上又说“filtered” is not currently available. "unfiltered" is required in the current release. 提供了两个可选项,但第一个不能支持。不太理解他们为什么要这么设计,可能是实现上遇到了某些问题。然后尝试不注册这个query,每次都用新的,代码如下。

declare function local:query ($query as xs:string) {  let $final-query :=               cts:query(search:parse($query,                 <options xmlns="http://marklogic.com/appservices/search">                      <grammar>                          <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" tokenize="word">NOT</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="10" apply="infix" element="cts:or-query">,</joiner>                          <joiner strength="50" apply="constraint">:</joiner>                      </grammar>                      <constraint name="Title">                          <value>                              <element name="title" ns="" />                              <term-option>case-insensitive</term-option>                              <term-option>punctuation-insensitive</term-option>                              <term-option>whitespace-insensitive</term-option>                              <term-option>wildcarded</term-option>                          </value>                      </constraint>                      <constraint name="From"><value><element ns="" name="from"/></value></constraint>                      <constraint name="To"><value><element ns="" name="to"/></value></constraint>                      <return-query>true</return-query>                 </options>))  return $final-query};

运行就可以得到想要的结果,只是test1.xml被返回。我们暂时只能做一个折衷的解决方案,判断传入的关键字是不是包含“Title”,如果包含就用不注册的query,如果不包含就用注册的query。同时将这个问题report给Marklogic的官方,后面的版本中也许会解决这个问题。

0 0
原创粉丝点击