我正在升级和测试大型安装程序,遇到了一个我无法理解的问题。我有很多文档,其中创建索引的方法如下:
<collection xmlns="http://exist-db.org/collection-config/1.0">
<index xmlns:mods="http://www.loc.gov/mods/v3" xmlns:xlink="http://www.w3.org/1999/xlink">
<fulltext default="none" attributes="false"/>
<lucene>
<analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer">
<param name="stopwords" type="org.apache.lucene.analysis.util.CharArraySet"/>
</analyzer>
<analyzer id="ws" class="org.apache.lucene.analysis.WhitespaceAnalyzer"/>
<text qname="p"/>
<text qname="li"/>
<text qname="h1"/>
<text qname="h2"/>
<text qname="h3"/>
</lucene>
</index>
</collection>
在我的版本2安装中,此方法非常完美。查询仅返回列表中的元素(p,li,h1,h2,h3)。它还 only 返回那些元素与文本相同的元素(如预期)。搜索功能是:
declare function ls:ls($collection as xs:string, $phrase as xs:string) as element()* {
for $hit in collection(xmldb:encode-uri($collection))//*[ft:query(.,
<query>
<phrase>{$phrase}</phrase>
</query>
)]
order by $hit/ancestor::div[@class='content']/@doc/string()
return
<tr>
<td>
{$hit/ancestor::div[@class='content']/@doc/string()}
</td>
<td>
{$hit/ancestor::div[@class='content']/@title/string()}
</td>
<td>
{local-name($hit)}
</td>
<td class="hit_text">
{normalize-space($hit)}
</td>
</tr>
};
仅查看结果,以下是网页结果的快照:
当然,这并不会显示所有结果,但是请相信我……它只是返回命名的元素,并且只返回其中包含“心脏”的元素。
在将内容导出/导入到新版本4安装中之后,大多数其他功能都可以正常运行。但是,即使在重新索引内容之后,完全相同的xQuery也会返回不需要的高级元素(如div),并且还会返回不包含搜索词组的元素。
例如,以下完全相同的查询将显示以下结果:
现在,很奇怪的是,如果我更改了删除通配符的功能,并且仅在“ h1”(或任何其他命名元素)之后执行,那么它将起作用:
for $hit in collection(xmldb:encode-uri($collection))//h1[ft:query(.,
收益:
您可以看到,与前面的示例不同,没有返回没有“心”的h1。
我在升级过程中错过了什么?我错过或不了解的Lucene有什么变化吗?
作为一种黑客(IMHO),它的工作原理是:
let $targets := collection(xmldb:encode-uri($collection))//*[local-name(.) = 'p' or local-name(.) = 'h1' or local-name(.) = 'h2' or local-name(.) = 'h3' or local-name(.) = 'li']
for $hit in $targets[ft:query(.,
<query>
<phrase>{$phrase}</phrase>
</query>
)]
但是,如果我删除创建节点集$ targets并将collection()放在“ for”中,则它将不起作用。
肯定有问题(例如,未启用全文或未运行全文?),因为在新的更新服务器中,在两者中同时运行类似的查询会花费更长的时间。
那么我在升级中错过了什么?我都有conf.xml都调用Lucene。任何有关寻找内容的提示都会很棒。
也许这是日志中的问题?我对此感到怀疑,因为搜索2.x版本的日志会显示相同的错误。
2018-12-19 19:27:05,570 [qtp14962548-143] ERROR (AnalyzerConfig.java [configureAnalyzer]:173) - Lucene index: analyzer class org.apache.lucene.analysis.WhitespaceAnalyzer not found. (org.apache.lucene.analysis.WhitespaceAnalyzer)
2018-12-19 19:27:38,852 [qtp14962548-43] INFO (NativeBroker.java [reindexCollection]:1844) - Start indexing collection /db/EIDO/data/Core
2018-12-19 19:27:54,837 [qtp14962548-43] INFO (NativeBroker.java [reindexCollection]:1854) - Finished indexing collection /db/EIDO/data/Core in 15985 ms.
我将collection.xconf更改为建议,以删除停用词并删除WhitespaceAnalyzer:
<collection xmlns="http://exist-db.org/collection-config/1.0">
<index xmlns:mods="http://www.loc.gov/mods/v3" xmlns:xlink="http://www.w3.org/1999/xlink">
<fulltext default="none" attributes="false"/>
<lucene>
<analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/>
<text qname="p"/>
<text qname="li"/>
<text qname="h1"/>
<text qname="h2"/>
<text qname="h3"/>
</lucene>
</index>
</collection>
我重新索引了该收藏集。从日志中:
2018-12-20 02:14:56,803 [qtp31631875-34] INFO (NativeBroker.java [reindexCollection]:1844) - Start indexing collection /db/EIDO/data/Core
2018-12-20 02:15:16,553 [qtp31631875-34] INFO (NativeBroker.java [reindexCollection]:1854) - Finished indexing collection /db/EIDO/data/Core in 19750 ms.
我得到完全相同的结果。
我想我正在踢。打算在本周末再次运行整个过程,删除所有内容并重试,但这没有任何意义,并且不起作用。
我不喜欢平底锅!现在,在查看结果时,基本上是在当前安装中进行以下搜索:
for $hit in collection(xmldb:encode-uri($collection))//*[ft:query(.,
<query>
<phrase>{$phrase}</phrase>
</query>
)]
返回数据库中的每个元素,无论它们是否具有$ phrase。它返回div,然后返回子级p,然后返回子级span。他们全部。单词是否真正存在于文本中都没有关系。
如果我将通配符“ *”更改为“ h1”,则它仅返回实际包含该文本的h1。那么有什么不对劲或坏了吗?我当然可以将ft:query的元素列表更改为有问题的确切元素(p,h1,h2,h3,li),但是该查询将花费4.5到2秒。
我放弃并重新安装了包括Monex在内的所有内容。我重新导出了现有的数据库并导入了它。我通常将端口更改为80,尽管我通常会进行其他更改。
现在,即使尝试运行仪表板(在导入后)也会产生:
javax.servlet.ServletException: javax.servlet.ServletException: An error occurred while processing request to /exist/apps/dashboard/: err:XPST0081 error found while loading module restxq: Error while loading module modules/restxq.xql: Invalid qname text:groups
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:146)
at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:724)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:531)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:352)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:260)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:281)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:118)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:760)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:678)
这向我表明,如果您安装了不同的应用程序,则先导出数据库再重新导入是行不通的。
不幸的是,我不得不平底锅,看看其他解决方案。我可以尝试仅重建数据或其他内容,但该应用程序有10,000个用户。无法重新创建。
这时,我只能说它还没有准备好黄金时间,只能坐在运行良好且已经运行了多年的旧数据库上。
仅需注意...在安装了全新,干净的数据库之后,没有任何更改,我可以访问Monex或仪表板。如果我从备份导入(由于与二进制不兼容,则需要这样做),一切都会中断。
对于开发人员来说,这对我来说是一个显而易见的问题。
我进行了完全干净的安装。之后,我就可以访问Monex了。然后,我还原数据库。注意:完成时,有一个问题正在询问我是否要升级应用程序。不确定正确的答案,也许这是一个问题,我回答错了(我回答不是)。
重新安装所有东西之后,我可以正常使用数据库和整个应用程序了。但是当尝试运行Monex时,我会得到:
<exception>
<path>/db/apps/monex/modules/view.xql</path>
<message>err:XPST0081 error found while loading module indexes: Error while loading module indexes.xqm: Invalid qname text:index-terms</message>
</exception>
升级应用程序是否正确?我认为这意味着我仅以纯安装方式安装的Monex被版本2备份所覆盖,这会导致错误。
我修改了导致问题的monex索引部分,并让Monex运行。因此它正在使用Lucene:
因此,关于Monex为什么运行正常但恢复我的(旧)数据库会杀死它的问题是一个问题。它不应该是AFAIK。
也许有人可以向我解释这个结果,我不理解第二个项目,但是我怀疑这是返回所有内容的一个项目:
所以。首先,我发现/ db的还原将在全新安装中破坏所有/ apps(如monex)。对我来说似乎很奇怪,或者对我或其他人的计划不周。因此,要解决此问题,我有一个全新的安装备份。
在安装新版本的eXist之后,我还原了旧数据库,然后立即再次还原了全新安装。这会用从备份中安装的最新版本覆盖所有/ apps(如monex),但不会破坏我的版本。对不起,可笑。
现在,我可以测试一下并查看正在使用的Lucene索引。但这就是告诉我的一切,没有别的(我怀疑)。
很明显,Lucene集成中的行为已更改。在我的旧版本中,我将发送每个元素,并且只会返回匹配。在此新版本中,您不能这样做。如果您发送的代码与上面的代码中的代码相同,即使没有,它也将作为“匹配”返回。因此,$ collection // *将整个结构发送到查询,并返回所有内容,无论是否命中。它以前没有这种行为。
因此,解决方案是(我什至不敢说这样的hack),您只能将项目发送到要搜索的查询中,以查看是否有受欢迎的内容。哇。再一次,对不起,但是如果我错了,请告诉我,但这完全是hack。如果我创建所有p的索引,则仅当我执行常规搜索以将p,h1等发送给它时,才期望p返回。除非您要求完全相同,否则现在发生的事情是将所有内容都发送回,命中或不命中您索引的元素的名称。
似乎是晚/早绑定的东西。在旧的eXist中,我将发送$ coll / [ft:query ...],它返回我作为索引中已标识元素的内容。现在它不能那样工作,因此您无法在$ coll / [ft:query ...之间执行for循环,因为它仍然返回 everything 。恕我直言,这是错误的。
为了解决这个问题,我做到了,基本上先执行搜索,然后遍历结果。
declare function ls:ls($collection as xs:string, $phrase as xs:string) as element()* {
let $coll := collection(xmldb:encode-uri($collection))
let $hits := ($coll//p | $coll//li | $coll//h1 | $coll//h2 | $coll//h3)[ft:query(.,
<query>
<phrase>{$phrase}</phrase>
</query>
)]
for $hit in $hits
order by $hit/ancestor::div[@class='content']/@doc/string()
return
<tr>
<td>
{$hit/ancestor::div[@class='content']/@doc/string()}
</td>
<td>
{$hit/ancestor::div[@class='content']/@title/string()}
</td>
<td>
{local-name($hit)}
</td>
<td class="hit_text">
{normalize-space($hit)}
</td>
</tr>
}
;
现在我更新了测试,它也可以工作:
let $hits := (collection(xmldb:encode-uri($collection))//*)[ft:query(.,
<query>
<phrase>{$phrase}</phrase>
</query>
)]
for $hit in $hits ...
因此,这已经非常接近我以前的水平了,我不需要遵循正确的显式元素。问题在于,现在它们不能进入for循环。
关键在这里:
(collection(xmldb:encode-uri($collection))//*)
与之相对:
collection(xmldb:encode-uri($collection))//*
所以...所有这些...解决方案是for循环需要为:
for $hit in (collection(xmldb:encode-uri($collection))//*)[ft:query(.,
<query>
<phrase>{$phrase}</phrase>
</query>
)]
既然已经解决了这个问题,也许有人想解释一下为什么没有在各个元素周围使用()的旧代码行得通,但在最新的eXist中却没有。
准确地说,我已经开放了两个系统进行测试。
版本2x:
for $hit in collection(xmldb:encode-uri($collection))//*[ft:query(.,
一秒钟,正确答案。
for $hit in (collection(xmldb:encode-uri($collection))//*)[ft:query(.,
17秒,正确答案。
版本4.5:
for $hit in collection(xmldb:encode-uri($collection))//*[ft:query(.,
10秒,答案完全错误(div和非匹配返回)
for $hit in (collection(xmldb:encode-uri($collection))//*)[ft:query(.,
一秒钟正确的答案。
在我看来,在旧的eXist中,查询什么都不返回,而在新的eXist中,查询似乎为发送的每个元素返回结果,如果不存在索引,则仍返回该结果。
在进行全新安装conf.xml
的浏览时,我在enable-query-rewriting
的xquery条目中找到了一条注释。此评论表明它是实验性的,将其设置为“是”可能会导致错误的结果。
我要指出的是,我不相信我曾碰过这个问题,并且默认安装将此值设置为“是”。我从全新安装中保存了conf.xml,因为我更改了其中的许多内容(当然),在查看全新安装时,我看到了以下内容:
<xquery enable-java-binding="no" disable-deprecated-functions="no"
enable-query-rewriting="yes" backwardCompatible="no"
enforce-index-use="always"
raise-error-on-failed-retrieval="no">
我更改为“ no”并重新启动了existent-db。现在一切都像以前一样工作,现在搜索中没有问题,并且它返回的查询与2x版本中编写的查询完全相同。
我实现了新的范围索引,并根据以下注释对集合重新编制了索引,并重新启用了查询重写功能。在检查monex时,我看到了索引,但查询未使用它们,它报告索引为旧版“范围”,而优化报告为“无索引”。
我发现我无法做到这一点(我认为通配符会这样做):
($ collection // foo | $ collection // bar)[包含(。,$ phrase)]
或这个
($ collection // foo,$ collection // bar)[包含(。,$ phrase)]
或这个
$ testnodes:= $ collection // foo | $ collection // bar
然后
$ testnodes [包含(。,$ phrase)]
虽然有效,但不使用新范围索引。这些将始终报告未使用任何索引。
但这确实使用了完全优化的新范围索引:
$ collection // foo [包含(。,$ phrase)] | $ collection // bar [包含(。,$ phrase)]
答案 0 :(得分:1)
我们应该先清除错误...
org.apache.lucene.analysis.core.WhitespaceAnalyzer
。虽然看起来好像您不是按其“ id”引用空白分析器的,但您可以将其删除。
StandardAnalyzer
的配置对我来说似乎是错误的。您已经指定了stopwords
参数,但是:
org.apache.lucene.analysis.util. CharArraySet
,并且如果只需要默认停用词,则可以完全省略该参数。
进行了这些更改后,应尝试重新索引并再次监视日志。
在那之后,您应该使用eXist 4.5.0中仪表板上的Monex应用程序来检查可用索引,以检查数据是否按预期被索引了。
来自@ kevin-brown的评论:
根据我今天看到的内容,如果执行此操作($ collection // foo | $ collection // bar)[fn:contains(。,'string')],则不使用索引。但是,如果我这样做$ collection // foo [fn:contains(。,'string')] | $ collection // bar [fn:contains(。,'string')],使用新范围索引,并且优化已满。
我可以确认,在XQuery的某些公式中,eXist-db没有正确优化查询以利用范围索引。这肯定是一个错误!
eXist-db的Java Admin客户端允许您显示查询的踪迹:
($collection//foo | $collection//bar)[fn:contains(., $string)]
未使用索引,生成了跟踪:
$collection/descendant::{}foo union
$collection/descendant::{}bar
[contains(self::node(), $string)]
$collection//foo[fn:contains(., $string)] | $collection//bar[fn:contains(., $string)]
正确使用了索引,生成了跟踪:
$collection
(# exist:optimize-field #)
(# exist:optimize #) {
descendant::{}foo[range:contains(self::node(), $string)]
}
union $collection
(# exist:optimize-field #)
(# exist:optimize #) {
descendant::{}bar[range:contains(self::node(), $string)]
}
在(2)中,我们可以清楚地看到XQuery编译指示指示了优化。这意味着可以检测到合适的指标,并将在评估过程中使用它。
通过比较,在(1)中我们看到eXist未能正确检测到可以进行优化的可用索引。
可悲的是,似乎eXist-db可能为此使用了错误的轴,即后代而不是后代或自身。
我为eXist-db打开了一个GitHub问题,报告了此问题-https://github.com/eXist-db/exist/issues/2363
答案 1 :(得分:1)
尽管我还是eXist的新手,但在我看来,有两个想法正在混为一谈。
让Lucene索引某些内容与在查询Xpath上放置谓词不同。 Lucene索引的qname
(我相信)并不意味着给定的元素将不受查询。这只是Lucene为加快搜索速度索引的一个问题?您发现通过使用谓词可以提高速度,这一事实表明确实如此。
当我进行搜索时,无论我告诉Lucene如何索引,我仍然会限制要查询的元素。我个人并不认为这是黑客,只是减少了“搜索池”。我不使用local-name()
作为谓词。相反,我将使用元素本身。我不确定使用local-name()
相对于此是否有成本:
let $coll := collection(xmldb:encode-uri($collection))
let target := $coll//p | $coll//h1 | $coll//h2 | $coll//h3 | $coll//li
根据您的XML层次结构,通过使用collection(xmldb:encode-uri($collection))//some-element
减少节点池,您可能会发现甚至更快的速度
以上方法可能会更有效地使用Lucene索引?值得测试。
此外,尽管我不知道您的XML的层次结构是什么,但是您也可以明确地告诉Lucene 忽略某些元素(但这通常适用于嵌套在您自己的元素中的那些元素)索引):
<ignore qname="div"/>
注意:我使用的是eXist 4.4
已添加:除了Lucene,还尝试使用range index。另外,我在qnames
中看不到命名空间(另外,您有两个正在运行的名称空间,并且我在范围索引中为xmlns:xs
添加了第三个名称空间)。
该示例假定(从上面链接的eXist文档复制)命名空间mods
进行演示。但是,如果xml集合中有特定的命名空间,则必须将其附加到每个qname
上。
<collection xmlns="http://exist-db.org/collection-config/1.0">
<index xmlns:mods="http://www.loc.gov/mods/v3"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<fulltext default="none" attributes="false"/>
<range>
<create qname="mods:p" type="xs:string"/>
<create qname="mods:li" type="xs:string"/>
<create qname="mods:h1" type="xs:string"/>
<create qname="mods:h2" type="xs:string"/>
<create qname="mods:h3" type="xs:string"/>
</range>
<lucene>
<analyzer class="org.apache.lucene.analysis.standard.StandardAnalyzer"/>
<text qname="mods:p"/>
<text qname="mods:li"/>
<text qname="mods:h1"/>
<text qname="mods:h2"/>
<text qname="mods:h3"/>
<ignore qname="mods:div"/>
</lucene>
</index>
</collection>
删除未使用的名称空间声明。
答案 2 :(得分:1)
eXist-db 2.2于2014年发布,因此跨两个主要版本的长时间跳转升级往往不太容易。
您的代码似乎仍在使用传统范围索引,这可能是导致不良结果的原因(如monex报告)。
此索引已标记为已弃用,而要使用新的范围索引。
如果您不能提供MWE,则需要弄清楚哪些查询调用了旧范围索引并将其更改为新范围,或者完全禁用了旧范围索引。
我不建议使用和旧的monex存在于新文件中,并且在要求将默认应用程序升级到新版本时说yes
。您仍然可以在没有任何默认应用的情况下运行生产站点。
无法从示例中看出for $hit in (collection(xmldb:encode-uri($collection))//*)[ft:query(.,
是如何在应用程序中回避调用旧范围索引的,但这应该可以为您提供线索。我的猜测是,如果摆脱了这些调用,您将看到for $hit in collection(xmldb:encode-uri($collection))//*[ft:query(.,
以相同的方式起作用和工作。