【问题标题】:Postgres word_similarity not comparing wordsPostgres word_similarity 不比较单词
【发布时间】:2017-10-27 01:54:01
【问题描述】:

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

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

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

例子:

select word_similarity('sage', 'message sag')

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

以下,将返回 0.6 分:

select word_similarity('sage', 'message test sag') 

编辑:玩弄http://sqlfiddle.com/#!17/b4bab/1

【问题讨论】:

  • 我根本不知道这个函数,但是如果我没记错的话sagemessage test sag的子字符串。
  • 是的,但是 word_similarity 声明它检查 WORDS。另外,word_similarity('sage', 'message') --> 0.6 word_similarity('sage', 'message s') --> 0.8 word_similarity('sage', 'message sag') --> 1.0 word_similarity('sage ', 'message test sag') --> 0.6 看起来奇怪而且错误,根本不比较单词三元组。
  • word_similarity('sage', 'message sag sag') 返回什么?

标签: postgresql similarity words trigram


【解决方案1】:

功能与描述不符

pgsql-bugs mailing list.相关主题

子字符串相似度算法described by the author 比较查询字符串和文本的三元数组。问题是三元组被优化(重复的三元组被消除)并且丢失了关于文本中单个单词的信息。

查询说明了问题:

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)    

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

很明显,实现与函数描述不一致(在文档的后期版本中描述有所改变):

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


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

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;
$$;

解决方法

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

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
$$;

比较:

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)

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)

【讨论】:

  • 这是一个很好的解决方案,我希望原始函数能够以这种方式工作,我希望找到对其行为的一些解释,即使将一个词与另一个词进行比较时,相似度返回的值与相似度不同,如果两个字符串都有 1 个单词,它们应该在哪里返回相同的值...我还期望使用自定义函数而不是本机/扩展函数时性能会更差?
  • 在我的临时基准测试中,自定义函数比原始函数慢了 2 倍。这是获得正确结果的好价格吗?无论如何,我认为问题可能是reported as a bug.
  • 是的,我想这还不错,我只是拒绝认为这真的是一个错误,因为扩展程序已经发布了很长时间,但遗憾的是没有关于函数行为的有用信息。
  • 我已经使用pg_trgm 很长时间了,在我看来这是一个很好的产品。但是,word_similarity() 是一个相对较新的函数(来自 Postgres 9.6)。老实说,直到昨天我才知道它的存在。
  • 被报告为bug,看来毕竟是bug! postgresql-archive.org/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-05-27
  • 1970-01-01
  • 2013-11-19
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多