Postgres word_similarity 不比较单词

Cri*_*lho 8 postgresql words similarity trigram

"返回一个数字,表示第一个字符串与第二个字符串中最相似的单词的相似程度。该函数在第二个字符串中搜索最相似的单词而不是最相似的子字符串。结果的范围为零(表示两个字符串完全不同)到一个(表示第一个字符串与第二个字符串的一个单词相同)。”

这就是 word_similarity(a,b) 的定义,据我了解,它会在文本 b 中查找 WORD a,将 b 按单词拆分并获得最高匹配单词的分数。

但是,我看到了一些不一致的地方,单词匹配不是真的按单词匹配,看起来所有的三元组都被打乱并进行了比较?

例子:

select word_similarity('sage', 'message sag')
Run Code Online (Sandbox Code Playgroud)

返回 1,显然 'message' 或 'sag' 都不应该与 'sage' 匹配,但是如果我们组合来自 'message sag' 的可能的三元组,我们会发现来自 'sage' 的所有三元组都匹配,但事实并非如此真的应该发生什么,因为函数描述一个字一个字地谈论......是因为两个词彼此相邻吗?

以下将返回 0.6 分:

select word_similarity('sage', 'message test sag') 
Run Code Online (Sandbox Code Playgroud)

编辑:小提琴玩http://sqlfiddle.com/#!17/b4bab/1

kli*_*lin 10

功能与描述不一致

pgsql-bugs 邮件列表上的相关主题

作者描述的子串相似度算法比较查询字符串和文本的三元组数组。问题是优化了三元组数组(消除了重复的三元组)并丢失了有关文本单个单词的信息。

该查询说明了这个问题:

with data(t) as (
values
    ('message'),
    ('message s'),
    ('message sag'),
    ('message sag sag'),
    ('message sag sage')
)

select 
    t as "text", 
    show_trgm(t) as "text trigrams", 
    show_trgm('sage') as "string trigrams", 
    cardinality(array_intersect(show_trgm(t), show_trgm('sage'))) as "common trgms"
from data;

       text       |                       text trigrams                       |       string trigrams       | common trgms 
------------------+-----------------------------------------------------------+-----------------------------+--------------
 message          | {"  m"," me",age,ess,"ge ",mes,sag,ssa}                   | {"  s"," sa",age,"ge ",sag} |            3
 message s        | {"  m","  s"," me"," s ",age,ess,"ge ",mes,sag,ssa}       | {"  s"," sa",age,"ge ",sag} |            4
 message sag      | {"  m","  s"," me"," sa","ag ",age,ess,"ge ",mes,sag,ssa} | {"  s"," sa",age,"ge ",sag} |            5
 message sag sag  | {"  m","  s"," me"," sa","ag ",age,ess,"ge ",mes,sag,ssa} | {"  s"," sa",age,"ge ",sag} |            5
 message sag sage | {"  m","  s"," me"," sa","ag ",age,ess,"ge ",mes,sag,ssa} | {"  s"," sa",age,"ge ",sag} |            5
(5 rows)    
Run Code Online (Sandbox Code Playgroud)

最后三行中的三元组数组相同,包含查询字符串的所有三元组。

显然,实现与函数的描述不一致(该描述在文档的后续版本中有所更改):

返回一个数字,该数字指示第一个字符串与第二个字符串中最相似的单词的相似程度。该函数在第二个字符串中搜索最相似的词,而不是最相似的子字符串。


我在上述查询中使用的函数:

create or replace function public.array_intersect(anyarray, anyarray)
returns anyarray language sql immutable
as $$
    select case 
        when $1 is null then $2
        else
            array(
                select unnest($1)
                intersect
                select unnest($2)
            )
        end;
$$;
Run Code Online (Sandbox Code Playgroud)

解决方法

您可以轻松编写自己的函数以获得更多预期结果:

create or replace function my_word_similarity(text, text)
returns real language sql immutable as $$
    select max(similarity($1, word))
    from regexp_split_to_table($2, '[^[:alnum:]]') word
$$;
Run Code Online (Sandbox Code Playgroud)

相比:

with data(t) as (
values
    ('message'),
    ('message s'),
    ('message sag'),
    ('message sag sag'),
    ('message sag sage')
)

select t, word_similarity('sage', t), my_word_similarity('sage', t)
from data;

        t         | word_similarity | my_word_similarity
------------------+-----------------+--------------------
 message          |             0.6 |                0.3
 message s        |             0.8 |                0.3
 message sag      |               1 |                0.5
 message sag sag  |               1 |                0.5
 message sag sage |               1 |                  1
(5 rows)
Run Code Online (Sandbox Code Playgroud)

Postgres 11+ 中的新功能

Postgres 11+ 中有一个新函数,strict_word_similarity()它给出了问题作者预期的结果:

with data(t) as (
values
    ('message'),
    ('message s'),
    ('message sag'),
    ('message sag sag'),
    ('message sag sage')
)

select t, word_similarity('sage', t), strict_word_similarity('sage', t)
from data;

        t         | word_similarity | strict_word_similarity
------------------+-----------------+------------------------
 message          |             0.6 |                    0.3
 message s        |             0.8 |             0.36363637
 message sag      |               1 |                    0.5
 message sag sag  |               1 |                    0.5
 message sag sage |               1 |                      1
(5 rows)
Run Code Online (Sandbox Code Playgroud)