Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > СУБД, общие вопросы > Нужны правильный FullText & geo search


Автор: kami 21.11.2014, 22:59
Здравствуйте, уважаемые!
Есть база... пусть будет с адресами, тем более что пока так и есть (улицы и дома) + их координаты.

Нужно: искать в базе по адресам (пример - подсказки Яндекс.Карт и 2ГИС при вводе адреса) и по координатам (найти ближайшие записи к указанной в запросе точке)
Что пробовали:
1. MySQL. поиск адреса проходит великолепно, к примеру запрос "11* лини*" с легкостью и правильно находит в числе первых "10-11 линии В.О.", "11-я Красноармейская линия" и т.п. Причем - можно задавать вес каждому слову в запросе, используя "<", ">". Но с поиском ближайших к точке - плохо, геопространственный индекс используется только с функциями "входит/не входит в прямоугольник", а при определении гео-дистанции перебирается полностью таблица.
2. MongoDB. Здесь отлично и очень быстро отрабатывает поиск ближайших. А вот с полнотекстовым какая-то беда. Во-первых, поиск регистро-зависимый. Во вторых - поиск по "Красно" выдает адреса типа "Краснопутиловская ул." (+ дома на этой улице), "Красная ул." и т.п. А вот запрос "Красноп" уже не выдает вообще ничего. И это "ничего" продолжается до ввода "Краснопутиловск". Имхо, нелогично как-то.
3. Postgre. Ситуация аналогична Монге, такое ощущение, что используется один и тот же движок. Попытки настроить индекс на pg_catalog.simple FTS (взамен pg_catalog.russian) не привели к успеху - "индекс не поддерживается на utf-8 строках".

Вопрос: есть ли что-нибудь, что совмещает:
- простоту полнотекстового поиска MySQL (я подозреваю, что там "неправильный" FTS, а правильный как раз у Mongo и Postgre, но нам он подходит идеально)
- поиск ближайших к указанной точке. В коллекциях гарантированно будут только координаты типа POINT и запрашиваться будут в 99% именно "ближайшие к вот такой точке".

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

Автор: tzirechnoy 23.11.2014, 17:05
Про постгрэсс: так преобразуй его в cp1251 и lower case. Можно -- дажэ не выделяя какого-то поля, прямо в индэксе (только в запросах -- те жэ функцыи, разумеется, должны быть).

Автор: kami 23.11.2014, 19:34
Цитата(tzirechnoy @  23.11.2014,  17:05 Найти цитируемый пост)
так преобразуй его в cp1251 и lower case. Можно -- дажэ не выделяя какого-то поля, прямо в индэксе (только в запросах -- те жэ функцыи, разумеется, должны быть). 

Это не избавляет от проблемы 
Цитата(kami @  21.11.2014,  22:59 Найти цитируемый пост)
поиск по "Красно" выдает адреса типа "Краснопутиловская ул." (+ дома на этой улице), "Красная ул." и т.п. А вот запрос "Красноп" уже не выдает вообще ничего. И это "ничего" продолжается до ввода "Краснопутиловск". 


Автор: tzirechnoy 24.11.2014, 14:28
А можно конкретный пример индэкса и запроса, как это получилось?

Автор: tzirechnoy 24.11.2014, 15:05
А, кстати понял, да. Надо запросы на начало через :* выделять.

Код
SELECT * FROM ftstest WHERE lower(name) @@ to_tsquery('simple', 'красно:*');

Автор: kami 24.11.2014, 15:17
tzirechnoy, да, конечно. Я подозреваю, что просто не умею готовить Postgres.

Тестовая база, примерно 11 тыс. записей.
Исходная таблица (скрипт скопирован из pgAdmin, "лишние" поля удалил):
Код

CREATE TABLE public.streets
(
  id integer NOT NULL DEFAULT nextval('streets_id_seq'::regclass),
  streetfull character varying(150) NOT NULL DEFAULT ''::character varying,
  fullname character varying(250) NOT NULL DEFAULT ''::character varying,
  streetname character varying(120) NOT NULL DEFAULT ''::character varying,
  streetprefix character varying(10) NOT NULL DEFAULT ''::character varying,
  parentcityname character varying(120) NOT NULL DEFAULT ''::character varying,
  geom point,
  CONSTRAINT streets_primary PRIMARY KEY (id)
)
WITH (
  OIDS=FALSE
);

После чего - создан индекс:
Код

CREATE INDEX IX_STREETS ON streets USING gin(to_tsvector('russian', streetfull));

Ну и собственно - запрос:
Код

SELECT * FROM streets
WHERE to_tsvector('russian', streetfull) @@ to_tsquery('russian', 'Краснопу');

Вот по этому запросу ничего не находит.
А по запросу "Краснопутиловск" - находит правильную улицу - "Краснопутиловская". Моя нипанимать, в чем дело...

Добавлено через 1 минуту и 54 секунды
Upd: пока писал, не увидел ответ выше smile
Пробую...

Добавлено через 10 минут и 28 секунд
Цитата(tzirechnoy @  24.11.2014,  15:05 Найти цитируемый пост)
Надо запросы на начало через :* выделять.

Ткните в меня ссылкой на документацию, пожалуйста, где описаны вот такие вот маски для поиска, а то я так долго буду тыкаться, как слепой котенок smile
С "Красноп:*" отрабатывает отлично, а вот с другим запросом "11:* лини:*" - ошибка синтаксиса. Теоретически, он должен находить улицы типа "11-я линия В.О.", "11-я Красноармейская линия" и т.п. Т.е. - в одном запросе - 2 начала слов для поиска.

Автор: tzirechnoy 24.11.2014, 17:34
Потому, что to_tsquery('russian', '11:* & лини:*')

http://www.postgresql.org/docs/9.3/static/textsearch-controls.html#TEXTSEARCH-PARSING-QUERIES

Там ещё ссылка на секцыю 8.11 -- тожэ полезная.

Автор: kami 24.11.2014, 20:46
tzirechnoy, спасибо. Относительно понятнее стало, почему так происходит.
Все-таки поведение отличается от того, что мне необходимо.
В частности:
Код
SELECT '10-11 линии В.О.'::tsvector

выдает 
Цитата
"'10-11' 'В.О.' 'линии'"

Т.е.  он считает, что 10-11 - это одна лексема (или как там они называются)...
Даже если сделать так: 
Код
SELECT to_tsvector('simple', '10-11 линии В.О.')

то выдается 
Цитата
"'-11':2 '10':1 'в':4 'линии':3 'о':5"

Как результат - искомые "10-11 линии" оказываются далеко-далеко по ранжированию на запрос типа
Код

SELECT 
  ts_rank_cd(to_tsvector('russian', streetfull), query) AS rank, 
  * 
FROM streets, to_tsquery('russian', '11:* | лини:*') query
WHERE query @@ to_tsvector('russian', streetfull)
ORDER BY rank DESC, Level1Name, Level2Name, Level3Name


А вот MySQL разбивает на лексемы так, как в нашем случае нужно - "10" "11" "В.О." "линии". Вернее, я считаю, что у него не используются лексемы как таковые, а просто разбиение на слова...

Что я продолжаю делать не так?

Автор: tzirechnoy 25.11.2014, 00:55
Цитата
Что я продолжаю делать не так?


Ну, он, очевидно, не матчит 11.

Если Вы думаете, что - у вас безсмысленнен -- то замените минусы на пробелы.

Заодно, кстати, и lower() сделайте.

Ну и, надо долго медитировать, конечно, по вопросу -- подходит вам встроенный русский словарь или лучшэ без него или лучшэ как-то его изменить.

Автор: kami 27.11.2014, 23:08
Прошу прощения за долгое молчание.

Ну, собственно, вопрос можно закрывать.
Путем долгих терзаний и сомнений между Монгой и Постгресом, был выбран Постгрес. Не последнюю роль в этом процессе сыграла монетка, которая легла орлом вверх smile
Бесспорных аргументов "за", по большому счету не было: в обеих системах обеспечить необходимый нам вид поиска можно только с дополнительными извратами. Плюсом Постгреса стала возможность использовать маску ":*", у Монги такого не нашел :(. А жаль, ибо на ней уже крутятся логи многих сервисов и очень хорошо себя чувствуют...

Подводя итог: в таблицах для обеспечения FTS будут созданы "нормализованные" поля, содержащие вместо всех знаков препинания - пробелы, все буквы - в одном регистре (без разницы - нижний или верхний, емнип, выбрали нижний). Эти поля будут заполняться программно, а не средствами СУБД. Дополнительно - рядом с ними будут поля типа ::tsvector (шаблон simple) (правда, моя нипанимать - зачем это надо, неужто полнотекстовый индекс, созданный по "нормализованному полю" - на самом деле не индекс?)

Полагаю, все следующие вопросы пойдут уже в профильный раздел.

Еще раз спасибо.

Автор: tzirechnoy 27.11.2014, 23:43
Цитата
 для обеспечения FTS будут созданы "нормализованные" поля,


А зачем? Всё равно ведь большынство БД -- IO-bound, смысл увеличивать количество данных на диске, экономя на преобразовании "на лету"?

Добавлено через 48 секунд
Цитата
правда, моя нипанимать - зачем это надо, неужто


Моя тожэ.

Добавлено через 1 минуту и 19 секунд
В смысле -- я совершэнно непонимаю, зачем тебе это понадобилось и почему ты считаешь, что это поле что-нибудь изменит.

Автор: tzirechnoy 28.11.2014, 00:57
А, я кажэтся понял. Вы не увидел postgres function index http://www.postgresql.org/docs/9.3/static/indexes-expressional.html.

Так вот, они здесь есть. И можно писать что-то вроде 
Код
CREATE INDEX fts_ndx ON table USING gin (to_tsvector('simple', translate(lower(name), '#$@!,.=-:;`"', '            ')));


При поиске, конечно, надо будет всегда указывать ровно те жэ функцыи, т.е.:
Код

SELECT ... FROM table WHERE to_tsvector('simple', translate(lower(name), '#$@!,.=-:;`"', '            ')) @@ 'query_expression';

Автор: kami 29.11.2014, 15:40
Цитата(tzirechnoy @  28.11.2014,  00:57 Найти цитируемый пост)
Так вот, они здесь есть. И можно писать что-то вроде 

Кошмар какой smile
Примерно такие же непонятки я испытывал, когда узнал, что у MySQL на каждый чих задается своя кодировка.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)