您要求“对于任何给定的文档 URI”进行字数统计。但是您假设解决方案涉及索引或词典,这不一定是一个好的假设。如果您想要从面向文档的数据库中获取特定于文档的内容,通常最好直接处理文档。
因此,让我们专注于单个文档的高效字数统计解决方案,然后从那里开始。好的?
以下是我们如何获取单个元素(包括所有子元素)的字数统计。这可能是您文档的根目录:doc($uri)/*
.
declare function local:word-count($root as element())
as map:map
{
let $m := map:map()
let $_ := cts:tokenize(
$root//text())[. instance of cts:word]
! map:put($m, ., 1 + (map:get($m, .), 0)[1])
return $m
};
这会生成一个地图,我发现它比平面文本更灵活。每个键是一个单词,值是计数。变量$doc
已经包含您的示例 XML。
let $m := local:word-count($doc)
for $k in map:keys($m)
return text { $k, map:get($m, $k) }
inside 1
This 2
is 2
paragraph 1
highlighted 1
EXAMPLE 1
header 1
are 1
word 1
words 1
these 1
tag 1
And 1
a 2
请注意,映射键的顺序是不确定的。添加一个order by
如果您愿意的话。
let $m := local:word-count($doc)
for $k in map:keys($m)
let $v := map:get($m, $k)
order by $v descending
return text { $k, $v }
如果要查询整个数据库,Geert 的解决方案使用cts:words
可能看起来不错。它使用词典作为单词列表,并使用一些索引查找来进行单词匹配。但它最终会为每个单词词典单词的每个匹配文档遍历 XML:O(nm)。为了正确地做到这一点,代码必须做类似的工作local:word-count
确实如此,但一次只能说一个词。许多单词将匹配相同的文档:“the”可能在 A 和 B 中,“then”也可能在 A 和 B 中。尽管使用了词典和索引,但通常这种方法会比简单应用慢local:word-count
到整个数据库。
如果您想要查询整个数据库并愿意更改 XML,您可以将每个单词包装在一个word
元素(或任何您喜欢的元素名称)。然后创建一个字符串类型的元素范围索引word
。现在你可以使用cts:values
and cts:frequency
直接从范围索引中提取答案。这将是 O(n),成本比cts:words
方法,并且可能比local:word-count
,因为根本不会访问任何文档。但生成的 XML 相当笨拙。
我们回去申请吧local:word-count
到整个数据库。首先调整代码,以便调用者提供地图。这样我们就可以构建一个包含整个数据库字数的地图,并且我们只查看每个文档一次。
declare function local:word-count(
$m as map:map,
$root as element())
as map:map
{
let $_ := cts:tokenize(
$root//text())[. instance of cts:word]
! map:put($m, ., 1 + (map:get($m, .), 0)[1])
return $m
};
let $m := map:map()
let $_ := local:word-count($m, collection()/*)
for $k in map:keys($m)
let $v := map:get($m, $k)
order by $v descending
return text { $k, $v }
在我的笔记本电脑上,它在不到 100 毫秒的时间内处理了 151 个文档。大约有 8100 个单词和 925 个不同的单词。得到相同的结果cts:words
and cts:search
花费了不到 1 秒的时间。所以local:word-count
效率更高,并且对于这项工作来说可能足够高效。
既然您可以高效地构建字数统计图,那么如果您可以保存它呢?本质上,您将建立我们自己的字数统计“索引”。这很容易,因为地图具有 XML 序列化。
(: Construct a map. :)
map:map()
(: The document constructor creates a document-node with XML inside. :)
! document { . }
(: Construct a map from the XML root element. :)
! map:map(*)
所以你可以打电话local:word-count
在插入或更新每个新 XML 文档时。然后将字数统计图存储在文档的属性中。使用 CPF 管道、通过 RecordLoader 使用您自己的代码或在 REST 上传端点等中执行此操作。
当您想要单个文档的字数统计时,只需调用xdmp:document-properties
or xdmp:document-get-properties
,然后调用map:map
右侧 XML 上的构造函数。如果您需要多个文档的字数统计,您可以轻松编写 XQuery 将这些映射合并为单个结果。