如何在 Ubuntu 16.04 上使用 PostgreSQL 全文搜索

介绍

Full-text search (FTS) 是搜索引擎用来在数据库中找到结果的一种技术,可用于在商店,搜索引擎,报纸等网站上提供搜索结果。

更具体地说,FTS检索了包含文本数据的数据库实体 documents,这意味着当用户搜索猫和狗时,例如,由FTS支持的应用程序可以返回包含单独单词的结果(只是),包含单词的不同顺序(狗和猫),或包含单词的变体()的结果。

从技术上讲,像PostgreSQL这样的数据库管理系统(DBMS)通常允许使用LIKE条款进行部分文本搜索,但是,这些请求往往在大数据集上表现不佳。

使用FTS,您可以建立一个更强大的文本搜索引擎,而不需要在更先进的工具上引入额外的依赖性。 在本教程中,我们将使用PostgreSQL存储包含文章的数据,用于假设新闻网站,然后学习如何使用FTS查询数据库,并只选择最佳匹配。

前提条件

在您开始本指南之前,您将需要以下内容:

如果您没有按照上面的教程设置PostgreSQL服务器,请确保您使用sudo apt-get list postgresql-contribpostgresql-contrib包。

步骤 1 - 创建示例数据

首先,我们需要一些数据来测试全文本搜索插件,所以让我们创建一些示例数据. 如果您已经有自己的文本值表,您可以跳过步骤2并在跟进时做出适当的替代。

否则,第一步是从其服务器连接到PostgreSQL数据库,因为您正在从同一个主机连接,默认情况下,您不需要输入您的密码。

1sudo -u postgres psql sammy

这将建立一个交互式 PostgreSQL 会话,表示您正在操作的数据库名称,在我们的情况下是sammy

接下来,在数据库中创建一个名为新闻的示例表. 此表中的每个条目将代表一个新闻文章,其中包含一个标题,一些内容,以及作者的名字以及一个独特的标识符。

1CREATE TABLE news (
2   id SERIAL PRIMARY KEY,
3   title TEXT NOT NULL,
4   content TEXT NOT NULL,
5   author TEXT NOT NULL
6);

「id」是表的主要索引,具有特殊类型的「SERIAL」,为表创建了自动增量计数器. 这是一个独特的标识符,自动进入数据库索引。

接下来,使用INSERT命令将一些示例数据添加到表中,下面命令中的示例数据代表了一些示例新闻文章。

1INSERT INTO news (id, title, content, author) VALUES 
2    (1, 'Pacific Northwest high-speed rail line', 'Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.', 'Greg'),
3    (2, 'Hitting the beach was voted the best part of life in the region', 'Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.', 'Ethan'),
4    (3, 'Machine Learning from scratch', 'Bare bones implementations of some of the foundational models and algorithms.', 'Jo');

现在,由于数据库有一些数据可以搜索,我们可以尝试写一些查询。

步骤二:准备和查找文件

这里的第一个步骤是构建一个文档,从数据库表中使用多个文本列,然后,我们可以将结果的字符串转换为单词矢量,这就是我们在查询中使用的。

<$>[注] 注: 在本指南中,psql输出使用扩展显示格式,以便在新行上显示输出中的每个列,从而更容易在屏幕上插入长文本。

1\x
1[secondary_label Output]
2Expanded display is on.

美元

首先,我们需要使用 PostgreSQL 连接函数,然后将函数 转换为_tsvector()。

1SELECT title || '. ' || content as document, to_tsvector(title || '. ' || content) as metadata FROM news WHERE id = 1;

这将返回第一个记录作为一个完整的文档,以及用于搜索的转换版本。

1[secondary_label Output]
2-[ RECORD 1 ]-----------------------------------------------------
3document    | Pacific Northwest high-speed rail line. Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.
4
5metadata    | '140':18 'current':8 'high':4 'high-spe':3 'ideal':29 'line':7 'mile':19 'none':25 'northwest':2 'option':14 'pacif':1 'rail':6 'seattl':21 'speed':5 'travel':16 'vancouv':23

您可能会注意到,转换版本中的元数据在上面的输出中比原来的文档少一些单词。 有些单词是不同的,每个单词都有一个半圆形和一个附加的数字。 这是因为函数to_tsvector()将每个单词正常化,使我们能够找到相同单词的变形形式,然后按字母排序结果。 数字是单词在文档中的位置。 如果正常化的单词出现过多次,可能会有额外的单词分离位置。

现在我们可以通过搜索探索这个词来利用这个转换的文档来利用FTS的功能。

1SELECT * FROM news WHERE to_tsvector(title || '. ' || content) @@ to_tsquery('Explorations');

让我们来看看我们在这里使用的函数和操作员。

函数 to_tsquery() 将参数,这可能是直接或稍微调整的用户搜索,转换为文本搜索标准,这将减少输入的方式与 to_tsvector() 相同。

@@操作器识别tsvector是否匹配tsquery或其他tsvector,返回truefalse,使其易于使用作为WHERE标准的一部分。

1[secondary_label Output]
2-[ RECORD 1 ]-----------------------------------------------------
3id      | 2
4title   | Hitting the beach was voted the best part of life in the region
5content | Exploring tracks and trails was second most popular, followed by visiting the shops and then checking out local parks.
6author  | Ethan

该查询返回了包含探索一词的文档,尽管我们在搜索中使用的单词是探索

现在我们知道如何为 FTS 准备文件以及如何构建查询,让我们看看如何提高 FTS 的性能。

步骤三:提高FTS绩效

在使用大数据集或较小的服务器时,每次我们使用 FTS 查询时生成文档可能会成为性能问题。我们将在这里实施的一个很好的解决方案是在插入行时生成转换的文档,并将其与其他数据一起存储。

首先,为现有新闻表创建一个名为文档的额外列。

1ALTER TABLE news ADD "document" tsvector;

现在我们需要使用不同的查询将数据插入表中,而不是步骤2,在这里我们还需要准备转换的文档,并将其添加到新的文档列中,如下:

1INSERT INTO news (id, title, content, author, document)
2VALUES (4, 'Sleep deprivation curing depression', 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.', 'Patel', to_tsvector('Sleep deprivation curing depression' || '. ' || 'Clinicians have long known that there is a strong link between sleep, sunlight and mood.'));

将新列添加到现有表中需要我们首先为文档列添加空值,现在我们需要与生成的值进行更新。

使用更新命令添加缺失的数据。

1UPDATE news SET document = to_tsvector(title || '. ' || content) WHERE document IS NULL;

将这些行添加到我们的表中是一个很好的性能改进,但在大型数据集中,我们可能仍然有问题,因为数据库仍然需要扫描整个表以找到匹配搜索标准的行。

database index 是一个数据结构,将数据与主要数据分开存储,从而提高数据检索操作的性能。它在对表内容的任何更改后以额外的写法和相对较少的存储空间为代价更新。

最终,索引有助于数据库通过使用特殊数据结构和算法搜索更快地找到行。PostgreSQL有 几个类型的索引适合特定类型的查询。对于这个用例最相关的是GiST索引和GIN索引。它们之间的主要区别在于它们可以从表中获取文件的速度有多快。GIN在添加新数据时更慢地构建,但更快地查询;GIST构建更快,但需要额外的数据读取。

由于GIST比GIN的数据检索速度慢约3倍,我们将在这里创建一个GIN索引。

1CREATE INDEX idx_fts_search ON news USING gin(document);

使用索引的文档列,我们的SELECT查询也变得更加简单。

1SELECT title, content FROM news WHERE document @@ to_tsquery('Travel | Cure');

结果将是这样的:

1[secondary_label Output]
2-[ RECORD 1 ]-----------------------------------------------------
3title   | Sleep deprivation curing depression
4content | Clinicians have long known that there is a strong link between sleep, sunlight and mood.
5-[ RECORD 2 ]-----------------------------------------------------
6title   | Pacific Northwest high-speed rail line
7content | Currently there are only a few options for traveling the 140 miles between Seattle and Vancouver and none of them are ideal.

完成后,您可以使用\q退出数据库控制台。

结论

本指南涵盖了如何在 PostgreSQL 中使用全文本搜索,包括准备和存储元数据文档,以及使用索引来提高性能。

Published At
Categories with 技术
comments powered by Disqus