我有一个大的xml数据库(30 000个文件,1.3 Go)。此数据库中的一个文件列出了数据库中存在的所有其他文件。我的目标是“简单地”检查列出的所有文件是否都存在于数据库中。但我不能处理文件的名称,只能处理文档中的XML代码。
就是这样:
declare variable $root := fn:collection();
declare function local:isValid($fileCode) {
let $fileSearchedIdentCode := $root/dmodule/identity/dmCode
return
$fileCode/@attribute1 = $fileSearchedIdentCode/@attribute1 and
$fileCode/@attribute2 = $fileSearchedIdentCode/@attribute2 and
$fileCode/@attribute3 = $fileSearchedIdentCode/@attribute3
};
<result>
{
for $fileCode in $root/file[identity/@fileType eq 'listOfFiles']/fileContent/fileEntry/fileCode
return
if (local:isValid($fileCode))
then <filePresent>1</filePresent>
else <fileNonPresent>2</fileNonPresent>
}
</result>
上面的代码是针对一个小数据库运行的,但是对于我来说,它需要大量的时间。
所以,我想知道是否有人可以帮助我改进代码以便在合理的时间内执行它;)
(我的数据库已编入索引)
感谢您的帮助!!
约翰
答案 0 :(得分:3)
似乎属性索引未应用于local:isValid
函数中的属性检查。您可以通过将它们重写为XPath谓词来实现这一目标:
declare variable $root := fn:collection();
declare function local:isValid($fileCode) {
$root/dmodule/identity/dmCode[@attribute1 = $fileCode/@attribute1
and @attribute2 = $fileCode/@attribute2
and @attribute3 = $fileCode/@attribute3]
};
<result> {
for $fileCode in $root/file[identity/@fileType = 'listOfFiles']/fileContent/fileEntry/fileCode
return
if (local:isValid($fileCode))
then <filePresent>1</filePresent>
else <fileNonPresent>2</fileNonPresent>
}</result>
完成这些更改后,BaseX中的查询信息视图告诉我使用了索引:
Compiling:
- pre-evaluating fn:collection()
- rewriting And expression to predicate(s)
- rewriting fn:boolean(@*:attribute1 = $fileCode/@attribute1)
- rewriting fn:boolean(@*:attribute2 = $fileCode/@attribute2)
- rewriting fn:boolean(@*:attribute3 = $fileCode/@attribute3)
- applying attribute index
- applying attribute index
我的测试数据的评估时间从4'500ms下降到~20ms。
答案 1 :(得分:3)
对于MarkLogic,您需要注意索引查找仅在某些表达式和函数中发生。在这种情况下,您需要更紧凑的代码。这是一个应该产生相同结果的表单,但会以简单的方式使用索引:
<result>
{
for $fileCode in
collection()/
file[identity/@fileType eq "listOfFiles"]/
fileContent/
fileEntry/
fileCode
let $fc1 := $fileCode/@attribute1/string()
let $fc2 := $fileCode/@attribute2/string()
let $fc3 := $fileCode/@attribute3/string()
return
if (collection()/
dmodule/
identity/
dmCode[
@attribute1 eq $fc1][
@attribute2 eq $fc2][
@attribute3 eq $fc3])
then <filePresent>1</filePresent>
else <fileNonPresent>2</fileNonPresent>
}
</result>
但是,该代码将根据listOfFiles
条目执行一次数据库查找,这不是最佳选择。
可以进一步优化。首先,MarkLogic是一个面向文档的数据库,其中每个文档都有一个唯一的URI。因此,如果您只是将三个属性值编码到每个文档URI中,那么效率会更高。我们可能会使用string-join(($fc1, $fc2, $fc3), '/')
之类的东西来构建URI。然后,您可以使用doc()
调用检查每个值,这比XPath查找更有效 - 即使使用索引也是如此。一旦进行了更改,listOfFiles
文档也可以存储URI而不是属性值。
其次,我认为结果格式不是很有用。它告诉你有些文件遗失了,但不是哪些文件。我会重构,以便代码只返回丢失的文档URI。我们还可以在MarkLogic中启用额外的索引:URI词典。这会自动维护所有文档URI的值索引,类似于listOfFiles
文档。使用URI词典,我可以写:
<result>{
let $uris :=
collection()/
file[identity/@fileType eq "listOfFiles"]/
fileContent/
fileEntry/
fileCode/
string-join(
(@attribute1/string(),
@attribute2/string(),
@attribute3/string()),
"/")
let $uris-present := cts:uris((), "document", cts:document-query($uris))
for $uri in $uris
where not($uri = $uris-present)
return <missing>{ $uri }</missing>
}</result>
这只需要一次数据库查找,并在内存中完成剩下的必要工作。它应该比原始查询或我的第一次迭代更好地扩展。如果您不同意我对结果格式的修改,并且仍希望查看每个输入fileCode
的结果,则可以将...where...return...
子句重构为...return...if...then...else...
,就像在原始查询中一样
请务必使用https://github.com/marklogic/cq中的个人资料工具 - 它可以帮助您尝试替代方案并发现优化机会。
答案 2 :(得分:1)
您没有在测试系统列表中包含eXist-db,但如果您有兴趣对数据进行基准测试,那么有一篇很棒的文章可以优化您的查询并智能地使用索引来加速eXist中的性能 - D b。见http://exist-db.org/exist/tuning.xml。您发布的查询应该可以正常工作而无需修改,但文章中的建议肯定会帮助您提高性能。如果您需要帮助,请随时发布到现有的邮件列表。
无论你使用哪种系统,我都非常有兴趣知道你的结果 - 而不仅仅是我 - 我认为会有广泛的兴趣。
祝你好运!答案 3 :(得分:1)
如果您在比较中使用的每个属性上定义属性范围索引,MarkLogic应该快速处理您的查询。
您可以通过MarkLogic管理界面(http:// 主机名:8001)执行此操作:
您使用的是什么版本的MarkLogic? 如果您使用的是MarkLogic 5,还可以使用Query Console来测试您的查询:
(HTTP:// 主机名:8000 / qconsole)
随时问您是否有任何疑问/让我知道它是怎么回事。 我来自MarkLogic,很乐意提供帮助。
答案 4 :(得分:0)
用等号(=)替换“eq”会有帮助吗?