最近,我遇到了全文搜索的问题。我在搜索输入中使用此功能,后端会在您键入时发送可能匹配的提示。后端数据库是PostgreSQL。我需要根据搜索词在文本中的位置对提示进行排名。
因此,如果您搜索“星球大战”标题,您将首先看到“星球大战”帖子,而不是“星球大战 7-9 如何改变星球大战世界(一部关于星球大战的有趣制作的纪录片)”,这可能会该词出现 3 次后排名更高。
PostgreSQL 中的全文搜索可以很容易地实现。主要使用两个工具:
假设我们要搜索博客文章的标题。为了使它们可搜索,我们可以使用以下查询:
SELECT id, title FROM blogposts WHERE to_tsquery('JavaScript') @@ to_tsvector(posts.title);
在这种情况下,我们会在每次搜索时动态地将帖子标题转换为 tsvector。然而,这种转变需要一些时间。更好的方法是提前在数据库中执行此转换,并将其存储为标题的索引,以便更快地搜索。
让我们创建一个新的标题向量列,并为这个新列建立索引:
ALTER TABLE blogposts ADD COLUMN search_vector tsvector; UPDATE blogposts SET search_vector = (to_tsvector(posts.title)); CREATE INDEX titles_fts_idx ON blogposts USING gin(search_vector);
现在尝试搜索术语“JavaScript”
SELECT id, title FROM blogposts WHERE to_tsquery('JavaScript') @@ search_vector;
您还可以直接在标题列上从 ts 向量创建索引,如下所示:
CREATE INDEX titles_fts_idx ON blogposts USING GIN (to_tsvector(posts.title));
并使用这样的搜索:
SELECT id, title FROM blogposts WHERE to_tsquery('JavaScript') @@ posts.title;
现在,全文搜索将非常快,只需几毫秒即可完成。
PostgreSQL 提供了 ts_rank 功能,它允许您对搜索结果进行评分并根据排名对其进行排序。 PostgreSQL 支持以下排名选项:
您可以像这样使用 ts_rank:
SELECT ... ts_rank(search_vector, to_tsquery('JavaScript'), 0) as rank_title ... ORDER BY rank_title DESC NULLS LAST
但是,没有基于搜索词在字符串中的位置(即标题列)的内置排名选项。
幸运的是 PostgreSQL 中有 POSITION 函数。 PostgreSQL POSITION 函数用于查找给定字符串中子字符串的位置。在我们的例子中,我们可以像这样使用它
SELECT id, title FROM blogposts WHERE to_tsquery('JavaScript') @@ to_tsvector(posts.title);
ts_rank 使用归一化整数 2,因为 2 将排名除以文档长度
神奇数字 0.0001 是为了避免除以 0,因为 POSTION 函数从 1 开始计数,而不是从 0 开始计数,如果未找到字符串,则返回 0。
最终代码可能如下所示:
ALTER TABLE blogposts ADD COLUMN search_vector tsvector; UPDATE blogposts SET search_vector = (to_tsvector(posts.title)); CREATE INDEX titles_fts_idx ON blogposts USING gin(search_vector);
如果您一次搜索多个术语(例如 JavaScript 和 TypeScript),则必须提及一个警告。
to_tsquery 函数的参数可以非常灵活地使用,包括逻辑运算符等。另一方面,POSITION 函数“只是”字符串中的一个子字符串。
这是我在 SvelteKit Web 应用程序中来自现实世界端点的示例,该应用程序使用 postgres (sql) npm 库:
SELECT id, title FROM blogposts WHERE to_tsquery('JavaScript') @@ search_vector;
以下是相关文档的链接:
以上是PostgreSQL 全文搜索按位置排名的详细内容。更多信息请关注PHP中文网其他相关文章!