4

使用 JCR-SQL2 查询时,我注意到CONTAINS运算符找到的节点与条件中的字符串不完全相同。

例子

以下查询:

SELECT * FROM [nt:base] AS s WHERE CONTAINS(s.*, 'my/search-expression')

不仅会找到包含my/search-expression字符串的节点,还会找到带有类似my/another/search/expression.

为什么查询不能只找到提供的确切字符串?如何更改以缩小结果范围?

此问题旨在由我自己回答,以共享知识 - 但请随时添加您自己的答案或改进现有答案。

4

2 回答 2

6

示例查询的执行计划揭示了问题的根本原因:

  [nt:base] as [s] /* lucene:lucene(/oak:index/lucene) +:fulltext:my +:fulltext:search +:fulltext:expression ft:("my/search-expression") where contains([s].[*], 'my/search-expression') */

CONTAINS操作员触发全文搜索。非单词字符,如“/”或“-”,用作单词分隔符。因此,查询会查找包含以下词的所有节点:“my”、“search”和“expression”。

可以用它做什么?有几种选择。

1.使用双引号

如果要将结果限制为具有给定单词的准确顺序且它们之间没有任何其他单词的短语,请将搜索表达式放在双引号内:

SELECT * FROM [nt:base] AS s WHERE CONTAINS(s.*, '"my/search-expression"')

现在,执行计划不同了:

[nt:base] as [s] /* lucene:lucene(/oak:index/lucene) :fulltext:"my search expression" ft:("my/search-expression") where contains([s].[*], '"my/search-expression"') */

查询现在将查找整个短语,而不是单个单词。但是,它仍然会忽略非单词字符,因此也会找到这样的短语:“my search expression”或“my-search-expression”。

2.使用LIKE表达式(不推荐)

如果您只想找到确切的短语,保留非单词字符,您可以使用以下LIKE表达式:

SELECT * FROM [nt:base] AS s WHERE s.* LIKE '%my/search-expression%'

但是,这要慢得多。在解释执行计划时,我需要添加另一个条件以避免超时。对于此查询:

SELECT * FROM [nt:base] AS s WHERE s.* LIKE '%my/search-expression%'  AND ISDESCENDANTNODE([/content/my/content]) 

执行计划是:

[nt:base] as [s] /* traverse "/content/my/content//*" where ([s].[*] like '%my/search-expression%') and (isdescendantnode([s], [/content/my/content])) */

它只会找到带有以下短语的节点:“my/search-expression”。

3.使用双引号并细化结果

使用第一种方法(使用双引号)并稍后细化结果可能会更好CONTAINS,例如,如果查询是从应用程序运行的,则在应用程序代码中。

4.混合包含和喜欢

另一种选择是将全文搜索和LIKE表达式与AND

SELECT * FROM [nt:base] AS s WHERE CONTAINS(s.*, '"my/search-expression"') AND s.* LIKE '%my/search-expression%'

现在的执行计划是:

[nt:base] as [s] /* lucene:lucene(/oak:index/lucene) :fulltext:"my search expression" ft:("my/search-expression") where (contains([s].[*], '"my/search-expression"')) and ([s].[*] like '%my/search-expression%') */

现在,它应该同时快速和严格。

于 2018-01-03T12:37:21.907 回答
1

有同样的问题。

所以基本上你应该为你的 lucene 索引定义不同的标记器,在我的例子中,“空白”标记器就可以了。

使用标准标记器“my/search-expression”分为 3 个标记“my”、“search”、“expression”。标准分词器使用一些特殊字符作为分隔符。

这就是为什么对于“我的/搜索表达式”你得到 0 个结果的原因。

另一个例子:

带有空格标记器的“其他我的搜索/表达式”分为:

“其他”、“我的”、“搜索/表达”

当您搜索“some-other my”时,这应该会返回结果。

标记器列表

Lucene 索引示例:

<yourLucene
jcr:primaryType="oak:QueryIndexDefinition"
type="lucene"
async="async"
evaluatePathRestrictions="{Boolean}true"
includedPaths="[/somepath]"
queryPaths="[/somepath]"
compatVersion="{Long}2">
<analyzers jcr:primaryType="nt:unstructured">
    <default jcr:primaryType="nt:unstructured">
        <tokenizer
            jcr:primaryType="nt:unstructured"
            name="Whitespace"/>
        <filters jcr:primaryType="nt:unstructured">
            <Standard jcr:primaryType="nt:unstructured"/>
            <LowerCase jcr:primaryType="nt:unstructured"/>
            <Stop jcr:primaryType="nt:unstructured"/>
        </filters>
    </default>
</analyzers>
<indexRules jcr:primaryType="nt:unstructured">
    <nt:unstructured jcr:primaryType="nt:unstructured">
        <properties jcr:primaryType="nt:unstructured">
            <someprop
                jcr:primaryType="nt:unstructured"
                name="someprop"
                propertyIndex="{Boolean}true"
                type="String"/>
        </properties>
    </nt:unstructured>
</indexRules>
于 2020-09-08T10:54:07.790 回答