#postgresql #full-text-search #levenshtein-distance
#postgresql #полнотекстовый поиск #левенштейн-расстояние
Вопрос:
Я хотел бы иметь возможность запрашивать слова из столбца типа ts_vector
, но все, что имеет расстояние Левенштейна ниже X, следует считать совпадением.
Что-то вроде этого my_table
, где:
id | my_ts_vector_colum | sentence_as_text
------------------------------------------------------
1 | 'bananna':3 'tasty':2 'very':1 | Very tasty bananna
2 | 'banaana':2 'yellow':1 | Yellow banaana
3 | 'banana':2 'usual':1 | Usual banana
4 | 'baaaanaaaanaaa':2 'black':1 | Black baaaanaaaanaaa
Я хочу запросить что-то вроде «Дайте мне идентификаторы всех строк, которые содержат слово banana или слова, похожие на banana, где подобное означает, что его расстояние Левенштейна меньше 4». Таким образом, результат должен быть 1, 2 и 3.
Я знаю, что могу сделать что-то подобное select id from my_table where my_ts_vector_column @@ to_tsquery('banana');
, но это даст мне только точные совпадения.
Я также знаю, что мог бы сделать что-то подобное select id from my_table where levenshtein(sentence_as_text, 'banana') < 4;
, но это будет работать только с текстовым столбцом и будет работать только в том случае, если совпадение будет содержать только слово banana.
Но я не знаю, могу ли я объединить их или как я мог бы объединить их.
PS Таблица, в которой я хочу выполнить это, содержит около 2 миллионов записей, и запрос должен выполняться быстро (точно менее 100 мс).
P.P.S — У меня есть полный контроль над схемой таблицы, поэтому изменение типов данных, создание новых столбцов и т. Д. Было Бы вполне осуществимо.
Комментарии:
1. Вы не можете этого сделать. Лучший вариант — использовать
pg_trgm
расширение и его операторы подобия (и индекс GIN).2. «Таблица, в которой я хочу выполнить это, содержит около 2 миллионов записей, и запрос должен выполняться очень быстро» функция расстояния Левенштейна не индексируется, поэтому это кажется почти безнадежной задачей.
Ответ №1:
Предположительно, 2 миллиона коротких предложений содержат гораздо меньше отдельных слов. Но если все ваши предложения имеют «креативное» написание, возможно, нет.
Таким образом, вы, возможно, можете создать таблицу отдельных слов для относительно быстрого поиска с помощью функции неиндексированного расстояния:
create materialized view words as
select distinct unnest(string_to_array(lower(sentence_as_text),' ')) word from my_table;
И создайте точный индекс в таблице большего размера:
create index on my_table using gin (string_to_array(lower(sentence_as_text),' '));
А затем объединить:
select * from my_table join words
ON (ARRAY[word] <@ string_to_array(lower(sentence_as_text),' '))
WHERE levenshtein(word,'banana')<4;
Комментарии:
1. Хотя эта стратегия не работает для меня в моей конкретной проблеме, я все равно приму ее, поскольку это правильный ответ на исходный вопрос. Спасибо. К настоящему времени я понял, что для решения моей проблемы Postgres, вероятно, не является инструментом, и мне следует больше ориентироваться на Elasticsearch.