Xquery MarkLogic连接查询
嗨,我是marklogic和Xquery领域的新手。我想不出在MarkLogicXQuery中编写以下逻辑的起点。如果有人能给我想法/样品,我将不胜感激,这样我就能实现以下目标: 我想根据B.XML中的单词查找来查询A.XML。查询应生成C.XML。逻辑应如下所示: A.XMLXquery MarkLogic连接查询,xquery,marklogic,Xquery,Marklogic,嗨,我是marklogic和Xquery领域的新手。我想不出在MarkLogicXQuery中编写以下逻辑的起点。如果有人能给我想法/样品,我将不胜感激,这样我就能实现以下目标: 我想根据B.XML中的单词查找来查询A.XML。查询应生成C.XML。逻辑应如下所示: A.XML 2004年,诺基亚沃达丰(Nokia Vodafone)、诺基亚成长衰退(Nokia Growth Recession)、克里克特HBO(Creicket HBO)通过了第一项禁止在开车时使用手持手机的禁令 B.XM
2004年,诺基亚沃达丰(Nokia Vodafone)、诺基亚成长衰退(Nokia Growth Recession)、克里克特HBO(Creicket HBO)通过了第一项禁止在开车时使用手持手机的禁令
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>
沃达丰
诺基亚
板球
HBO
国内生产总值
生长
抑郁
经济衰退
C.XML(结果XML)
2004年,诺基亚沃达丰(Nokia Vodafone)、诺基亚成长衰退(Nokia Growth Recession)、克里克特HBO(Creicket HBO)通过了第一项禁止在开车时使用手持手机的禁令
沃达丰
诺基亚
1.
体育
游戏娱乐
22
这是一个有趣的过程,在这个过程中我学到了一些东西。谢谢 注意:为了得到您想要的结果,我修复了a.xml中的一个输入错误(“Creicket”->“Cricket”) 以下解决方案使用两个特定于MarkLogic的函数:
(用于用节点替换匹配的文本,然后可以对其进行计数)cts:highlight
(用于将给定字符串拆分为单词、空格和标点符号部分)cts:tokenize
- 特殊变量
的动态绑定(这对于这个特定的用例来说并不是必需的,但我离题了),以及$cts:text
- 添加以下子类型的数据模型扩展名
:xs:string
cts:word
,以及cts:space
cts:标点符号
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:stem
将给定单词规范化为其词干,因此,例如,搜索“pass”将匹配“passed”,等等。但是,这对于多单词(短语)搜索仍然不起作用。所以为了安全起见,我会坚持使用cts:highlight
,它与cts:search
和cts:contains
一样,可以处理您给出的任何cts:query(包括我们上面所做的简单单词/短语搜索),它可以与任何兼容的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>
你把“2004”算作一个词。请你编辑一下这个问题,准确地定义“单词”好吗?
中的单词是否总是用空格分隔,或者是否有其他分隔符?“字数”是一个字还是两个字?那么FY-2012呢?知道一个完全兼容的XQuery解决方案不使用任何扩展函数,因此可以在任何兼容的XQuery实现下运行,这可能会很有趣。这一点很好。或许可以将上述解决方案用作文档充实脚本,这样(非规范化的)C.xml就可以成为实时应用程序代码的基础,在该代码中,您可以根据文档的字数(例如,使用上的范围索引)高效地查找文档,等等,也许每次更新部件时都会使用CPF重新运行。@EvanLenz:正如您在这里提到的CPF,我有一个问题,我将在使用CPF框架摄取时将a.xml转换为C.xml。但情况是lookup XML(B.XML),可以经常更改,现在如果更改了,如何更新数据库中以前转换的文档。我相信CPF管道将只适用于新插入的文档。我个人还没有使用CPF,但我相信它相当灵活。您应该能够自动启动一个XQuery脚本,该脚本在更新B.xml时批量更新其他文档。只需确保评估性能影响(需要更新多少文档以及更新B.xml的频率)。如果它太贵(或者不够充分),那么你会考虑另一种方法。Askk发现这个答案是有用的,就像埃文一样,即使Dimitre似乎认为这是无关紧要的。也许Dimitre应该在回答这样标记的问题之前了解更多关于MarkLogic的知识。在这里,我发现,当试图找到“Walmart Inc”或“International Walmart”这样的词时,它们都被规范化为“Walmart”(
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>
(: 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)]
};
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>
<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>