12.2. 表和索引

上一节中的例子说明使用简单常量字符串的全文匹配。本节显示如何搜索表中的数据,选择使用索引。

12.2.1. 搜索表

在不使用索引的情况下也是可以进行全文检索的,一个简单查询,显示出title从所有body字段中包含friend的每一行:

SELECT title
FROM pgweb
WHERE to_tsvector('english', body) @@ to_tsquery('english', 'friend');

这也将找到相关的词,比如friendsfriendly,因为所有的这些都降低到相同规范化的词。

以上查询指定english配置是用来解析和规范化字符串。或者我们可以省略配置参数:

SELECT title
FROM pgweb
WHERE to_tsvector(body) @@ to_tsquery('friend');

这个查询将通过default_text_search_config使用配置设置。

复杂一点的例子:检索出最近的10个文档,在title 或者body字段中包含createtable的:

SELECT title
FROM pgweb
WHERE to_tsvector(title || ' ' || body) @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC
LIMIT 10;

为了清楚,我们忽略coalesce函数调用,这需要找到在两个字段之一中包含NULL的行。

虽然这些查询在没有索引的情况下工作,大多数应用程序会发现这个方法太慢了,除了偶尔的特定搜索。 文本搜索的实际使用通常需要创建索引。

12.2.2. 创建索引

为了加速文本搜索,我们可以创建GIN索引(第 12.9 节):

CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector('english', body));

注意,使用to_tsvector的2-参数版本。唯一的文本搜索功能指定可用于表达式索引的配置名称(节第 11.7 节)。 这是因为索引的内容必须不受default_text_search_config的影响。如果他们受到影响,索引内容可能不一致, 因为不同的条目可能包含不同的文本搜索配置创建的tsvector,并且没有办法猜出是哪个。正确的转储和恢复 这样的一个索引是不可能的。

因为在上述索引中使用to_tsvector的2-参数版本,只有一个使用带有相同配置名称的to_tsvector的2-参数版本的查询参考, 它将使用该索引。也就是说,WHERE to_tsvector('english', body) @@ 'a & b' 可以使用索引,但WHERE to_tsvector(body) @@ 'a & b'不能。 这确保将使用一个索引仅仅伴随着用于创建索引的相同配置。

建立更复杂的表达式索引是可能的,配置名称由另一列指定,例如:

CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector(config_name, body));

pgweb表中config_name是一列。这允许在同一索引中混合配置,当记录的配置被用于每个索引条目。 这将是有用的,例如,如果文档集合中包含不同的语言文件。再次,意味着使用索引的查询必须措辞匹配, 例如,WHERE to_tsvector(config_name, body) @@ 'a & b'

索引甚至可以连接列:

CREATE INDEX pgweb_idx ON pgweb USING gin(to_tsvector('english', title || ' ' || body));

另一个方法是创建一个单独的tsvector列控制to_tsvector的输出。这个例子是titlebody的一个级联, 当其他是NULL的时候,使用coalesce确保一个字段仍然会被索引:

ALTER TABLE pgweb ADD COLUMN textsearchable_index_col tsvector;
UPDATE pgweb SET textsearchable_index_col =
     to_tsvector('english', coalesce(title,'') || ' ' || coalesce(body,''));

然后我们为加速搜索创建一个GIN索引:

CREATE INDEX textsearch_idx ON pgweb USING gin(textsearchable_index_col);

现在我们准备执行一个快速全文搜索:

SELECT title
FROM pgweb
WHERE textsearchable_index_col @@ to_tsquery('create & table')
ORDER BY last_mod_date DESC
LIMIT 10;

当使用一个单独的列存储tsvector形式时,有必要创建一个触发器以保持tsvector列当前任何时候title或者body的变化。 节第 12.4.3 节解释了如何做。

单独列方法比一个表达式索引的优势是它没有必要明确地声明为充分利用索引查询中的文本搜索配置, 正如上面例子所示,查询依赖于default_text_search_config。另一个优势是搜索比较快速, 因为它没有必要重新进行to_tsvector调用来验证索引匹配(当使用GIST索引而不是GIN索引的时候, 这是非常重要的,参见节第 12.9 节)。表达式索引方法更容易建立,然而,它需要较少的磁盘空间, 因为tsvector形式没有明确存储。