PostgreSQL: Полнотекстовый поиск - Как искать отдельные слова? - PullRequest
16 голосов
/ 25 марта 2010

После опубликованного здесь вопроса о том, как я могу увеличить скорость одного из моих методов поиска SQL, мне посоветовали обновить таблицу, чтобы использовать полнотекстовый поиск. Это то, что я сейчас сделал, используя индексы Gist для ускорения поиска. В некоторых «простых» запросах я заметил заметное увеличение, которое меня очень радует.

Однако у меня возникают трудности с поиском неполных слов. Например, у меня есть несколько записей, которые содержат слово Squire (454), и у меня есть несколько записей, которые содержат Squirrel (173). Теперь, если я ищу Squire, он возвращает только 454 записи, но я также хочу, чтобы он также возвращал записи Squirrel.

Мой запрос выглядит так

SELECT title 
FROM movies 
WHERE vectors @@ to_tsoquery('squire');

Я думал, что смогу сделать to_tsquery('squire%'), но это не работает.
Как мне получить его для поиска частичных совпадений?

Кроме того, в моей базе данных есть записи, которые представляют собой фильмы и другие, которые являются просто телешоу. Они различаются по «» по названию, так что «Мунстер» - это телешоу, а «Мунстерс» - это фильм шоу. То, что я хочу, - это искать только телешоу и фильмы. Есть идеи, как мне этого добиться?

С уважением Anthoni

Ответы [ 6 ]

47 голосов
/ 09 августа 2010

Попробуй,

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire:*')

Это работает на PostgreSQL 8.4+

31 голосов
/ 17 февраля 2012

Anthoni,

Если вы планируете использовать только кодировку ASCII (я знаю, это может быть сложно), очень жизнеспособным вариантом может быть модуль Trigram (pg_trgm): http://www.postgresql.org/docs/9.0/interactive/pgtrgm.html

Trigram использует встроенные методы индексации, такие как Gist и Gin. Единственное изменение, которое вы должны сделать, это при определении индекса указать класс оператора gist_trgm_ops или gin_trgm_ops.

Если модули contrib еще не установлены, в Ubuntu это так же просто, и в командной строке запускается следующая команда:

# sudo apt-get install postgresql-contrib

После того, как модули contrib станут доступны, вы должны установить расширение pg_trgm в соответствующую базу данных. Это можно сделать, выполнив следующий запрос PostgreSQL к базе данных, в которую вы хотите установить модуль:

CREATE EXTENSION pg_trgm;

После того, как расширение pg_trgm установлено, мы готовы повеселиться!

-- Create a test table.
CREATE TABLE test (my_column text)
-- Create a Trigram index.
CREATE INDEX test_my_colun_trgm_idx ON test USING gist (my_column gist_trgm_ops);
-- Add a couple records
INSERT INTO test (my_Column) VALUES ('First Entry'), ('Second Entry'), ('Third Entry')
-- Query using our new index --
SELECT my_column, similarity(my_column, 'Frist Entry') AS similarity FROM test WHERE my_column % 'Frist Entry' ORDER BY similarity DESC
5 голосов
/ 21 ноября 2012

@ alexander-mera решение отлично работает!

Примечание : Также убедитесь, что вы преобразовали пробелы в +. Например, если вы ищете squire knight.

SELECT title FROM movies WHERE to_tsvector(title) @@ to_tsquery('squire+knight:*')
5 голосов
/ 25 марта 2010

Даже используя LIKE, вы не сможете получить 'белку' из squire%, потому что у 'белки' есть два 'r. Чтобы получить Сквайра и Белку, вы можете выполнить следующий запрос:

SELECT title FROM movies WHERE vectors @@ to_tsquery('squire|squirrel');

Чтобы различать фильмы и сериалы, вы должны добавить столбец в вашу базу данных. Однако есть много способов снять шкуру с этой кошки. Вы можете использовать подзапрос, чтобы заставить postgres сначала найти фильмы, соответствующие «squire» и «squirrel», а затем выполнить поиск в этом подмножестве, чтобы найти заголовки, начинающиеся с «». Можно создать индексы для использования в LIKE '"%...' поиски.

Не исследуя другие возможности индексации, вы также можете запустить их - возитесь с ними, чтобы найти самый быстрый:

SELECT title 
FROM (
   SELECT * 
   FROM movies 
   WHERE vectors @@ to_tsquery('squire|squirrel')
) t
WHERE title ILIKE '"%';

или

SELECT title 
FROM movies 
WHERE vectors @@ to_tsquery('squire|squirrel') 
  AND title ILIKE '"%';
2 голосов
/ 05 июля 2016

Широкое решение этой проблемы - использовать функцию PG ts_rewrite для настройки таблицы псевдонимов, которая работает для альтернативных совпадений (см. Перезапись запроса ). Это охватывает случаи, подобные вашему выше, а также обрабатывает совершенно разные случаи, такие как поиск tree rat и получение результатов для squirrel и т. Д.

Полная информация и объяснение по этой ссылке, но суть в том, что вы можете настроить таблицу псевдонимов с 2 столбцами ts_query и передать запрос этой таблицы в свой поиск, например, так:

CREATE TABLE aliases (t tsquery primary key, s tsquery);
INSERT INTO aliases VALUES(to_tsquery('supernovae'), to_tsquery('supernovae|sn'));

SELECT ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases');

Результатом является окончательный запрос, который выглядит примерно так:

WHERE vectors @@ ts_rewrite(to_tsquery('supernovae & crab'), 'SELECT * FROM aliases')

Это похоже на настройку тезауруса в PG, но работает без полного переиндексации при каждом добавлении чего-либо. Когда вы сталкиваетесь с небольшими вариациями правописания и случаями «когда я ищу это, я ожидаю таких результатов», очень легко просто быстро добавить их в таблицу. Вы можете добавить больше столбцов в эту таблицу, а также, если запрос, основанный на ts_rewrite, возвращает 2 ожидаемых to_tsquery столбца.

Когда вы изучите эту документацию, вы увидите также примеры для настройки производительности. Существует баланс между использованием триграммы для чистой скорости и вектором / запросом / перезаписью для надежности.

0 голосов
/ 25 марта 2010

Одна вещь, которая может сработать, это разбить слово, которое вы ищете, на более мелкие части. Таким образом, вы могли бы искать вещи, которые имеют Squi или Quir или Squire или т. Д. ... Я не уверен, насколько это будет эффективно, но это может помочь.

При поиске фильма или фильма вы можете попробовать поместить текст в одинарную кавычку. так что это будет либо «показать», либо «показать». Я думаю, что это также может сработать.

...