您好我是marklogic和Xquery世界的新手。我无法想到在Marklogic Xquery中编写以下逻辑的起点。如果有人能给我一些想法/样本,我会感激不尽,所以我可以实现以下目标:
我想基于B.XML中的单词查找查询A.XML。查询应该生成C.XML。逻辑应该如下:
A.XML
<root>
<content> The state passed its first ban on using a handheld cellphone while driving in 2004 Nokia Vodafone Nokia Growth Recession Creicket HBO</content>
</root>
B.XML
<WordLookUp>
<companies>
<company name="Vodafone">Vodafone</company>
<company name="Nokia">Nokia</company>
</companies>
<topics>
<topic group="Sports">Cricket</topic>
<topic group="Entertainment">HBO</topic>
<topic group="Finance">GDP</topic>
</topics>
<moods>
<mood number="4">Growth</mood>
<mood number="-5">Depression</mood>
<mood number="-3">Recession</mood>
</moods>
C.XML(结果XML)
<root>
<content> The state passed its first ban on using a handheld cellphone while driving in 2004 Nokia Vodafone Nokia Growth Recession Creicket HBO</content>
<updatedElement>
<companies>
<company count="1">Vodafone</company>
<company count="2">Nokia</company>
</companies>
<mood>1</mood>
<topics>
<topic count="1">Sports</topic>
<topic count="1">Entertainment</topic>
</topics>
<word-count>22</word-count>
</updatedElement>
</root>
在B.xml中搜索A.xml的每个公司/文本(),如果匹配找到了创建标记: TAG {company count =“该词的出现次数”} company / @ name {/公司}
如果匹配找到了创建标记,则在B.xml中搜索A.xml的每个主题/文本() 标签{主题主题=“该词的出现次数”}主题/ @ group {/ topic}
如果找到匹配项,则搜索B.xml中A.xml的每个心情/文本() [出现第一个单词* {/ mood [first word] / @ number}] + [出现第二个单词* {/ mood [second word] / @ number})] ....
获取元素的字数。
答案 0 :(得分:2)
这是一个有趣的,我在这个过程中学到了一些东西。谢谢!
注意:为了获得你想要的结果,我在A.xml(“Creicket” - &gt;“Cricket”)中修正了拼写错误。
以下解决方案使用两个特定于MarkLogic的函数:
cts:highlight
(用于将匹配的文本替换为您可以计算的节点)cts:tokenize
(将给定字符串分解为单词,空格和标点符号部分)它还包括一些特定于这两个功能的强大魔法:
$cts:text
的动态绑定(对于这个特定的用例来说并不是必需的,但我离题了),xs:string
的这些子类型的数据模型扩展:
cts:word
,cts:space
和cts:punctuation
。享受!
xquery version "1.0-ml";
(: Generic function using MarkLogic's ability to find query matches within a single node :)
declare function local:find-matches($content, $search-text) {
cts:highlight($content, $search-text, <MATCH>{$cts:text}</MATCH>)
//MATCH
};
(: Generic function using MarkLogic's ability to tokenize text into words, punctuation, and spaces :)
declare function local:get-words($text) {
cts:tokenize($text)[. instance of cts:word]
};
(: The rest of this is pure XQuery :)
let $content := doc("A.xml")/root/content,
$lookup := doc("B.xml")/WordLookUp
return
<root>
{$content}
<updatedElement>
<companies>{
for $company in $lookup/companies/company
let $results := local:find-matches($content, string($company))
where exists($results)
return
<company count="{count($results)}">{string($company/@name)}</company>
}</companies>
<mood>{
sum(
for $mood in $lookup/moods/mood
let $results := local:find-matches($content, string($mood))
return count($results) * $mood/@number
)
}</mood>
<topics>{
for $topic in $lookup/topics/topic
let $results := local:find-matches($content, string($topic))
where exists($results)
return
<topic count="{count($results)}">{string($topic/@group)}</topic>
}</topics>
<word-count>{
count(local:get-words($content))
}</word-count>
</updatedElement>
</root>
如果您对上述所有方法有任何后续问题,请与我们联系。起初,我倾向于使用cts:search
或cts:contains
,这是MarkLogic中搜索的基础。但我意识到这个例子不是关于搜索(查找文档),而是关于在已经给定的文档中查找匹配文本。如果您需要以某种方式对此进行扩展以汇总大量文档,那么您需要考虑cts:search
或cts:contains
的其他用途。
最后一点需要注意的是:如果您认为您的内容已经有<MATCH>
个元素,那么在调用cts:highlight
时您将需要使用不同的元素名称(您可以保证的名称不会冲突)与您的内容的现有元素名称)。否则,您可能会得到错误的结果数(高于准确计数)。
<强>附录:强>
我很好奇是否可以在没有cts:highlight
的情况下完成此操作,因为cts:tokenize
已经将文本分解为所有单词。使用local:find-matches
的替代实现生成相同的结果(假设您交换了函数声明的顺序,因为一个取决于另一个):
(: Find word matches by comparing them one-by-one :)
declare function local:find-matches($content, $search-text) {
local:get-words($content)[cts:stem(.) = cts:stem($search-text)]
};
它使用cts:stem
将给定单词规范化为其词干,因此,例如搜索“pass”将匹配“pass”等。但是,这仍然不适用于多词(短语) )搜索。为了安全起见,我坚持使用cts:highlight
,cts:search
和cts:contains
可以处理任何cts:查询你给它(包括简单的单词/短语搜索,如我们做上面)。
答案 1 :(得分:0)
可能有必要退后一步,询问是否可以更好地为数据和/或文档建模,以便与面向文档的数据库而不是rdbms一起使用
答案 2 :(得分:-1)
这更简单/更短且完全兼容XQuery,不包含任何实现扩展,这使得它可以与任何兼容的XQuery 1.0处理器一起使用:
let $content := doc('file:///c:/temp/delete/A.xml')/*/*,
$lookup := doc('file:///c:/temp/delete/B.xml')/*,
$words := tokenize($content, '\W+')[.]
return
<root>
{$content}
<updatedElement>
<companies>
{for $c in $lookup/companies/*,
$occurs in count(index-of($words, $c))
return
if($occurs)
then
<company count="{$occurs}">
{$c/text()}
</company>
else ()
}
</companies>
<mood>
{
sum($lookup/moods/*[false or index-of($words, data(.))]/@number)
}
</mood>
<topics>
{for $t in $lookup/topics/*,
$occurs in count(index-of($words, $t))
return
if($occurs)
then
<topic count="{$occurs}">
{data($t/@group)}
</topic>
else ()
}
</topics>
<word-count>{count($words)}</word-count>
</updatedElement>
</root>
当应用于提供的文件A.xml和B.XML(包含在本地目录c:/temp/delete
中)时,会生成所需的正确结果:
<root>
<content> The state passed its first ban on using a handheld cellphone while driving in 2004 Nokia Vodafone Nokia Growth Recession Cricket HBO</content>
<updatedElement>
<companies>
<company count="1">Vodafone</company>
<company count="2">Nokia</company>
</companies>
<mood>1</mood>
<topics>
<topic count="1">Sports</topic>
<topic count="1">Entertainment</topic>
</topics>
<word-count>22</word-count>
</updatedElement>
</root>