我目前正在尝试找出应如何处理应用程序中的搜索功能。为了显示我当前遇到的问题,我创建了一个虚构的数据库表。
+----+--------------+-----------+----------+---------------------------------------------------------------------------------+------------------------+
| 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 |
+----+--------------+-----------+----------+---------------------------------------------------------------------------------+------------------------+
这当然不是真实数据。
我知道我可以使用to_tsvector和to_tsquery在postgres中创建一种全文搜索。但是这种方法的问题是,我不能真正使用小值,例如用户名,名字或姓氏。如果我使用查询词leo
,postgres可能找不到ID为3的用户,因为搜索词和目标值太小。如果使用模糊匹配(例如levenshtein),则会出现问题,说明会出现问题。 Levenshtein在小价值方面表现出色。但是,在文本上效果并不理想。
我尝试将两种方法结合起来,但是我无法在tsvector和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;
不能保证此查询确实有效,因为我从应用程序中获取了该查询并对其进行了修改以适合示例数据。
解释我的尝试:
首先在描述字段上使用“ ts_rank_cd”创建一个等级值(实际上,在实际应用中,我需要在多列上进行文本搜索)。然后,我获得每个字段的levenshtein值,并使用最小值从“ ts_rank_cd”中减去它。这就是我试图平衡levenshtein与tsvector的方式。但是,当然,这种方法有很大的缺点。如果每个levenshtein列都与搜索查询完全不同,但是描述中包含我们要搜索的术语,则可能会由于levenshtein索引而使搜索到的行的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;
这是我该怎么做的另一个想法。但是仍然存在一些问题。