Совпадение фразы, заканчивающейся префиксом, с полнотекстовым поиском - PullRequest
9 голосов
/ 27 мая 2011

Я ищу способ эмулировать что-то вроде SELECT * FROM table WHERE attr LIKE '%text%', используя tsvector в PostgreSQL.

Я создал атрибут tsvector без использования словаря.Теперь запрос типа ...

SELECT title
FROM table
WHERE title_tsv @@ plainto_tsquery('ph:*');  

... вернул бы все заголовки, такие как «Физика», «PHP» и т. Д. Но как я могу создать запрос, который возвращает все записи, с которых начинается заголовок?с «Zend Fram» (который должен возвращать, например, «Zend Framework»)?

Конечно, я мог бы использовать что-то вроде:

SELECT title
FROM table
WHERE title_tsv @@ to_tsquery('zend')
AND   title_tsv @@ to_tsquery('fram:*');

Однако это кажется немного неловким

Итак, вопрос в следующем: есть ли способ сформулировать приведенный выше запрос, используя что-то вроде:

SELECT title
FROM table
WHERE title_tsv @@ to_tsquery('zend fram:*');

Ответы [ 4 ]

6 голосов
/ 27 мая 2011
SELECT title
FROM table
WHERE title_tsv @@ to_tsquery('zend') and
title_tsv @@ to_tsquery('fram:*')  

эквивалентно:

SELECT title
FROM table
WHERE title_tsv @@ to_tsquery('zend & fram:*')

, но, конечно, также находит "Zend не имеет фреймворка".

Конечно, вы можете выразить совпадение регулярного выражения с названиемпосле совпадения с tsquery, но вам нужно будет использовать объяснение analysis, чтобы убедиться, что он выполнялся после tsquery, а не до.

5 голосов
/ 13 декабря 2016

Postgres 9,6 представляет возможности поиска по фразе для полнотекстового поиска. Так что теперь это работает:

SELECT title
FROM  tbl
WHERE title_tsv @@ to_tsquery('zend <b><-></b> fram:*');

<-> - оператор СЛЕДУЮЩИЙ BY.

Он находит 'foo Zend Framework Bar' или 'Zend frames' , но not 'foo Zend не имеет рамочной панели' .

Цитирование замечаний к выпуску для Postgres 9.6:

В поиске по фразе можно указать запрос поиска фразы, используя новый операторы <-> и <N>. Первое означает, что лексемы до и после того, как он должен появиться рядом друг с другом в этом порядке. Последний означает, что они должны быть точно N в лексемах.

Для лучшей производительности поддержите запрос с индексом GIN:

CREATE INDEX tbl_title_tsv_idx ON tbl USING GIN (title_tsv);

Или вообще не хранить title_tsv в таблице (вздутие и усложнение записи). Вместо этого вы можете использовать индекс выражения:

CREATE INDEX tbl_title_tsv_idx ON tbl USING GIN (to_tsvector('english', title));

Вам нужно указать конфигурацию текстового поиска (часто для конкретного языка), чтобы сделать выражение неизменным. И соответственно измените запрос:

...
WHERE to_tsvector('english', title) @@ to_tsquery('english', 'zend <-> fram:*');
4 голосов
/ 11 декабря 2015

Не очень хорошее решение, но оно должно делать свою работу:

psql=# SELECT regexp_replace(cast(plainto_tsquery('Zend Fram') as text), E'(\'\\w+\')', E'\\1:*', 'g') ;
   regexp_replace    
---------------------
 'zend':* & 'fram':*
(1 row)

Может использоваться как:

psql=# SELECT title FROM table WHERE title_tsv(title) @@ to_tsquery(regexp_replace(cast(plainto_tsquery('Zend Fram') as text), E'(\'\\w+\')', E'\\1:*', 'g'));

Как это работает:

  1. переводит простой запрос в строку: cast(plainto_tsquery('Zend Fram') as text)
  2. использует регулярное выражение для добавления соответствия префикса :* к каждому поисковому запросу: regexp_replace(..., E'(\'\\w+\')', E'\\1:*', 'g')
  3. преобразует его обратно в простой запрос. to_tsquery(...)
  4. и использует его в поисковом выражении SELECT title FROM table WHERE title_tsv(title) @@ ...
2 голосов
/ 29 мая 2011

В Postgres есть способ сделать это, используя триграммы и индексы Gin / Gist.В этой статье Кристо Кайва есть простой пример, но с некоторыми грубыми краями: Поиск по подстроке .

...