全文检索索引

BM25(Best Matching 25)是一种广泛应用于信息检索领域的概率相关性模型,用于衡量查询与文档之间的匹配程度。通常使用场景为根据用户输入的查询内容,检索出与查询内容最相关的 top-k 文档,BM25 为查询和文档的相关性分数计算方式。其核心思想是结合以下几个因素来计算文档的相关性得分:

  • 词频(Term Frequency,TF):词语在文档中出现的次数,反映了词的重要性,但单纯依赖词频可能会使长文档得分偏高。
  • 逆文档频率(Inverse Document Frequency,IDF):反映了词语在整个文档集合中的稀有程度,常见词(如“的”、“是”)的 IDF 较低,而罕见词的 IDF 较高。
  • 文档长度归一化:通过考虑文档长度及平均文档长度,平衡长文档与短文档之间的影响。

VexDB 支持基于 BM25 算法的全文检索索引 fulltext。

在进行全文检索时,您可以选择使用系统默认词典,或者通过下面介绍的方式创建自定义分词词典。

词典语法介绍

分词词典创建

CREATE TEXT SEARCH DICTIONARY <dict_name> (
TEMPLATE = vex_jieba
[, option = value [, ... ]]
);

创建分词词典时,只能指定模板为 vex_jieba 分词模板。该分词模板支持中英文混合分词与处理。创建语句如下:

CREATE TEXT SEARCH DICTIONARY test_dict (TEMPLATE = vex_jieba);
说明
  • PG_TS_DICT 系统表包含定义文本检索词典的项;PG_TS_CONTENT 系统表用于记录每个自定义分词词典中包含的具体词条内容,分为停用词和用户词。
  • 内置的字典模版vex_jieba不支持tsvector或其他内核功能,仅限fulltext索引内部使用。

当前,创建分词词典的语句支持以下两个自选参数:

  • stopwords:停用词词典,指定哪些词或符号在分词时会被自动过滤掉(常用于排除“的”、“是”、“and”等无实际含义的词)。
    配置值含义说明
    未指定(默认)使用系统内置的默认停用词词典。
    empty创建一个空停用词词典,用户可后续通过 SQL 添加。
    <dict_name>使用已有分词词典 <dict_name> 中的停用词配置作为当前词典的停用词。
  • userdict:用户自定义词典,用于指定自定义关键词,这些词在分词结果中将原样保留,不会被拆分。
    配置值含义说明
    未指定(默认)使用系统默认用户词典。
    empty创建一个空白词典,支持后续添加关键词。
    <dict_name>继承已有分词词典 <dict_name> 的用户词典配置。

词典修改

  • 插入停用词
    使用函数 vexjieba_add_stopwords(dict_name, words[]) 向词典添加停用词:
    select vexjieba_add_stopwords(
        <dict_name_or_oid>,
        array[stopword1, stopword2, ...]);
    

    系统会自动去重,重复插入不会影响效果。
  • 清空停用词
    使用函数 vexjieba_delete_stopwords(dict_name) 将该词典中所有停用词一次性清除:
    SELECT vexjieba_delete_stopwords('my_dict');
    
  • 插入自定义关键词
    使用函数 vexjieba_add_userdict(dict_name, words[]) 插入自定义关键词,支持指定词频:
    select vexjieba_add_userdict(
        <dict_name_or_oid>,
        array[keyword1, keyword2, ...]);
    
    keyword格式说明
    关键词如不指定词频,仅插入词本身,如 '苹果'
    关键词+词频'苹果,10000'

    默认词频为 10000,高于大多数系统词条,保证可切分。
  • 清空用户自定义词典
    使用函数 vexjieba_delete_userdict(dict_name) 清除该词典中所有自定义关键词:
    select vexjieba_delete_userdict(<dict_name_or_oid>);
    

词典重加载

修改词典内容后,必须执行重加载操作以使新词生效。

select vexjieba_reload(<dict_name_or_oid>);

词典删除

删除全文检索词典。

DROP TEXT SEARCH DICTIONARY [ IF EXISTS ] name [ CASCADE | RESTRICT ]

词典使用示例

  1. 使用默认分词词典 cn_tokenizer。
    select bm25_tokenize('数智引航正在研发不仅高并发又高性能还高可用的VexDB向量数据库');
    

    分词结果:{数智,引航,正在,研发,高,并发,高性能,高,可用,vexdb,向量,数据库}
  2. 创建一个分词词典 cn_dict,停用词词典为空,用户词典为空。
    CREATE TEXT SEARCH DICTIONARY cn_dict(
        template = pg_catalog.vex_jieba,
        stopwords = empty,
        userdict = empty);
    
  3. 插入自定义停用词。
    select vexjieba_add_stopwords(
        'cn_dict',
        array['不仅', '又', '还', '的']);
    
  4. 插入自定义关键词。
    SELECT vexjieba_add_userdict(
        'cn_dict',
        ARRAY['数智引航,10000,nt', 'VexDB向量数据库,nz', '高并发,10000', '高可用', '高性能']);
    
  5. 重新加载词典 cn_dict 并使用。
    select vexjieba_reload('cn_dict');
    select bm25_tokenize('数智引航正在研发不仅高并发又高性能还高可用的VexDB向量数据库', 'cn_dict');
    

    分词结果: {数智引航,正,在,研发,高并发,高性能,高可用,vexdb向量数据库}
  6. 清空停用词。
    SELECT vexjieba_delete_stopwords('cn_dict');
    
  7. 重新加载词典cn_dict并使用。
    select vexjieba_reload('cn_dict');
    select bm25_tokenize('数智引航正在研发不仅高并发又高性能还高可用的VexDB向量数据库', 'cn_dict');
    

    分词结果为:{数智引航,正,在,研发,不仅,高并发,又,高性能,还,高可用,的,vexdb向量数据库}
  8. 清空用户自定义词典。
    select vexjieba_delete_userdict('cn_dict');
    
  9. 重新加载词典 cn_dict 并使用。
    select vexjieba_reload('cn_dict');
    select bm25_tokenize('数智引航正在研发不仅高并发又高性能还高可用的VexDB向量数据库', 'cn_dict');
    

    分词结果为:{数智引,航正,在,研发,不仅,高,并,发,又,高性,能,还,高,可用,的,vexdb,向量,数据库}
  10. 删除词典。
    DROP TEXT SEARCH DICTIONARY public.cn_dict CASCADE;
    

索引参数

索引构建参数

参数名称 取值说明 参数描述
DICTS 取值范围:cn_tokenizer
默认值:cn_tokenizer
用于指定文本类型数据解析词典,将查询内容或者文档文本转化为分隔开的单词。在多列属性场景下通过#对各列单独指定词典,顺序同索引列声明顺序,使用 cn_tokenizer 表示系统默认词典。
如果字段是文本数组, 就不会进行分词。
ALGORITHMS 取值范围:BM25/TF_IDF/
LOG_TF_IDF
默认值:BM25
用于指定文档-查询相似分数计算方式。
在多列属性场景下通过#对各列单独指定算法。
parallel_workers 取值范围:0~64
默认值:0
并行构建参数,构建索引并行计算线程数。
COEFFICIENTS 参数 b
取值范围:[0,1]
默认值:0.75
用于指定文档-查询相似分数算法所使用的参数,设置格式"<参数>=<数值>",不同参数通过:隔开,不允许包含空格,未指定的参数自动使用默认值,在多列属性场景下通过#对各列单独指定。目前参数包括b(默认值0.75),k(默认值1.2),只有 BM25算法会使用到这些参数。
参数 k
取值范围:[1,2]
默认值:1.2

使用建议

  • 注意关键词可以包含空格,但左右边的空格会被忽略,比如 ' a b c ' 等效于 'a b c'。另外关键词搜索使用完全匹配,比如包含关键词'数据库'的文档只能通过'数据库'关键词关联,而'数'和'数据'等词不能匹配到'数据库',如关键词 @-@ 查询未匹配到预期结果可以使用 bm25_tokenize(text, dictionary) 函数校验文档分词内容。
  • 多列匹配查询,只能用 AND 连接。对于需要表示 OR 逻辑的查询可以将算子嵌入到查询关键词或者使用常规 union 等优化实现。

全文检索使用示例

  1. 创建测试表并插入测试数据。
    DROP TABLE IF EXISTS comments;
    CREATE TABLE comments (
    id serial PRIMARY KEY,
    document text,
    title text
    );
    
    INSERT INTO comments (document, title) VALUES
    ('The quick brown fox jumps over the lazy dog', 'Animal story'),
    ('An agile fox was seen near the river bank', 'Wildlife report'),
    ('Dogs are loyal animals and good companions', 'Pet life'),
    ('Foxes are known for their cunning behavior', 'Wild instincts'),
    ('The quick blue hare outpaced the lazy dog', 'Animal race'),
    ('Dogs and foxes can both be found in Europe', 'Ecosystem overview');
    
  2. 默认使用系统分词器 cn_tokenizer,document 使用 cn_tokenizer 和 BM25 算法,参数为 b=0.75 和 k=1.2;title 使用 cn_tokenizer 和 TF_IDF 算法,无需参数。
    CREATE INDEX bm25_idx_comments
    ON comments USING fulltext(document, title)
    WITH (
    DICTS=cn_tokenizer#cn_tokenizer,
    ALGORITHMS=BM25#TF_IDF,
    COEFFICIENTS='b=0.75:k=1.2'
    );
    
  3. 关键词匹配查询。
    -- 以下查询只能通过BM25索引执行,关键词包含使用@-@操作符,BM25评分使用@~@操作符。
    -- 1. 单关键词命中,查询document包含“fox”关键词的记录
    SELECT * FROM comments WHERE document @-@ 'fox';
    -- 2. 多关键词AND查询,查询document包含“fox”和“dog”关键词的记录
    SELECT * FROM comments WHERE document @-@ 'fox AND dog';
    -- 3. 多关键词OR查询,查询document包含“fox”或者“dog”关键词的记录
    SELECT * FROM comments WHERE document @-@ 'fox OR dog';
    -- 4. 复杂布尔逻辑查询,查询document包含“fox” 或者 “dog”和“quick”同时存在的关键词的记录
    SELECT * FROM comments WHERE document @-@ 'fox OR (dog AND quick)';
    -- 5. 多列匹配查询(必须用AND连接),查询document包含“fox” 和 title包含“Animal”的记录
    SELECT * FROM comments WHERE document @-@ 'fox' AND title @-@ 'Animal';
    
  4. BM25 Top-k 排序查询。
    • 文本格式查询,查找与搜索词最相关文档,并按 BM25 算法打分排序,返回前3。
      SELECT *, bm25_score() as score
      FROM comments
      WHERE document @~@ 'quick fox dog'
      ORDER BY score DESC NULLS LAST LIMIT 3;
      
  5. 外部分词创建索引。
    DROP TABLE IF EXISTS docs;
    CREATE TABLE docs (
    id serial PRIMARY KEY,
    raw_text text,        -- 自动分词字段
    tokens text[]         -- 外部分词字段
    );
    INSERT INTO docs (raw_text, tokens) VALUES
    ('The quick brown fox jumps over the lazy dog', ARRAY['the', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog']),
    ('An agile fox was seen near the river bank', ARRAY['an', 'agile', 'fox', 'was', 'seen', 'near', 'the', 'river', 'bank']),
    ('Dogs are loyal animals and good companions', ARRAY['dogs', 'are', 'loyal', 'animals', 'and', 'good', 'companions']);
    
    CREATE INDEX idx_bm25_tokens
    ON docs USING fulltext(tokens)
    WITH (
    ALGORITHMS = BM25,
    COEFFICIENTS = 'b=0.75:k=1.2'
    );
    

需要帮助?

扫码添加企业微信
获得专业技术支持

企业微信二维码
🎯 快速响应💡 专业解答