[英]Most efficient way to count occurrences in XQuery for multiple values
我有1581个单词需要在荷兰语的XML语料库中查找(5亿字)。 该语料库本身在许多数据库中被分开。 (你可以在这里阅读原因。)我们使用BaseX作为服务器(版本7.9),它使用XQuery作为输入。
我有兴趣通过中性确定器( het )或非中性确定器( de )找出每个单词在语料库中的次数 - 这是通过寻找由NP(名词短语)组成的XPath结构来完成的。有两个女儿,一个带有lemma de或het的限定词 ,和一个头,这是我感兴趣的词。
de的示例结构
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="de"] and node[@rel="hd" and @pt="n" and @word="accelerator"]]
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="de"] and node[@rel="hd" and @pt="n" and @word="accountant"]]
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="de"] and node[@rel="hd" and @pt="n" and @word="ace"]]
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="de"] and node[@rel="hd" and @pt="n" and @word="acroniem"]]
het的示例结构
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="het"] and node[@rel="hd" and @pt="n" and @word="accelerator"]]
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="het"] and node[@rel="hd" and @pt="n" and @word="accountant"]]
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="het"] and node[@rel="hd" and @pt="n" and @word="ace"]]
/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="het"] and node[@rel="hd" and @pt="n" and @word="acroniem"]]
在XQuery中,我会这样做,对于每个XPath结构:
count(for $node in db:open("mydatabase")/treebank/tree/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="het"] and node[@rel="hd" and @pt="n" and @word="accelerator"]] return $node)
这很好用。 问题是,这需要很长时间。 每次都需要按顺序打开相同(数千个)数据库,并对每个单词重复此过程。 我的问题是,是不是有办法连接一些查询。 我有一些想法,但我不确定如何执行它们 - 另外,我不确定BaseX可以处理多少个参数。
这可能是最简单的情况。 通过这样做,我至少减少了一半的查询需求。 但我不知道在找到结果时如何区分这两者。 例如,如果我将我的XPath代码更改为:
... (@lemma="de" or @lemma="het") ...
我应该找到所有案例,但我怎么能区分其中一个呢? 换句话说,如果我使用了XPath,我会得到一个数的计数功能回到XQuery的,但没有办法,我知道这是去的,哪些是对 ?
我可以将它们连接起来,而不是为每个单词执行新的查询:
... (@word="accelerator" or @word="accountant" or @word="ace" or ...) ...
但同样,我如何区分这些价值观呢? 我可以将所有1581值放在一个XPath中吗? BaseX可以处理吗?
我不是XQuery的专家,但在伪代码中,我猜这样的事情是可能的:
$wordlist = ['accelerator', 'accountant', 'ace', 'acroniem'];
$determinerlist = ['de', 'het'];
$db = 'mydatabase';
foreach ($wordlist as $word) {
foreach ($determinerlist as $det) {
count(for $node in db:open("'.$db.'")/treebank/tree/node[@cat="np" and node[@rel="det" and @pt="lid" and @lemma="'.$det.'"] and node[@rel="hd" and @pt="n" and @word="'.$word.'"]] return $node);
}
}
我不确定如何将计数分配给XQuery中的数组变量,如果可能的话,但XML输出可能看起来像这样(但当然欢迎更好的变体):
<results>
<result word="accelerator">
<neuter>12</neuter>
<nonneuter>3</nonneuter>
</result>
<result word="accountant">
<neuter>4</neuter>
<nonneuter>0</nonneuter>
</result>
<result word="ace">
<neuter>14</neuter>
<nonneuter>2</nonneuter>
</result>
<result word="acroniem">
<neuter>3</neuter>
<nonneuter>7</nonneuter>
</result>
</results>
然后,我可以使用带有正则表达式或XML twig的Perl来获取我需要的值。
正如你所看到的那样,问题是找到一个合适的XQuery代码是有效的,并且考虑到我在一个巨大的语料库中查找了1581个单词,并且要经历的数据库数量也很多(千)。 对于每个数据库查找,通过Perl建立新连接。
如果您有任何疑问,请发表评论,我会尽可能地回答。
一般来说,如果你利用索引而不是让你的查询遍历一万亿个节点,那么BaseX查询将是最快的(通常,非常快)。 除非您修改了默认的数据库创建选项,否则BaseX默认为您创建TEXT,ATTRIBUTE和TOKEN索引。 (BaseX还尝试重新编写查询以利用索引 - 虽然它并不总是成功)。
因此,假设您的数据库是使用ATTRIBUTE索引构建的,您应该能够按以下方式重新编写查询:
db:attribute('dbname', 'accelerator', 'word')/parent::*
上面使用的db:attribute函数将为数据库'dbname'返回任何属性的父元素,其中'accelerator'作为@word的值。 显然你可以根据需要预测这个查询,就像这样,从你之前的例子来看:
db:attribute('dbname', 'accelerator', 'word')
[parent::node[@rel="hd" and @pt="n"]]
[ancestor::node
[@cat="np"]
[child::node[@rel="det" and @pt="lid" and @lemma="het"]
]
]
以下是有关BaseX索引功能的详细文档。 我已经将它用于大型(> 20 GB)数据库的速度查询。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.