Как использовать полнотекстовый поиск в PostgreSQL в Ubuntu 16.04

Вступление

Full-text search (FTS) - это метод, используемый поисковыми системами для поиска результатов в базе данных. Его можно использовать для поиска результатов поиска на таких веб-сайтах, как магазины, поисковые системы, газеты и т. Д.

В частности, FTS извлекает documents, которые являются объектами базы данных, содержащими текстовые данные, которые не полностью соответствуют критериям поиска. Это означает, что когда пользователь ищет «кошки и собаки», например, приложение, поддерживаемое FTS, может возвращать результаты, которые содержат слова отдельно (только «кошки» или «собаки»), содержат слова в другом порядке. («Собаки и кошки») или содержат варианты слов («кошка» или «собака»). Это дает приложениям преимущество в угадывании значения пользователя и более быстром возвращении более релевантных результатов.

Технически говоря, системы управления базами данных (СУБД), такие как PostgreSQL, обычно допускают частичный поиск текста с использованием предложений LIKE. Однако эти запросы, как правило, неэффективны для больших наборов данных. Они также ограничены соответствием точному вводу пользователя, что означает, что запрос может не дать результатов, даже если есть документы с соответствующей информацией.

Используя FTS, вы можете создать более мощный механизм текстового поиска, не вводя дополнительных зависимостей от более продвинутых инструментов. В этом руководстве мы будем использовать PostgreSQL для хранения данных, содержащих статьи для гипотетического новостного веб-сайта, а затем узнаем, как запрашивать базу данных с использованием FTS и выбирать только лучшие совпадения. В качестве последнего шага мы реализуем некоторые улучшения производительности для запросов полнотекстового поиска.

Предпосылки

Прежде чем начать это руководство, вам потребуется следующее:

Если вы настроили сервер PostgreSQL, не следуя приведенному выше руководству, убедитесь, что у вас есть пакет + postgresql-contrib +, использующий + sudo apt-get list postgresql-contrib +.

Шаг 1 - Создание примера данных

Для начала нам понадобятся некоторые данные для тестирования плагина полнотекстового поиска, поэтому давайте создадим несколько примеров данных. Если у вас уже есть собственная таблица с текстовыми значениями, вы можете перейти к шагу 2 и сделать соответствующие замены, следуя далее.

В противном случае первым шагом является подключение к базе данных PostgreSQL со своего сервера. Поскольку вы подключаетесь с одного хоста, по умолчанию вам не нужно будет вводить пароль.

sudo -u postgres psql sammy

Это установит интерактивный сеанс PostgreSQL с указанием имени базы данных, с которой вы работаете, в нашем случае это ++. Вы должны увидеть командную строку базы данных + sammy = # +.

Затем создайте пример таблицы в базе данных под названием + news +. Каждая запись в этой таблице будет представлять новостную статью с заголовком, некоторым содержанием и именем автора вместе с уникальным идентификатором.

CREATE TABLE news (
  id SERIAL PRIMARY KEY,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  author TEXT NOT NULL
);

+ id + - это первичный индекс таблицы со специальным типом + SERIAL +, который создает счетчик с автоприращением для таблицы. Это уникальный идентификатор, который автоматически попадает в индекс базы данных. Мы поговорим об этом индексе в шаге 3, когда рассмотрим улучшения производительности.

Затем добавьте некоторые примеры данных в таблицу, используя команду + INSERT +. Данные этого примера в приведенной ниже команде представляют некоторые примеры статей новостей

INSERT INTO news (id, title, content, author) VALUES
   (1, 'Pacific Northwest high-speed rail line', 'Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.', 'Greg'),
   (2, 'Hitting the beach was voted the best part of life in the region', 'Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.', 'Ethan'),
   (3, 'Machine Learning from scratch', 'Bare bones implementations of some of the foundational models and algorithms.', 'Jo');

Теперь, когда в базе данных есть данные для поиска, мы можем попробовать написать несколько запросов.

Шаг 2 - Подготовка и поиск документов

Первым шагом здесь является создание одного документа с несколькими текстовыми столбцами из таблицы базы данных. Затем мы можем преобразовать полученную строку в вектор слов, который мы будем использовать в запросах.

Во-первых, нам нужно собрать все столбцы вместе, используя функцию сцепления PostgreSQL + || + и функцию преобразования + to_tsvector () +.

SELECT title || '. ' || content as document, to_tsvector(title || '. ' || content) as metadata FROM news WHERE id = 1;

Это возвращает первую запись как весь документ, а также ее преобразованную версию, которая будет использоваться для поиска.

Output-[ RECORD 1 ]-----------------------------------------------------
document    | Pacific Northwest high-speed rail line. Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.

metadata    | '140':18 'current':8 'high':4 'high-spe':3 'ideal':29 'line':7 'mile':19 'none':25 'northwest':2 'option':14 'pacif':1 'rail':6 'seattl':21 'speed':5 'travel':16 'vancouv':23

Вы можете заметить, что в преобразованной версии меньше слов + metadata + в приведенном выше выводе, чем в исходном + document +. Некоторые слова различны, и каждое слово имеет точку с запятой и число, добавленное к нему. Это связано с тем, что функция + to_tsvector () + нормализует каждое слово, чтобы мы могли найти варианты формы одного и того же слова, а затем сортирует результат по алфавиту. Число - это позиция мира в «+ документе». Могут быть дополнительные разделенные запятыми позиции, если нормализованное слово появляется более одного раза.

Теперь мы можем использовать этот преобразованный документ, чтобы воспользоваться возможностями FTS, выполнив поиск по термину «Исследования».

SELECT * FROM news WHERE to_tsvector(title || '. ' || content) @@ to_tsquery('Explorations');

Давайте рассмотрим функции и операторы, которые мы использовали здесь.

Функция + to_tsquery () + переводит параметр, который может быть прямым или слегка скорректированным пользовательским поиском, в критерии текстового поиска, которые уменьшат ввод таким же образом, как + to_tsvector () +. Кроме того, функция позволяет указать используемый язык и указать, должны ли все слова присутствовать в результате или только одно из них.

Оператор + @@ + определяет, соответствует ли + tsvector + + tsquery + или другому + tsvector +. Возвращает + true или` + false`, что упрощает использование как часть критериев + WHERE.

Output-[ RECORD 1 ]-----------------------------------------------------
id      | 2
title   | Hitting the beach was voted the best part of life in the region
content | Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.
author  | Ethan

Запрос возвратил документ, который содержит слово «Исследование», хотя слово, которое мы использовали для поиска, было «Исследования». Использование оператора + LIKE + вместо FTS здесь дало бы пустой результат.

Теперь, когда мы знаем, как подготовить документы для FTS и как структурировать запросы, давайте рассмотрим способы повышения производительности FTS.

Шаг 3 - Улучшение производительности FTS

Генерация документа каждый раз, когда мы используем запрос FTS, может стать проблемой производительности при использовании больших наборов данных или небольших серверов. Одним из хороших решений этого, которое мы здесь реализуем, является создание преобразованного документа при вставке строки и сохранение его вместе с другими данными. Таким образом, мы можем просто получить его с помощью запроса вместо того, чтобы генерировать его каждый раз.

Сначала создайте дополнительный столбец с именем ++ для существующей таблицы + news +.

ALTER TABLE news ADD "document" tsvector;

Теперь нам нужно использовать другой запрос для вставки данных в таблицу. В отличие от шага 2, здесь нам также нужно подготовить преобразованный документ и добавить его в новый столбец + document +, например так:

INSERT INTO news (id, title, content, author, document)
VALUES (4, 'Sleep deprivation curing depression', 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.', 'Patel', to_tsvector('Sleep deprivation curing depression' || '. ' || 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.'));

Добавление нового столбца в существующую таблицу требует, чтобы мы сначала добавили пустые значения для столбца + document +. Теперь нам нужно обновить его сгенерированными значениями.

Используйте команду + UPDATE +, чтобы добавить недостающие данные.

UPDATE news SET document = to_tsvector(title || '. ' || content) WHERE document IS NULL;

Добавление этих строк в нашу таблицу является хорошим улучшением производительности, но в больших наборах данных у нас все еще могут быть проблемы, потому что базе данных все равно придется сканировать всю таблицу, чтобы найти строки, соответствующие критериям поиска. Простым решением этого является использование индексов.

Database index - это структура данных, в которой данные хранятся отдельно от основных данных, что повышает производительность операций поиска данных. Он обновляется после любых изменений в содержимом таблицы за счет дополнительных записей и сравнительно небольшого дискового пространства. Его небольшой размер и индивидуальная структура данных позволяют индексам работать намного эффективнее, чем использование основного табличного пространства для выбора запросов.

В конечном счете, индексы помогают базе данных быстрее находить строки, выполняя поиск с использованием специальных структур данных и алгоритмов. PostgreSQL имеет several типы индексов, которые подходят для определенных типов запросов. Наиболее подходящими для этого варианта использования являются индексы GiST и GIN. Основное различие между ними заключается в том, насколько быстро они могут извлекать документы из таблицы. GIN создается медленнее при добавлении новых данных, но быстрее запрашивает; GIST строится быстрее, но требует дополнительного чтения данных.

Поскольку GiST примерно в 3 раза медленнее извлекает данные, чем GIN, мы создадим здесь индекс GIN.

CREATE INDEX idx_fts_search ON news USING gin(document);

Используя индексированный столбец + document, запрос` + SELECT` также стал немного проще.

SELECT title, content FROM news WHERE document @@ to_tsquery('Travel | Cure');

Вывод будет выглядеть так:

Output-[ RECORD 1 ]-----------------------------------------------------
title   | Sleep deprivation curing depression
content | Clinicians have long known that there is a strong link between sleep, sunlight and mood.
-[ RECORD 2 ]-----------------------------------------------------
title   | Pacific Northwest high-speed rail line
content | Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.

Когда вы закончите, вы можете выйти из консоли базы данных с помощью + \ q +.

Заключение

В этом руководстве рассказывалось, как использовать полнотекстовый поиск в PostgreSQL, включая подготовку и хранение документа метаданных и использование индекса для повышения производительности. Если вы хотите больше узнать о FTS в PostgreSQL, взгляните на official PostgreSQL документацию по полнотекстовому поиску.

Related