文本检索函数
BM25(Best Matching 25)是一种用于信息检索的经典算法,主要用于衡量一个查询与一组文档之间的相关性得分。VexDB支持基于BM25算法实现的全文检索索引fulltext,本文介绍的函数仅支持在全文检索索引查询中使用。
bm25_score()
描述: 用于计算文档与查询的相关性得分。
BM25算法基于词频(Term Frequency,TF)、逆文档频率(Inverse Document Frequency,IDF)和文档长度归一化(所有文档的平均长度)进行相关性计算。
全文检索功能依赖文本整体的统计信息计算每条文本和查询的相关度分数,常规表达式无法仅通过文本和查询内容计算出有效内容,必须通过全文检索索引fulltext查询对应算子返回分数数值。
参数: 无
返回类型: real
示例: 从comments表中搜索与查询给定的'quick fox dog'相关性最高的前3个结果。
SELECT *, bm25_score() as score
FROM comments
WHERE document @~@ 'quick fox dog'
ORDER BY score DESC NULLS LAST LIMIT 3;
使用建议
以下提供了一个使用fulltext索引进行全文检索的示例场景,并介绍了使用bm25_score函数计算文本相关度分数的说明。
- 运行以下语句产生定义与数据作为用例运行环境。
-- 创建表和全文索引 create table t1 (id int, title text, content text); create index on t1 using fulltext(title, content); create table t2 (like t1 including all); create table t3 (like t1 including all); -- 导入数据 insert into t1 values (1, '数据库文档', '全文检索文档说明'), (1, '数据库文档', '向量查询文档说明'), (2, '数据库功能', '支持全文检索功能'), (2, '数据库功能', '向量查询功能和全文检索功能结合'), (3, '其他信息', 'TBA'); insert into t2 select id + 3, title, content from t1; insert into t3 select id + 3, title, content from t2; - 使用全文检索索引。
- 使用分数进行排序
全文检索查询可以使用@~@操作符对自然语言查询内容做匹配,并根据其相似度返回TOP-K数据,例如:select id, content from t1 where content @~@ '全文检索功能' order by bm25_score() desc nulls last limit 5;
注意,全文检索查询仅持降序查询。即越相关的数据越先返回,在BM25算法相似计算中不存在最不相似的概念,所以对于使用升序查询的语句(明确指定或者使用默认升序),例如对于以下语句,则会报警“错误的排序顺序”并仍使用降序排序:select id, content from t1 where content @~@ '全文检索功能' order by bm25_score() asc limit 5;
同理,排序中NULL数据处理将按照nulls last排序,对于nulls first排序(明确指定或者使用默认排序),例如对于以下语句,会报警“错误的排序顺序”并仍使用降序排序:
select id, content from t1 where content @~@ '全文检索功能' order by bm25_score() desc nulls first limit 5;- 获取分数
其中的bm25_score()函数表示查询返回的数据与@~@自然语言查询的总相似分数;可以通过SELECT获取其数值,此时可以使用别名机制进行简化,例如:select id, content, bm25_score() as score from t1 where content @~@ '全文检索功能' order by score desc nulls last limit 5; - 多@~@操作符条件
- 分数计算:对所有条件的评分求和,条件不匹配为0分。如上例title @~@ '宠物'中数据('', '狗')的title为空完全不符合条件,则该对应分数为0,只计算其他条件分数。
- 多条件组合使用方法:所有条件必须通过AND排列,不支持用OR联结条件。
- 返回数据过滤逻辑:可选返回内容为所有评分为非0的数据,例如查询包括条件A AND 条件B AND 条件C,那么数据跟此三个条件任意一个相关则可以返回,如果要求三个条件均必须符合则可以通过设置MINIMUM_SHOULD_MATCH实现。
- @-@查询不参与分数计算,所有条件同样必须通过AND排列但返回数据必须符合全部条件。可以和@~@查询混合使用。
- 跨表多@~@操作符相关语句
同理,对于多个表的多@@操作符查询,bm25_score()函数返回其相关分数总和,但需注意函数生效范围,分数计算只涉及生效范围内的所有@@操作。
以下两个查询语句效果是等价的:select t1.id, t2.id, t1.content, t2.content, bm25_score() as score from t1, t2 where t1.content @~@ '全文检索功能' and t2.content @~@ '向量' and t1.id + 3 = t2.id; --等价于 select t1.id, t2.id, t1.content, t2.content, a + b as score from (select *, bm25_score() as a from t1 where t1.content @~@ '全文检索功能') t1, (select *, bm25_score() as b from t2 where t2.content @~@ '向量') t2 where t1.id + 3 = t2.id;
同样,bm25_score()函数在不同生效范围会有不同的数值,以下示例中函数返回分数会根据其生效范围决定数值。select t1.id, t2.id, t1.content, t2.content, bm25_score() as score, a + b as cmp_score from (select *, bm25_score() as a from t1 where t1.content @~@ '全文检索功能') t1, (select *, bm25_score() as b from t2 where t2.content @~@ '向量') t2 where t1.id + 3 = t2.id;
注意以上跨表用法会读取全部符合条件的数据,如果需要做TOP-K检索,建议将TOPK下推至每个表中。
以下方案以损失召回的代价大幅度提升可能的性能。select t1.id, t2.id, t1.content, t2.content, a + b as score from (select *, bm25_score() as a from t1 where t1.content @~@ '全文检索功能' order by a desc nulls last limit 10) t1 full join (select *, bm25_score() as b from t2 where t2.content @~@ '向量' order by b desc nulls last limit 10) t2 on (t1.id + 3 = t2.id) order by score desc limit 5;
- 使用分数进行排序
- 清理当前环境,删除数据。
DROP TABLE IF EXISTS t1, t2, t3;
SQL语句中bm25_score函数使用范围
- 聚集操作,例如:
select id, string_agg(content, ',') as all_relavant_content, sum(bm25_score()) as total_score from t1 where content @~@ '全文检索功能' group by id;
聚集后的别名可能由于函数性质限制在某些地方不能直接使用,但可以尝试使用原表达式替换,例如:select id, string_agg(content, ',') as all_relavant_content, sum(bm25_score()) as total_score from t1 where content @~@ '全文检索功能' group by id having sum(bm25_score()) > 1;
注意,该场景中使用聚集操作后对相似度进行排序操作无法通过索引完成,此时全文检索会遍历所有与查询相关的数据,时延较之TOP-K查询可能大幅增加。
由于评分函数表现为不定条件的分数总和,在聚集算子返回校验中并未对分数做唯一限制,如果错误使用评分函数作为算子下集合返回值,则可能产生报错或者对应集合中的任意分数。select sum(bm25_score()) as total_score, bm25_score() from t1 where content @~@ '全文检索功能'; - 链接操作,在跨表场景文档中已经说明了函数在SELECT和ORDER BY部分的用例,可以正常使用,其生效范围即对应语句范围。
但需注意在函数在链接部分的指代范围不明确,在链接部分中使用该函数属于未定义行为,不推荐使用。例如:select t1.id, t2.id, t1.content, t2.content, bm25_score() as score from (select *, bm25_score() as a from t1 where t1.content @~@ '全文检索功能' order by a desc nulls last limit 10) t1 full join (select *, bm25_score() as b from t2 where t2.content @~@ '向量' order by b desc nulls last limit 10) t2 on (t1.id + 3 = t2.id) full join (select * from t3 where t3.content @~@ '全文向量') t3 on (t1.id + 6 = t3.id and bm25_score() > 0.3) order by score desc limit 5;
其链接条件包含bm25_score() > 0.3,该函数分数所对应范围可能有(t1),(t1,t2,t3),(t3)甚至可能包括(t2,t3),其实际生效范围由优化器推导的最优路径决定,而且路径可能会随着数据分布和统计信息变化而发生改变,在生产场景使用可能产生严重问题。 - CTE操作,bm25_score函数作用域不穿透CTE子扫描,如果需要CTE子域中的相关分数,可以使用别名机制选择。例如:
-- 正确用例 with cte as (select *, bm25_score() as s from t1 where content @~@ '全文检索功能' order by s desc nulls last limit 10) select id, content, s from cte; -- 错误用例 with cte as (select *, bm25_score() as s from t1 where content @~@ '全文检索功能' order by s desc nulls last limit 10) select id, content, bm25_score() from cte;
同理,外界的评分函数计算总和不包括CTE子域分数,如有需要可以手动累加。 - 并集操作,对于并集内部语句,只要函数使用符合语法要求则应该正常执行;但函数评分不能向内穿透其作用域,同CTE查询需要别名机制选择返回评分:
-- 正确用例 select id, content, s as score from (select id, content, bm25_score() as s from t1 where content @~@ '全文检索功能' order by s desc nulls last limit 10) union all (select id, content, bm25_score() as s from t2 where content @~@ '全文检索' order by s desc nulls last limit 10) union (select id, content, bm25_score() as s from t3 where content @~@ '功能' order by s desc nulls last limit 10) order by score desc limit 5; -- 错误用例 select id, content, bm25_score() as score from (select id, content, bm25_score() as s from t1 where content @~@ '全文检索功能' order by s desc nulls last limit 10) union all (select id, content, bm25_score() as s from t2 where content @~@ '全文检索' order by s desc nulls last limit 10) union (select id, content, bm25_score() as s from t3 where content @~@ '功能' order by s desc nulls last limit 10) order by score desc limit 5;
bm25_tokenize(text,dictionary)
描述: 用于文本预处理和分词。
参数: 函数输入需要进行分词处理的文本类型字符串(必选),以及分词使用的分词词典名称或OID(可选),不指定词典时使用系统默认词典cn_tokenizer。
返回类型: text,字符串分词结果,用于模拟显示用该词典构建索引中文本数据处理结果。
示例:
SELECT bm25_tokenize('我司正在研发一款基于openGauss的不仅高性能又高可用而且高精度还易用的数据库');
返回结果如下:
bm25_tokenize
-------------------------------------------------------------------
{我司,正在,研发,一款,opengauss,高性能,高,可用,高精度,易用,数据库}
(1 row)
bm25_query_tokenize(text,dictionary)
描述: 对输入文本字符串进行扩展分词。
扩展分词 = 基础分词 + 语义/语法扩展,表示对查询字符串进行更全面和智能的分词处理,而不仅仅是简单的分词。
说明
该函数自V3.0.0.1开始支持。
参数: 函数输入需要进行分词处理的文本类型字符串(必选),以及分词使用的分词词典名称或OID(可选),不指定词典时使用系统默认词典cn_tokenizer。
返回类型: text,函数返回包括扩展分词关键字在内的字符串分词结果,用于模拟显示用该词典进行查询时对查询文本的处理结果。
示例:
SELECT bm25_query_tokenize('向量数据库是AI时代的技术基石');
返回结果如下:
bm25_query_tokenize
-------------------------------------------
{向量,数据,据库,数据库,ai,时代,技术,基石}
(1 row)
说明