В настоящее время я пытаюсь выяснить, как мне следует обрабатывать функцию поиска в моем приложении.Чтобы показать, какая проблема у меня есть в настоящее время, я создал вымышленную таблицу базы данных.
+----+--------------+-----------+----------+---------------------------------------------------------------------------------+------------------------+
| id | username | firstname | lastname | description | email |
+====+==============+===========+==========+=================================================================================+========================+
| 1 | example.hans | Hans | Example | Some randome user called Hans. It doesn't really matter what the description is | example@test.com |
| 2 | root | Root | Root | Some other user | root@localhost |
| 3 | Shawn94 | Leo | Larson | Loves to play with firework | leo.larson@hotmail.com |
+----+--------------+-----------+----------+---------------------------------------------------------------------------------+------------------------+
Это, конечно, нет реальных данных.
Я знаю, что могу создать вид полнотекстового поискав postgres с to_tsvector и to_tsquery.Но проблема этого метода в том, что я не могу работать с небольшими значениями, такими как имя пользователя, имя или фамилия.Если бы я использовал термин запроса leo
, postgres, вероятно, не нашел бы пользователя с идентификатором 3, поскольку поисковый термин и целевое значение слишком малы.Проблема, если бы я использовал fuzzymatching (пример: levenshtein), у меня была бы проблема с описанием.Левенштейн прекрасно работает на небольших ценностях.Тем не менее, это не очень хорошо работает с текстом.
Я пытался объединить оба метода, но не смог уравновесить левенштейна с цветектором.
Вот пример запроса, как я пытался его сбалансировать:
SELECT "id" as "id",
(
(ts_rank_cd(to_tsvector("user"."description"), to_tsquery('Bee')))
-
(
(
SELECT MIN("LEVENSHTEIN_VALUES")
FROM unnest(
ARRAY [levenshtein("user"."fullName", 'Bee'), levenshtein("user"."username", 'Bee'), levenshtein("user"."email", 'Bee')]) AS "LEVENSHTEIN_VALUES"
)
)
) AS "FULLTEXT_RANK"
FROM "user" "user"
ORDER BY "FULLTEXT_RANK" ASC
LIMIT 10;
Нет гарантии, что этот запрос действительно работает, потому что я взял его из своего приложения и изменил, чтобы он соответствовал примеруdata.
Объяснение того, что я пытался:
Сначала создайте значение ранга с помощью ts_rank_cd в поле описания (на самом деле в реальном приложении мне потребуется текстовый поиск по нескольким столбцам).Затем я получаю значения Левенштейна для каждого поля и использую наименьшее значение, чтобы вычесть его из ts_rank_cd.Вот как я пытался уравновесить Левенштейна с Цвектором.Но, конечно, этот метод имеет огромный недостаток.Если каждый столбец Левенштейна полностью отличается от поискового запроса, но в описании есть искомый термин, возможно, что искомая строка все еще получает низкий рейтинг FULLTEXT_RANK из-за индекса Левенштейна.
Какможно ли объединить полнотекстовый поиск с нечетким сопоставлением?
Редактировать:
SELECT *,
(
SELECT SUM("TS_AND_SIM_RANK")
FROM unnest(ARRAY [
(SELECT MAX("TS_RANK")
FROM unnest(ARRAY [
ts_rank(to_tsvector('simple', "user"."description"), phraseto_tsquery('simple', ?))
]) AS "TS_RANK"),
(SELECT MAX("SIM_RANK")
FROM unnest(ARRAY [
similarity("user"."username", ?),
similarity("user"."firstname", ?),
similarity("user"."lastname", ?)
]) AS "SIM_RANK")
]) AS "TS_AND_SIM_RANK"
) as "RANK"
FROM "user"
ORDER BY "RANK" DESC;
Это еще одна идея, как я мог это сделать.Но есть еще некоторые проблемы.