postgresql - PostgreSQL 中的短语比较

标签 postgresql

我将如何在 Postgres 表中的 varchar 列中查找包含相同 3 词短语的行?

其他问题中的大多数全文搜索建议都是将向量与特定查询进行比较,但我正在寻找的是包含任何 3 个词短语的行作为其他行。

例子:

SELECT * 
FROM types t1 
WHERE EXISTS (SELECT * 
              FROM types t2 
              WHERE t1.name phrase_matches t2.name 
                AND t1.id > t2.id)

在这里,phrase_matches 是一个组合操作,其中

'my foo bar baz' phrase_matches 'foo bar baz whatever' 返回 true

'my foo bar baz' phrase_matches 'foo baz whatever bar' 返回 false

编辑:来自 Google 的任何人的更新 - 没有临时表的解决方案,使用连接,在具有 18k 行的表上花费了一个多小时。临时表版本总共运行了几秒钟。

最佳答案

制作一个 trigrams-to-row-ids 表,然后在 trigram 列上自连接。浪费了很多空间,但这是最简单的方法。在 klin's answer to How to extract n-gram word sequences from text in Postgres 的帮助下:

-- your table
CREATE TABLE phrases (
  id INT,
  phrase TEXT
);

-- your data
INSERT INTO phrases (id, phrase) VALUES
(1, 'my foo bar baz'),
(2, 'foo bar baz whatever'),
(3, 'foo baz whatever bar');

-- function to extract word n-grams
-- from https://stackoverflow.com/a/51571001/240443
CREATE OR REPLACE FUNCTION word_ngrams(str TEXT, n INT)
RETURNS SETOF TEXT LANGUAGE plpgsql AS $$
DECLARE
    i INT;
    arr TEXT[];
BEGIN
    str := regexp_replace(str, '[^[:alnum:]|\s]', '', 'g');
    arr := string_to_array(str, ' ');
    FOR i in 1 .. cardinality(arr) - n + 1 LOOP
        RETURN NEXT array_to_string(arr[i : i+n-1], ' ');
    END LOOP;
END $$;

-- table of all trigrams (my foo bar, foo bar baz, bar baz whatever...)
-- and rows they belong to
CREATE TEMPORARY TABLE trigrams (
  id INT,
  trigram TEXT
);

-- make sure JOIN doesn't take forever
CREATE INDEX ON trigrams (trigram, id);

-- extract the trigrams into their stylish new - yet temporary - home
INSERT INTO trigrams SELECT id, word_ngrams(phrase, 3) FROM phrases;

-- see which original rows have common trigrams
SELECT DISTINCT T1.id AS id1, T2.id AS id2
FROM trigrams T1 JOIN trigrams T2
  ON T1.trigram = T2.trigram
  AND T1 < T2;

-- | id1 | id2
---+-----+----
-- |   1 |   2

你也可以直接使用 word_ngrams 函数,不需要临时表,但是会慢很多。时间或空间,只选一个 :P 这将替换之前 CREATE TEMPORARY TABLE 代码片段中的所有内容(但仍然使用 klin 的精彩功能)。

SELECT DISTINCT T1.id AS id1, T2.id AS id2
FROM phrases T1 JOIN phrases T2
  ON EXISTS (
    SELECT word_ngrams(T1.phrase, 3)
    INTERSECT
    SELECT word_ngrams(T2.phrase, 3)
  )
  AND T1.id < T2.id;

-- | id1 | id2
---+-----+----
-- |   1 |   2

关于postgresql - PostgreSQL 中的短语比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53669690/

相关文章:

postgresql - 如何在 postgresQL 中的 json 值的 array_agg 中组合 DISTINCT 和 ORDER BY

postgresql - 通过查询提取重复项

sql - Ruby on Rails - 具有连接和顺序的范围

Spring @Procedure 和 List 作为返回

python - 数组值必须以 "{"或维度信息开头

postgresql - puppet:创建符号链接(symbolic link)并将文件保留在原始目录中

postgresql - 在 Ubuntu 上打开端口以远程访问 PostgresQL

sql - 进行内部连接时,索引顺序很慢

sql - 如何在 PostgreSQL 事务中获取实时?

ruby-on-rails - 运行任何脚本的 Rails 无效字节