计算 XQuery 中多个值出现次数的最有效方法
Most efficient way to count occurrences in XQuery for multiple values
我需要在 XML 荷兰语语料库(5 亿个单词)中查找 1581 个单词。这个语料库本身被分成许多数据库。 (你可以阅读为什么 here。)我们使用 BaseX 作为服务器(版本 7.9),它使用 XQuery 作为输入。
我想知道每个词在语料库中有多少次带有中性限定词 (het) 或非中性限定词 (de) - 这是通过寻找一个 XPath 结构来完成的,该结构由一个 NP(名词短语)组成,它有两个女儿,即具有引理 de 或 [=48 的限定词=]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 可以处理多少参数。
- 合并 de 和 het 查询。
这可能是最直接的案例。通过这样做,我至少将查询需求减少了一半。但是我不知道在找到结果时如何区分两者。例如,如果我将我的 XPath 代码更改为:
... (@lemma="de" or @lemma="het") ...
我应该找到所有的情况,但是我怎么才能区分一个或另一个呢?换句话说,如果我使用那个 XPath,我将从 XQuery 中的计数函数中得到一个数字,但我无法知道哪些是 de 哪些是 那个?
- 同样的想法可以应用到接近结尾的单词属性
我可以按如下方式连接它们,而不是对每个词执行新查询:
... (@word="accelerator" or @word="accountant" or @word="ace" or ...) ...
但同样,我如何区分这些值?我可以将所有 1581 个值放在一个 XPath 中吗? BaseX 可以处理吗?
- 一个带有单词列表的 for 循环,然后 return 以 XML 格式返回大量单词的结果(如果 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>
然后我可以 运行 通过带有正则表达式的 Perl 或 XML twig 来获得我需要的值。
如您所见,可能的问题是找到合适的高效 XQuery 代码,并且考虑到我有 1581 个单词要在庞大的语料库中查找,并且要经过的数据库数量是也很多(数千)。对于每个数据库查找,都会通过 Perl 建立一个新连接。
如有任何问题,请发表评论,我会尽力解答。
一般来说,如果您利用索引而不是让您的查询遍历一万亿个节点,BaseX 查询将是最快的(通常快得令人眼花缭乱)。 BaseX 默认为您创建 TEXT、ATTRIBUTE 和 TOKEN 索引,除非您修改了默认的 DB 创建选项。 (BaseX 还尝试重写查询以利用索引——尽管在这方面并不总是成功)。
因此假设您的数据库是使用 ATTRIBUTE 索引构建的,您应该能够按照这些行重写查询:
db:attribute('dbname', 'accelerator', 'word')/parent::*
上面使用的db:attribute函数将return,对于数据库'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)数据库的速度查询产生了很大的影响。
我需要在 XML 荷兰语语料库(5 亿个单词)中查找 1581 个单词。这个语料库本身被分成许多数据库。 (你可以阅读为什么 here。)我们使用 BaseX 作为服务器(版本 7.9),它使用 XQuery 作为输入。
我想知道每个词在语料库中有多少次带有中性限定词 (het) 或非中性限定词 (de) - 这是通过寻找一个 XPath 结构来完成的,该结构由一个 NP(名词短语)组成,它有两个女儿,即具有引理 de 或 [=48 的限定词=]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 可以处理多少参数。
- 合并 de 和 het 查询。
这可能是最直接的案例。通过这样做,我至少将查询需求减少了一半。但是我不知道在找到结果时如何区分两者。例如,如果我将我的 XPath 代码更改为:
... (@lemma="de" or @lemma="het") ...
我应该找到所有的情况,但是我怎么才能区分一个或另一个呢?换句话说,如果我使用那个 XPath,我将从 XQuery 中的计数函数中得到一个数字,但我无法知道哪些是 de 哪些是 那个?
- 同样的想法可以应用到接近结尾的单词属性
我可以按如下方式连接它们,而不是对每个词执行新查询:
... (@word="accelerator" or @word="accountant" or @word="ace" or ...) ...
但同样,我如何区分这些值?我可以将所有 1581 个值放在一个 XPath 中吗? BaseX 可以处理吗?
- 一个带有单词列表的 for 循环,然后 return 以 XML 格式返回大量单词的结果(如果 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>
然后我可以 运行 通过带有正则表达式的 Perl 或 XML twig 来获得我需要的值。
如您所见,可能的问题是找到合适的高效 XQuery 代码,并且考虑到我有 1581 个单词要在庞大的语料库中查找,并且要经过的数据库数量是也很多(数千)。对于每个数据库查找,都会通过 Perl 建立一个新连接。
如有任何问题,请发表评论,我会尽力解答。
一般来说,如果您利用索引而不是让您的查询遍历一万亿个节点,BaseX 查询将是最快的(通常快得令人眼花缭乱)。 BaseX 默认为您创建 TEXT、ATTRIBUTE 和 TOKEN 索引,除非您修改了默认的 DB 创建选项。 (BaseX 还尝试重写查询以利用索引——尽管在这方面并不总是成功)。
因此假设您的数据库是使用 ATTRIBUTE 索引构建的,您应该能够按照这些行重写查询:
db:attribute('dbname', 'accelerator', 'word')/parent::*
上面使用的db:attribute函数将return,对于数据库'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)数据库的速度查询产生了很大的影响。