Firebird нечеткий поиск. А как ищете вы? [РЕШЕНО]

Модератор: Модераторы

Аватара пользователя
wofs
постоялец
Сообщения: 379
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань
Контактная информация:

Firebird нечеткий поиск. А как ищете вы? [РЕШЕНО]

Сообщение wofs »

Доброго дня!
Продолжаю писать свою утилитку и по ходу возник вопрос - а как искать то?
Что я подразумеваю под "нечетким" поиском.
Имеется таблица несколько 200-300 тыс записей.
Необходимо произвести поиск по полю VARCHAR(300).

В ней есть строка:

Код: Выделить всё

Тюль красная с цветами на кольцах высота 2,5м [УльтраКолор]

Я хочу получить эту (и другие, подходящие под условие поиска) строку следующими запросами:

Код: Выделить всё

1. Тюль с цветами
2. Тюль высота 2,5м
3. Красная тюль
4. Тюль с цветами красная

Варианты 1 и 2 - решаются просто стандартным LIKE.
Варианты 3 и 4 пока для меня темный лес.

Поиск по сети выдал решения Sphinx и Lucene.
Но мне они кажутся излишне громоздкими для поставленной задачи.
А как ищете в FireBird вы?
Последний раз редактировалось wofs 30.03.2018 21:56:21, всего редактировалось 1 раз.
olegy123
долгожитель
Сообщения: 1643
Зарегистрирован: 25.02.2016 11:10:20

Сообщение olegy123 »

Код: Выделить всё

SELECT * FROM table where text='*тюль*'

"*" - любые буквы..

Добавлено спустя 1 минуту 31 секунду:
Но заметь - еще нужно свести все буквы в нужный регистр.

Добавлено спустя 57 секунд:
UPPER/LOWER

Добавлено спустя 47 секунд:

Код: Выделить всё

SELECT * FROM table where LOWER(text)='*тюль*'
Vadim
долгожитель
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Сообщение Vadim »

Ещё можно присобачить полнотекстный индекс. В FB по умолчанию его нету, а вот в RedBase он уже подключён в комплекте.
Аватара пользователя
wofs
постоялец
Сообщения: 379
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань
Контактная информация:

Сообщение wofs »

olegy123 писал(а):"*" - любые буквы..

Строка (для наглядности).

Код: Выделить всё

Тюль красная с цветами на кольцах высота 2,5м [УльтраКолор]

Так я ищу, только через LIKE

Код: Выделить всё

SELECT * FROM table where UPPER(text) LIKE UPPER('%тюль%');

Могу искать и так:

Код: Выделить всё

SELECT * FROM table where UPPER(text) LIKE UPPER('%Тюль%высота%2,5м%');

Это не проблема, проблема в том, что запрос

Код: Выделить всё

SELECT * FROM table where UPPER(text) LIKE UPPER('%Тюль%с%цветами%красная%');

не даст результата.
Это задача, которую я пытаюсь решить.

Возникла идея (все одно LIKE заставляет сервер перебирать все записи) написать UDF модуль для FireBird, который будет реализовывать один из алгоритмов нечеткого поиска (расстояние Левенштейна, шинглы и т.п.), если разорбаться с построением нужного индекса в БД и научиться его использовать, то может получиться не сильно медленнее, чем LIKE (так как LIKE %text%, если верить руководству по языку FireBird - не использует индексы).

Но прежде хотелось бы услышать кто как ищет, если это не тайна конечно :)

Добавлено спустя 3 минуты 4 секунды:
Vadim писал(а):Ещё можно присобачить полнотекстный индекс. В FB по умолчанию его нету, а вот в RedBase он уже подключён в комплекте.

Читал, там он на Lucene основан. Имхо громоздко как-то - еще и Java машину тащить с собой. Чай не корпоративный сервер, а всего-лишь небольшая утилитка для личного пользования.

Добавлено спустя 5 минут 4 секунды:
Мне не нужна морфология и прочие плюшки. На данном этапе мне нужно лишь искать слова в разнобой.

Добавлено спустя 2 минуты 28 секунд:
Для полноценного поиска соответствий я думаю о модуле UDF (ибо меня очень устраивал (кроме скорости) алгоритм шинглов). Но не хотелось бы использовать такое ресурсоемкое решение для обычного поиска. Возможно, что я чего-то недопонимаю в SQL и поиск с перестановкой слов можно организовать малой кровью.
Последний раз редактировалось wofs 20.10.2017 09:50:09, всего редактировалось 1 раз.
olegy123
долгожитель
Сообщения: 1643
Зарегистрирован: 25.02.2016 11:10:20

Сообщение olegy123 »

Ах, да "%" в FB. Извините, это в других принято "*"..

Добавлено спустя 3 минуты 5 секунд:
Для поддержки регулярных выражений добавлен новый предикат SIMILAR TO
http://firebirdsql.su/doku.php?id=similar_to
Аватара пользователя
wofs
постоялец
Сообщения: 379
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань
Контактная информация:

Сообщение wofs »

olegy123 писал(а):Для поддержки регулярных выражений добавлен новый предикат SIMILAR TO
http://firebirdsql.su/doku.php?id=similar_to

Спасибо, видел, но как то не под тем углом смотрел.
olegy123
долгожитель
Сообщения: 1643
Зарегистрирован: 25.02.2016 11:10:20

Сообщение olegy123 »

wofs писал(а):Так я ищу, только через LIKE

Код: Выделить всё

SELECT * FROM table where UPPER(text) LIKE UPPER('%тюль%');


Еще можно так:

Код: Выделить всё

SELECT * FROM table where UPPER(text) IN (UPPER('%тюль%'),UPPER('%ткань%'),UPPER(''%Тюль%с%цветами%красная%'));
Аватара пользователя
wofs
постоялец
Сообщения: 379
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань
Контактная информация:

Сообщение wofs »

Хм... а SIMILAR вроде то, что нужно для этой задачи.
Запрос вида

Код: Выделить всё

SELECT NAME FROM table WHERE
LOWER(text) SIMILAR TO LOWER('%тюль%')
AND
LOWER(text) SIMILAR TO LOWER('%с%')
AND
LOWER(text) SIMILAR TO LOWER('%цветами%')
AND
LOWER(text) SIMILAR TO LOWER('%красная%')

теряет во времени выполнения порядка 60%, по сравнению с

Код: Выделить всё

SELECT * FROM table where UPPER(text) LIKE UPPER('%Тюль%красная%с%цветами%');

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

Добавлено спустя 1 минуту 45 секунд:
olegy123 писал(а):Еще можно так:
Код: Выделить всё
SELECT * FROM table where UPPER(text) IN (UPPER('%тюль%'),UPPER('%ткань%'),UPPER(''%Тюль%с%цветами%красная%'));

У меня не вышло получить ничего, отличное от null. :(
tema
постоялец
Сообщения: 376
Зарегистрирован: 24.03.2011 19:19:27

Сообщение tema »

Религия не позволяет использовать and?
Для "Тюль с цветами красная", например

Код: Выделить всё

select * from table where LOWER(text) LIKE LOWER('%Тюль%') and LOWER(text) LIKE LOWER('%с%') and LOWER(text) LIKE LOWER('%цветами%') and LOWER(text) LIKE LOWER('%красная%') and 
olegy123
долгожитель
Сообщения: 1643
Зарегистрирован: 25.02.2016 11:10:20

Сообщение olegy123 »

IN - это пересечения.. им можно объединить несколько условий.
Хотя (UPPER('%тюль%'),UPPER('%ткань%'),UPPER(''%Тюль%с%цветами%красная%')) - неправильно..
Блин совсем забыл я SQL

Добавлено спустя 1 минуту 16 секунд:
tema писал(а):Для "Тюль с цветами красная", например

А если строка не однозначная? Что то выпадает из условия?
Аватара пользователя
wofs
постоялец
Сообщения: 379
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань
Контактная информация:

Сообщение wofs »

tema писал(а):Религия не позволяет использовать and?

Точно!

Добавлено спустя 1 минуту 10 секунд:
olegy123 писал(а):А если строка не однозначная? Что то выпадает из условия?

Выпадет, но в данном контексте это не важно.
Спасибо всем, развел на пустом месте демагогию :)

Добавлено спустя 56 секунд:
olegy123 писал(а):IN - это пересечения.. им можно объединить несколько условий.

Я почитаю поподробнее, сдается есть вариант использовать для другого.
tema
постоялец
Сообщения: 376
Зарегистрирован: 24.03.2011 19:19:27

Сообщение tema »

olegy123 писал(а):
tema писал(а):Для "Тюль с цветами красная", например

А если строка не однозначная? Что то выпадает из условия?

В смысле? Перебрать циклом все слова запроса и составить такой select, который я написал
Аватара пользователя
WAYFARER
энтузиаст
Сообщения: 567
Зарегистрирован: 09.10.2009 00:00:04
Откуда: г. Курган

Сообщение WAYFARER »

http://www.textolution.com/ftsib.asp - в каком состоянии проект - неизвестно
Или использовать сфинкса (https://www.firebirdsql.org/en/sphinx-full-text-search/ )
Или как вариант перенести БД на Ред (http://www.red-soft.ru/ru/main_products.html#rbd)

Добавлено спустя 3 минуты 59 секунд:

Код: Выделить всё

select * from table where LOWER(text) LIKE LOWER('%Тюль%') and LOWER(text) LIKE LOWER('%с%') and LOWER(text) LIKE LOWER('%цветами%') and LOWER(text) LIKE LOWER('%красная%') 

Таким запросом найдется именно "тюль с цветами красная", но не найдется просто "красная тюль" или "тюль с цветами".

Добавлено спустя 6 минут 20 секунд:
Еще как вариант вообще отказаться от использования FireBird.
Для каждого проекта нужно подбирать СУБД согласно потребностям.
Mirage
энтузиаст
Сообщения: 881
Зарегистрирован: 06.05.2005 20:29:07
Откуда: Russia
Контактная информация:

Сообщение Mirage »

Для этого используется Sphynx или Elastic search. Последний есть обертка на Lucene.
Что они делают - индексируют указанные поля, строят индекс и по этому индексу быстро обрабатывают поисковые запросы. Указанные варианты легко настраиваются.
У PostgreSQL есть встроенный поиск, который вроде бы тоже позволяет такой поиск сделать.
Другое дело, что если записей так мало (300K), то это как из пушки по воробьям бить. Так что имеет смысл попробовать получить требуемое с помощью LIKE/SIMILAR TO.
Хотя можно на PostgreSQL перелезть, полезно будет в любом случае.
tema
постоялец
Сообщения: 376
Зарегистрирован: 24.03.2011 19:19:27

Сообщение tema »

WAYFARER писал(а):Добавлено спустя 3 минуты 59 секунд:

Код: Выделить всё

select * from table where LOWER(text) LIKE LOWER('%Тюль%') and LOWER(text) LIKE LOWER('%с%') and LOWER(text) LIKE LOWER('%цветами%') and LOWER(text) LIKE LOWER('%красная%') 

Таким запросом найдется именно "тюль с цветами красная", но не найдется просто "красная тюль" или "тюль с цветами".

Запрос составляется программно. И будет составлен по конкретному запросу с помощью цикла по всем словам.
Для других запросов составится по их словам:
WAYFARER писал(а):но не найдется просто "красная тюль" или "тюль с цветами".

Код: Выделить всё

select * from table where LOWER(text) LIKE LOWER('%тюль%') and LOWER(text) LIKE LOWER('%с%') and LOWER(text) LIKE LOWER('%цветами%') 

Код: Выделить всё

select * from table where LOWER(text) LIKE LOWER('%красная%') and  LOWER(text) LIKE LOWER('%тюль%')
Ответить