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

本文档介绍了Marklogic作为NOSQL数据库在XML文档存储和Search API方面的应用,特别是针对wildcard search(通配符搜索)的使用。在实际项目中,通过XQuery API实现以关键字开头的通配符搜索功能,例如根据用户输入'Wildcard search*'匹配相关数据。在数据库中,此方法能确保只有以特定字符串开头的文档被返回。尽管Marklogic在国内使用较少,但其官方文档详尽,能够解决大部分开发问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的官方,后面的版本中也许会解决这个问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值