示例查询的执行计划揭示了问题的根本原因:
[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%') */
现在,它应该同时快速和严格。