文档

使用文档

更新时间:

本文介绍pgsearch插件的功能、安装卸载、使用方法等,为您在实现强大的全文检索方面提供参考。

功能简介

pgsearch插件实现了全新的BM25(Best Matching 25)索引,该索引基于高性能全文检索引擎Tantivy构建。BM25是许多现代搜索引擎(如 Elasticsearch)的优先选择。它通过考虑一个词项在记录行中出现的频率和该词项在记录行中的独特性来对行进行排名。当您在记录行中检索关键词或短语时,BM25十分有用。BM25索引能够在云原生数据仓库 AnalyticDB PostgreSQL 版的表中进行全文检索并使用BM25算法进行相关性得分,从而匹配更准确的检索结果。

安装与卸载

pgsearch暂不支持白屏化安装,如有需要请提交工单联系工作人员协助安装(需要重启实例)。如有卸载插件需求,也请提交工单联系工作人员协助卸载。

测试表

为方便测试,本文提供了一个样例表,样例表中预先插入了几十条数据。执行pgsearch.create_test_table()会自动创建该样例表。

CALL pgsearch.create_test_table(table_name => 'mock_items', schema_name => 'public');

表结构如下所示

CREATE TABLE mock_items (
 description TEXT,
 rating INTEGER CHECK (
 rating BETWEEN 1
 AND 5
 ),
 category VARCHAR(255),
 in_stock BOOLEAN,
 metadata JSONB,
 created_at TIMESTAMP,
 last_updated_date DATE,
 latest_available_time TIME
);

索引管理

重要

创建索引和删除索引均不支持回滚。

创建索引

您可以使用pgsearch.create_bm25()函数创建BM25索引。该函数支持在多个字段上创建索引,并为每个字段指定相关的索引配置。创建多个索引会消耗过多的资源,从而影响使用体验。因此建议一张表只创建一个BM25索引。。

语法

CALL pgsearch.create_bm25(
  index_name => '<index_name>',
  table_name => '<table_name>',
  schema_name => '<schem_name>'
  text_fields => '<text_fields>',
  numeric_fields => '<numeric_fields>',
  boolean_fields => '<boolean_fields>',
  json_fields => '<json_fields>',
  datetime_fields => '<datetime_fields>'
)

参数说明

参数

是否必填

支持的字段类型

说明

index_name

String

索引名称。

table_name

String

需要创建索引的表的名称。

schema_name

String

表所在的Schema,默认为当前所在的Schema。

text_fields

至少设置一个。

VARCHAR、TEXT、VARCHAR[]、TEXT[]

定义哪些文本字段被索引及这些字段的索引方式。它接受一个JSON5格式的字符串,其结构为键值对。其中键对应数据库表中的字段名称,值则是一个配置对象。支持的配置如下。

  • fast:默认值为false。该字段可以被迅速随机访问,也适用于加速得分和过滤。

  • fieldnorms:默认值为true。存储文本字段长度的信息。必须为true才能计算BM25得分。

  • tokenizer:一个JSON5字符串,指定分词器及其配置选项。分词器相关配置请参见分词器

  • record:默认值为position。描述索引中包含的信息。record相关配置请参见Record

numeric_fields

INT2、INT4、INT8、OID、XID、FLOAT4、FLOAT8、NUMERIC

定义哪些数值字段被索引以及这些字段的索引方式。它接受一个JSON5格式的字符串,其结构为键值对。其中键对应数据库表中的字段名称,值则是一个配置对象。支持的配置如下。

fast:默认值为true。该字段可以被迅速随机访问,也适用于加速得分和过滤。

boolean_fields

BOOLEAN

定义哪些布尔字段被索引以及这些字段的索引方式。支持的配置如下。

fast:默认值为true。该字段可以被迅速随机访问,也适用于加速得分和过滤。

json_fields

JSON、JSONB

定义哪些JSON字段被索引以及这些字段的索引方式。其结构为键值对。其中键对应数据库表中的字段名称,值则是一个配置对象。一旦索引创建,就可以在JSON值内的嵌套文本字段上执行检索。支持的配置如下。

  • fast:默认值为false。该字段可以被迅速随机访问,也适用于加速得分和过滤。

  • expand_dots:默认值为true。如果为true,则JSON键将通过.被展平。例如,当expand_dots为true,则{"metadata.color": "red"}将匹配到{"metadata": {"color": "red"}}。

  • tokenizer:en_stem,指定分词器及其配置选项。分词器相关配置请参见分词器

  • record:默认值为position。描述索引中包含的信息。record相关配置请参见Record

datetime_fields

DATE、TIMESTAMP、TIMESTAMPTZ、TIME和TIMETZ

定义哪些日期时间字段被索引以及这些字段的索引方式。如果未指定,检索词将使用UTC时区,并且采用RFC3339格式。支持的配置如下。

fast:默认值为true。该字段可以被迅速随机访问,也适用于加速得分和过滤。

Record

pgsearch目前支持三种Record。

  • raw:不做分词处理。

  • freq:记录行ID以及词项频率。

  • position:记录行ID、词项频率和出现位置。

分词器

pgsearch支持多种分词器,详情如下表。

分词器名称

描述

配置

default

根据空格和标点符号进行文本分词,并转换为小写。过滤掉大于255字节的词项。

{type: "default"}

raw

不做分词处理。

{type: "raw"}

en_stem

根据空格和标点符号进行文本分词,并转换为小写,最后再提取每个词的词干。过滤掉大于40个字符的词项。

{type: "en_stem"}

whitespace

根据空格分词。

{type: "whitespace"}

ngram

通过根据指定参数将词分词成重叠的子字符串进行文本分词。

  • min_gram:定义n-gram的最小长度。例如,设置为2,生成的最小词项长度将是2个字符。

  • max_gram:定义n-gram的最大长度。例如,设置为5,生成的最大词项长度将是5个字符。

  • prefix_only: 如果设置为true,分词器仅生成从词头开始的n-gram,确保了前缀的递进。如果为false,n-gram将从min_gram和max_gram范围内的所有可能字符组合中生成。

{type: "ngram", min_gram: 1, max_gram: 2, prefix_only: true}

chinese_compatible

考虑中文字符的细微差别而分词文本,基于空格和标点符号分词。对于连续的非中文字符,会生成一个token;对于单个中文字符,会生成单独一个token;对于标点符号等非数字或者字母的字符,会忽略掉不生成token。例如,对于字符串“我爱吃橙子 oranges!12”,会生成"我,爱,吃,橙,子,oranges,12"这7个token。

{type: "chinese_compatible"}

chinese_lindera

使用Lindera分词器分词文本,它使用CC-CEDICT词典来分段和分词文本。

{type: "chinese_lindera"}

korean_lindera

使用Lindera分词器分词文本,它使用KoDic词典来分段和分词文本。

{type: "korean_lindera"}

japanese_lindera

使用Lindera分词器分词文本,它使用IPADIC词典来分段和分词文本。

{type: "japanese_lindera"}

jieba

使用jieba分词器进行分词,适用于大多数中文文本。 更多详情请参见pg_jieba

{type: "jieba"}

创建索引示例

-- 使用ngram分词器,注意,ngram的所有配置字段都必须显示给出
CALL pgsearch.create_bm25(
    index_name => 'search_idx',
    table_name => 'mock_items',
    text_fields => '{description: { tokenizer: {type: "ngram", min_gram: 2, max_gram: 3, prefix_only: false}}}'
);

-- 使用jieba分词器
CALL pgsearch.create_bm25(
    index_name => 'search_idx',
    table_name => 'mock_items',
    text_fields => '{description: { tokenizer: {type: "jieba"}}}'
);

-- 使用lindera分词器
CALL pgsearch.create_bm25(
    index_name => 'search_idx',
    table_name => 'mock_items',
    text_fields => '{description: { tokenizer: {type: "chinese_lindera"}}}'
);
--在多字段上创建索引
CALL pgsearch.create_bm25(
    index_name => 'search_idx',
    table_name => 'mock_items',
    text_fields => '{description: {fast: false, filednorms: true, tokenizer: {type: "jieba"}}, category: {}}',
    datetime_fields => '{created_at: {fast: true}, last_updated_date: {fast: true}}',
    numeric_fields => '{rating: {fast: true}}',
    json_fields => '{metadata: {fast: true, expand_dost: true, tokenizer: {type: "en_stem"}, record: "position"}}',
    boolean_fields => '{in_stock: {fast: true} }'
);

--大部分场景下,只需要对text_fileds以及json_fields中的字段按需配置tokenizer,剩余参数通常采用默认值即可。即:
CALL pgsearch.create_bm25(
    index_name => 'search_idx',
    table_name => 'mock_items',
    text_fields => '{description: {tokenizer: {type: "jieba"}}, category: {}}',
    datetime_fields => '{created_at: {}, last_updated_date: {}}',
    numeric_fields => '{rating: {}}',
    json_fields => '{metadata: {tokenizer: {type: "en_stem"}}}',
    boolean_fields => '{in_stock: {} }'
);

查看索引配置

SELECT * FROM pgsearch.schema_bm25('index_name'::regclass);

删除索引

DROP INDEX index_name;

重建索引

REINDEX INDEX index_name;

查询语法

@@@排序操作符

@@@排序操作符主要用于KNN检索,即需要将检索结果按照BM25得分排序,然后按需获取TOP结果。

语法

SELECT * FROM <table_name> 
ORDER BY <index_col> @@@ pgsearch.config('<query>');

参数说明

  • <table_name>:表的名称。

  • <index_col>可以是创建索引时指定的任意一个索引字段。建议使用在检索关键词query中出现的第一个索引字段

  • <query>:检索的关键词。

  • pgsearch.config:不仅接受检索字符串外,还接受其他检索对象。检索对象是可以组合的,允许任意粒度的检索。实际上,当传递检索关键词给pgsearch.config时,隐式地利用pgsearch.parse将检索关键词解析为检索对象。下面示例中两个查询是相同的效果。

  • SELECT * FROM mock_items 
    ORDER BY description @@@ pgsearch.config('description:socks');
    
    SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
      query => pgsearch.parse('description:socks')
    );

@@操作符

  • 对于非字符串类型的BM25得分是没有意义的。如果您不希望将结果按照BM25得分排序,可以使用@@操作符。@@操作符的参数与@@@类似,左边参数是表名,右边参数是pgsearch.config函数封装的查询条件。不同的是,@@操作符用在WHERE子句里。@@操作符不仅支持indexscan,同时也支持BitmapIndexScan。因此,在获取大量结果时,性能比@@@操作符更好。如果您不需要将结果按照BM25得分排序,同时需要获取大量结果,可以使用@@操作符以获得更好的性能体验。

  • 语法

  • SELECT * FROM <table_name> 
    WHERE <index_col> @@ pgsearch.config('<query>');
  • 参数说明

  • 参数说明请参见参数说明

基础查询

指定字段检索

文本字段检索

以下查询将检索含“keyboard”的行。

SELECT * FROM mock_items 
ORDER BY description @@@ pgsearch.config('description:keyboard');

JSON字段检索

以下查询将检索含{"metadata": {"color": "white"}}的行。

SELECT * FROM mock_items 
ORDER BY metadata @@@ pgsearch.config('metadata.color:white');

DATETIME字段检索

在DATETIME字段上检索时,默认采用UTC时区和RFC3339格式。

SELECT * FROM mock_items ORDER BY created_at @@@ pgsearch.config('created_at:"2023-05-01T09:12:34Z"') LIMIT 10;

SELECT * FROM mock_items ORDER BY created_at @@@ pgsearch.config('created_at:"2023-05-01T04:12:34-05:00"') LIMIT 10;

邻近性操作符检索

slop~运算符用于匹配中间有单词分隔的短语。例如,description字段存在“ergonomic metal keyboard”,由于单词“ergonomic”和“keyboard”之间有一个单词分隔,因此以下查询将找到“ergonomic metal keyboard”。

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config('description:"ergonomic keyboard"~1');

高效过滤检索

过滤器只适用于已创建BM25索引的数值字段(numeric_fields)和布尔字段(boolean_fields)。与标准SQL语句中WHERE子句相比,过滤检索可以缩短检索时间。

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config('description:keyboard AND rating:<4');

提升排名检索

如果您希望提升检索关键词的结果排名,可以使用^字符加提升系数实现。该系数作用于关键词匹配的结果,提高BM25得分,从而提升这些匹配行在结果中的排名。

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config('description:keyboard^2 OR category:electronics^3');

布尔运算符检索

ANDORNOT可用于组合并筛选多个关键词。括号可用于分组和控制运算顺序。

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config('description:keyboard OR category:toy');

集合运算符检索

集合运算符使用一个或者多个OR。优点:更节省CPU资源。

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config('description:IN [keyboard, toy]');

限制和偏移

支持OFFSETLIMIT语句。

SELECT * FROM mock_items
ORDER BY description @@@ pgsearch.config('description:socks') OFFSET 2 LIMIT 10;

高级查询

词集合检索(term_set)

检索包含任意一个关键词的行。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query =>  pgsearch.term_set(
	    terms => ARRAY[
	        pgsearch.term(field => 'description', VALUE => 'socks'),
	        pgsearch.term(field => 'description', VALUE => 'novel')
            
	    ]
	)
);

参数说明

  • field:指定用于检索的字段。如果省略,将检索所有索引字段。

  • terms:检索对象ARRAY。

短语检索(phrase)

检索包含所有关键词且符合关键词顺序的行。短语检索需要索引的record配置为position。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.phrase(
    field => 'description',
    phrases => ARRAY['little', 'red', 'riding' 'hood'],
        slop => 0
    )
);

参数说明

  • field:指定用于检索的字段。如果省略,将检索所有索引字段。

  • phrases:关键词数组。要求文本包含数组中的所有关键词,且这些词在文本中出现的顺序与数组中关键词的顺序一致。只有词语匹配到全部关键词,且词语的顺序与关键词的顺序一致时,才会返回该行。

  • slop:可选参数,表示各个关键词之间的最大距离。slop值为0,表示词语在文本中出现的顺序与关键词的顺序一致,并且相邻。例如,'little' 'red' 'riding' 'hood'按顺序连续出现,才算满足匹配条件。如果slop的值大于0,表示允许关键词之间可以有一定的间隔,也就是说'little' 'red' 'riding' 'hood'中间可以有其他词。

短语前缀检索(phrase_prefix)

检索包含指定关键词且符合关键词顺序(包含检索词前缀功能)的行。短语前缀检索需要对被检索字段建立索引。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.phrase_prefix(
    field => 'description',
    phrases => ARRAY['little', 'red', 'riding' 'hood'],
        max_expansion => 1
    )
);

参数说明

  • field:指定用于检索的字段。如果省略,将检索所有索引字段。

  • phrases:关键词数组。query表示检索的关键词序列。数组里面的关键词序列作为前缀,可匹配到指定检索关键词后有任意字符的字符串(须顺序一致)。例如:ARRAY['little', 're'] ,前缀为“little re”,因此可以匹配到“little red riding hood”。

  • max_expansion:可选参数,限制前缀在检索过程中可以扩展的关键词变体的数量。即该参数设置限制了关键词匹配的变体数量的上限,缩小了检索的范围。

全部检索(all)

此检索类型无区别地匹配索引字段中的每个结果行,并为每一行分配一个统一的分数1.0。即每一行的分数一样,检索结果也不分优先级。

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query =>  pgsearch.all()
);

布尔检索(boolean)

布尔检索根据子查询定义的逻辑关系过滤匹配结果行。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.boolean(
	    should => ARRAY[
		    pgsearch.parse('description:socks'),
		    pgsearch.phrase_prefix(field => 'description', phrases => ARRAY['book']),
		    pgsearch.term(field => 'description', VALUE => 'writer'),
		    pgsearch.fuzzy_term(field => 'description', VALUE => 'wow')
	    ],
        must_not => ARRAY[
            pgsearch.term(field => 'description', VALUE => 'writer')
        ],
        must => ARRAY[
            pgsearch.term(field => 'rating', VALUE => 4)
        ]
    )
);
-- 可以只配置should、must_not、must中的一个或几个
SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.boolean(
	    should => pgsearch.parse('description:socks')
    )
);

参数说明

  • must:一组ARRAY检索对象作为必须匹配的条件。只有满足所有must的行才会出现在检索结果中。

  • must_not:一组ARRAY检索对象作为不得匹配的条件。满足任意must_not条件的行会被排除在检索结果中。

  • should:一组ARRAY检索对象作为条件,当must不存在时,其中至少有一个必须匹配。

  • must不存在时,满足至少一个should条件的行会出现在检索结果中。

提升排名检索(boost)

提升查询作用于子查询(即以下示例中query => pgsearch.parse('description:socks'))以放大其得分影响,从而提升子查询的结果排名。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.boost(query => pgsearch.parse('description:socks'), boost => 2)
);

参数说明

  • boost:与每个结果的得分相乘的因子。

  • query:检索对象。

恒等分数检索(const_score)

在与子查询(即以下示例中的query => pgsearch.all())匹配的所有行中应用常量分数。它可以避免在子查询上进行不必要的分数计算。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.const_score(query => pgsearch.all(), score => 2)
);

参数说明

  • score:用于子查询的每个结果的恒定得分。

  • query:检索对象。

最大析取检索(disjunction_max)

最大析取检索返回与一个或多个指定子查询匹配的行。如果某行符合的匹配条件越多,则该行的得分越高。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.disjunction_max(
      disjuncts => ARRAY[
          pgsearch.parse('description:socks'),
          pgsearch.parse('description:Generic')
      ],
      tie_breaker => 0.75
    )
);

参数说明

  • disjuncts: 子查询对象数组,可以包含一个或多个子查询。

  • tie_breaker:可选配置,用于某行匹配多个子查询时,调整该行的BM25得分。当某行同时满足多个子查询时,该行的BM25得分计算规则:首先计算所有匹配子查询中最高的BM25得分,然后对于每个额外匹配的子查询,会计算递增分数,最后计算BM25总分。在上面的例子中,假设有一行与子查询pgsearch.parse('description:socks')的BM25得分为1.0,与子查询pgsearch.parse('description:Generic')的BM25得分为0.5,那么该行最终的BM25得分为1.0+0.75*0.5=1.375。

空匹配(empty)

空匹配用作占位符,不匹配任何行。它可用于测试场景或特定边缘情况。

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.empty()
);

模糊检索(fuzzy_term)

模糊检索可让用户获得与检索关键词大致匹配的检索结果,能够容忍细微的拼写错误。即使拼写不完全正确,也能检索到相关结果。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.fuzzy_term(
      field => 'description', 
      VALUE => 'wow',
      distance => 2,
      tranposition_cost_one => true,
      prefix => true)
);

参数说明

  • field:指定用于检索的字段。如果省略,将检索所有索引字段。

  • value:指定检索的关键词,使用基于Levenshtein距离的模糊匹配来检索与该关键词相似的结果。

  • distance:可选配置,默认值为2。将索引字段中的词语视为检索关键词匹配的允许最大编辑距离(即单字符编辑),最大值为2。

  • tranposition_cost_one:可选配置,默认值为true。当设置为true,在Levenshtein距离计算中,交换(交换两个相邻的字符)被认为是单个编辑。当设置为false,其被认为是两个单独的编辑(删除和插入)。

  • prefix:可选配置,默认值为true。当设置为true,检索关键词的前缀不参与模糊编辑距离计算。设置为false时,则将整个字符串用于计算。

范围检索(range)

检索包含属于指定值范围的行,可以用在参数numeric_fields和datetime_fields。

使用示例

SELECT * FROM mock_items ORDER BY rating @@@ pgsearch.config(
    query => pgsearch.range(
        field => 'rating',
        RANGE => '[1,4)'::int4range
  )
);

参数说明

  • field:指定用于检索的字段。如果省略,将检索所有索引字段。

  • range:指定与字段匹配的值范围。范围支持的类型包括INT4RANGE、INT8RANGE、DATERANGE、TSRANGE和TSTZRANGE。

正则表达式检索(regex)

如果某行中的词语能够匹配指定的正则表达式,则返回该行。

使用示例

SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.regex(
		field => 'description',
		PATTERN => '(glass|screen|like|cloth|phone)'
	)
);
SELECT * FROM mock_items ORDER BY description @@@ pgsearch.config(
    query => pgsearch.regex(
		field => 'description',
		PATTERN => '(.*screen.*)'
	)
);

参数说明

  • field:指定用于检索的字段。如果省略,将检索所有索引字段。

  • pattern:正则表达式模式字符串。

其他

获取分词结果

  • 通过pgsearch.tokenizer函数获取分词结果。函数返回一个数组,数组元素是分词后的关键词。

    语法

    SELECT pgsearch.tokenizer(<tokenizer_config>, <query_str>);

    参数说明

    • tokenizer_config:包含分词器配置信息的JSON字符串。具体请参见分词器

    • query_str:需要被分词的字符串。

    使用示例

    SELECT pgsearch.tokenizer('{type: "ngram", min_gram: 1, max_gram: 2, prefix_only: true}', 'hell');
    SELECT pgsearch.tokenizer('{type: "jieba"}', '数据仓库');
  • 通过pgsearch.tokenizer函数和布尔检索来检索指定字段上包含任意一个分词后的token的结果。为了方便使用,提前定义如下函数。

    -- 该函数用于在 `mock_items` 表中检索指定字段包含任意从输入字符串通过给定的分词器配置派生的 token 的记录。
    -- 参数:
    --  search_col: 指定在`mock_items` 表上的search_col字段上检索
    --  tokenizer_config: 用于对输入字符串进行分词的 JSON 配置。
    --  input_string: 需要进行分词的字符串。
    -- 返回:
    --  一个包含符合检索条件的结果的表, 包含 (id, description) 字段。
    CREATE OR REPLACE FUNCTION find_mock_items(search_col text, tokenizer_config text, input_string text)
    RETURNS TABLE(id int, description text) AS $$
    DECLARE
        tokens text[];
        should_conditions text;
    BEGIN
        -- Step 1: Get tokens using pgsearch.tokenizer
        tokens := pgsearch.tokenizer(tokenizer_config, input_string);
        
        -- Step 2: Construct `should` conditions as text
        SELECT string_agg(
            format($f$pgsearch.parse('%I:%s')$f$, search_col, token),
            ', '
        ) INTO should_conditions
        FROM unnest(tokens) AS token;
    
        -- Step 3: Construct the full query as text
        RETURN QUERY EXECUTE format(
            'SELECT id, description FROM mock_items ORDER BY %s @@@ pgsearch.config(
                query => pgsearch.boolean(
                    should => ARRAY[%s]
            )) LIMIT 10',
            search_col,
            should_conditions
        );
    END;
    $$ LANGUAGE plpgsql;

    使用时,需要指定在哪一个字段上检索、分词器信息、检索字符串。值得注意的是,配置的分词器信息必须和构建索引时,对应字段上的分词器配置信息一致,否则无法得到期望的结果。您也可以按需修改find_mock_items函数,来满足不同的需求。

    SELECT * FROM find_mock_items(
        'description',
        '{type: "jieba"}', 
        '数据仓库'
    );

    查询结果

     tokenizer 
    -----------
     {h,he}
    (1 row)
    
      tokenizer  
    -------------
     {数据,仓库}
    (1 row)

获取BM25得分

将@@@操作符前置,并使用AS语法获取BM25得分。

因为云原生数据仓库 AnalyticDB PostgreSQL 版数据库中默认的ORDER BY是升序,而BM25得分是越大越好,因此,返回的实际上是BM25得分的负值。例如,BM25得分为2.86,那么返回的结果为-2.86。除此之外,只有在text_fields和json_fields上的检索才会计算BM25得分,对于numeric_fields、datetime_fields和boolean_fields类型的检索,是无法计算BM25得分的。

语法

SELECT *, mock_items @@@ pgsearch.config(<query>) as BM25
FROM mock_items
ORDER BY BM25;

使用示例

-- 1.只在字符串类型的字段上进行检索,也就是最常见的全文检索,可以返回BM25得分
SELECT description, rating, description @@@ pgsearch.config('description:socks') AS bm25 FROM mock_items ORDER BY bm25 limit 1;

-- 2.在字符串类型的字段上进行全文检索,同时基于数值类型的字段进行过滤,同样可以返回BM25得分
SELECT description, rating, description @@@ pgsearch.config('description:socks AND rating:4') AS bm25 FROM mock_items ORDER BY bm25 limit 4;
 
-- 3.在json字段上进行全文检索,同样可以返回BM25得分
SELECT metadata, metadata @@@ pgsearch.config('metadata.color:White') AS bm25 FROM mock_items ORDER BY bm25 LIMIT 1;

-- 4.在rank字段上进行范围过滤,无法计算BM25得分,返回默认值-1
 SELECT description, rating, rating @@@ pgsearch.config('rating:[4 TO 5]') AS bm25 FROM mock_items ORDER BY bm25 LIMIT 1;

查询结果

 -- 1.只在字符串类型的字段上进行检索,也就是最常见的全文检索,可以返回BM25得分
   description  | rating |    bm25    
---------------+--------+------------
 Generic socks |      4 | -2.1048825
(1 row)

-- 2.在字符串类型的字段上进行全文检索,同时基于数值类型的字段进行过滤,同样可以返回BM25得分
 description  | rating |    bm25    
---------------+--------+------------
 Generic socks |      4 | -3.1081846
(1 row)

-- 3.在json字段上进行全文检索,同样可以返回BM25得分
                metadata                 |   bm25    
-----------------------------------------+-----------
 {"color": "White", "location": "China"} | -3.453373
(1 row)

-- 4.在rank字段上进行范围过滤,无法计算BM25得分,返回默认值-1
  description    | rating | bm25 
------------------+--------+------
 Plastic Keyboard |      4 |   -1
(1 row)

@@操作符使用示例

示例1

-- 查询有多少文档包含“shoes”
SELECT count() FROM mock_items WHERE description @@ pgsearch.config('description:socks');

-- 返回所有包含"shoes"的文档
SELECT * FROM mock_items WHERE description @@ pgsearch.config('description:socks');

@@操作符支持前文描述的所有基础查询和高级查询语法,例如布尔检索、正则表达式检索等。

示例2

SELECT * FROM mock_items WHERE description @@ pgsearch.config(
    query => pgsearch.boolean(
	    should => ARRAY[
		    pgsearch.parse('description:socks'),
		    pgsearch.phrase_prefix(field => 'description', phrases => ARRAY['book']),
		    pgsearch.term(field => 'description', VALUE => 'writer'),
		    pgsearch.fuzzy_term(field => 'description', VALUE => 'wow')
	    ],
        must_not => ARRAY[
            pgsearch.term(field => 'description', VALUE => 'writer')
        ],
        must => ARRAY[
            pgsearch.term(field => 'rating', VALUE => 4)
        ]
    )
);