忽略Oracle文本中包含搜索的顺序

时间:2019-04-12 17:22:17

标签: sql oracle text

假设我的Oracle Text索引中有2行,例如:

Row 1 'John Smith Bristol South West'
Row 2 'John James Smith London South East'

进行以下搜索的最佳方法是什么?

  1. 如果我提供搜索词: John Smith Smith John ,则应返回两行,但鉴于搜索,第一行的得分应更高字词彼此靠近。

  2. 如果我提供搜索词: Joh Smit Smit Jon ,则应返回两行,但鉴于搜索,第1行的得分应更高字词彼此靠近。

当前,我的SQL看起来像这样:

SELECT display_value
     , score(1)
  FROM  my_indx_table
 WHERE contains ( search_tokens, '%' || replace(replace( :SEARCH_STRING, '_', '\_' ), '-', '\-') || '%', 1 ) > 0
ORDER BY score( 1 ) desc;

但是它没有按我的预期工作。

在此先感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

欢迎来到黑暗,可怕的Oracle Text搜索世界。 (您可能想read the docs。)让我们从一些设置开始,以便我可以复制您的问题。

create table my_indx_table (display_value number, search_tokens varchar2(100));
create index my_indx on my_indx_table (search_tokens) indextype is ctxsys.context;
insert into my_indx_table values (1, 'John Smith Bristol South West');
insert into my_indx_table values (2, 'John James Smith London South East');
commit;
exec ctx_ddl.sync_index(idx_name => 'MY_INDX');

好的,这是您的查询。它仅返回第1行,因为该行的确切顺序为“ John Smith”。

SELECT display_value, score(1)
  FROM  my_indx_table
 WHERE contains ( search_tokens, '%' || replace(replace( 'John Smith', '_', '\_' ), '-', '\-') || '%', 1 ) > 0
ORDER BY score( 1 ) desc;

DISPLAY_VALUE   SCORE(1)
------------- ----------
            1          3

如果您要使用一个CONTAINS调用一次进行多种搜索,则可能要使用Query Templates

下一个示例使用查询重写和查询松弛。它首先尝试使用确切的短语“ John Smith”,然后搜索彼此靠近的两个单词。

SELECT display_value, score(1)
  FROM  my_indx_table
 WHERE contains ( search_tokens, 
'<query>
<textquery lang="ENGLISH" grammar="CONTEXT">' || 'John Smith' || '
 <progression>
   <seq><rewrite>transform((TOKENS, "{", "}", " "))</rewrite></seq>
   <seq><rewrite>transform((TOKENS, "{", "}", " NEAR "))</rewrite></seq>
 </progression>
</textquery>
<score datatype="FLOAT" algorithm="COUNT"/>
</query>', 
    1 ) > 0
ORDER BY score( 1 ) desc;

DISPLAY_VALUE   SCORE(1)
------------- ----------
            1       50.5
            2     6.8908

第1行的得分高于第2行,这主要是因为它包含了确切的词组。 如果您删除第一行<seq></seq>(或尝试使用“史密斯·约翰”),您会注意到,尽管行距不同,但两行的得分非常相似。默认的分数数据类型为Integer,因此第1行和第2行都将四舍五入为相同的分数14。可能不是您想要的。 (我认为这样做的原因是Oracle Text主要用于索引大型文本块,如文档或书籍。对于这样的短短语,其评分有些奇怪。)

现在让我们看一下fuzzy搜索,以解决拼写错误。此功能的默认相似性得分为60,但我将其降低为50,这样它将获得Smit = Smith。

SELECT display_value, score(1)
  FROM  my_indx_table
 WHERE contains ( search_tokens, 
'<query>
<textquery lang="ENGLISH" grammar="CONTEXT">' || 'Joh Smit' || '
 <progression>
   <seq><rewrite>transform((TOKENS, "{", "}", " "))</rewrite></seq>
   <seq><rewrite>transform((TOKENS, "{", "}", " NEAR "))</rewrite></seq>
   <seq><rewrite>transform((TOKENS, "fuzzy(", ", 50)", " "))</rewrite></seq>
   <seq><rewrite>transform((TOKENS, "fuzzy(", ", 50)", " NEAR "))</rewrite></seq>
 </progression>
</textquery>
<score datatype="FLOAT" algorithm="COUNT"/>
</query>', 
    1 ) > 0
ORDER BY score( 1 ) desc;

DISPLAY_VALUE   SCORE(1)
------------- ----------
            1      25.25
            2     3.4454

我认为这很简单。这里最令人困惑的事情可能是查询重写语法。但是,您可以使用fuzzy运算符进行很多调整,以使其能够处理您要处理的特定搜索。