分词、匹配、相关性、排序表达式
针对目前若干用户遇到的搜索结果与预期不符合的问题进行统一详细说明,并以此为话题展开说明下OpenSearch在搜索效果方面的功能和后续一些工作方向。
首先,对于搜索来讲,最常见的有两种做法:
数据库的like查询,可以理解为简单的包含关系;
百度、google等搜索引擎,涉及到分词,将查询词根据语义切分成若干词组term(这个是搜索引擎重难点之一),通过term组合匹配给相应文档进行打分,根据分值排序,并最终返回给用户。
OpenSearch采用的方式与上述搜索引擎做法基本一致。那这里就有三部分内容会影响搜索效果:1,分词方式;2,匹配方式;3,相关性算分。
我们来分别说下这三部分在OpenSearch上的行为和表现。
接下来,我们详细说明下各个字段的展现效果及适用场景,供大家参考。
分词方式
熟悉各类分词是本篇操作的前提,请务必先查阅 内置分析器文档。
匹配方式
原理
分完词后得到若干term,如何召回文档,就涉及到匹配方式。目前OpenSearch内部默认支持的是AND,即一篇文档中包含全部的term才能被搜索出来。当然这是对同一关键词而言的,除此之外系统还支持多种匹配方式,如AND、OR、RANK、NOTAND以及(),优先级从高到低为(),ANDNOT,AND,OR,RANK。
举例
关系 | 用法 | 含义 |
query=title:”苹果 手机” | 查询title中包含苹果和手机的文档 | |
AND | query=title:’苹果’ AND cate:’手机’ | 交集。查询title中包含苹果,且cate包含手机的文档 |
OR | query=title:’苹果’ OR cate:’手机’ | 并集。查询title中包含苹果,或cate包含手机的文档 |
RANK | query=title:’苹果’ RANK cate:’手机’ | 查询title中包含苹果的文档,如果cate包含手机则可以加分 |
ANDNOT | query=title:’苹果’ ANDNOT cate:’手机’ | 查询title中包含苹果,但cate不包含手机的文档 |
案例
问:我文档中包含“吃饭了”,我搜索“吃饭”、“吃饭了”都能召回,搜索“吃饭了吗”没结果?
答:因为目前OpenSearch是要求全部的分词结果都匹配才能召回文档,上面的“吗”在文档中没有出现,所以无法召回。但可以通过查询分析解决。
问:我只想查找某些词排在最前面的文档,比如以“肯德基”开头的文档;答:目前不支持位置相关召回。
相关性算分
上面提到的都是跟召回相关的技术,召回文档之后,究竟文档如何排序就涉及到相关性。目前OpenSearch有sort子句来支持用户自定义排序。如果不设置sort,则默认为sort=-RANK;sort本身支持多维排序,以及升降序的支持。比如sort=-RANK;+bonus,意思为第一位按照相关性降序排序,相关性分值一样的文档再按照bonus升序排列。这里我们重点描述下RANK的用法,RANK即为OpenSearch中的相关性设置,主要分为两部分:基础排序(粗排)和业务排序(精排)。
原理
OpenSearch相关性算分策略为,取召回的rank_size(目前是100万)个文档按照粗排表达式的定义进行算分;取粗排分最高的N个结果(百级别)按照精排表达式进行算分,并排序;然后根据start与hit的设置取相应结果返回给用户。如果用户获取的结果超过了精排结果数N,则后续按照粗排分数排序结果继续展现。
基础排序-(粗排)表达式:从上面原理介绍中可以看出粗排对性能(latency)的影响非常大,但同时粗排又非常的重要,否则会出现好的文档无法进入精排而导致文档不能被最终展现。所以粗排要尽量的简单有效,目前OpenSearch的粗排只支持几个简单的正排字段、静态bm25、时效分等因素。
业务排序-(精排)表达式:通过粗排表达式筛选出较优质的N个文档进行详细排序,精排表达式中支持复杂的数学计算、逻辑等,并且OpenSearch提供了丰富的典型场景(如O2O类)的function和feature来满足日常的相关性需求。
同时,系统以内置了多个场景的应用结构和排序表达式,可以供大家参考和使用。
举例
场景 | 表达式 | 含义 |
论坛-粗排 | static_bm25() | 简略文本分 |
论坛-精排 | text_relevance(title)*3+text_relevance(body) + if(text_relevance(title)>0.07,timeliness(create_timestamp),timeliness(create_timestamp)*0.5) + (topped+special+atan(hits)*0.5+atan(replies))*0.1 | 文本分 、 时效分 、 其他属性分 |
O2O-粗排 | sold_score+general_score*2 | 销量、门店综合分值(离线算好) |
O2O-精排 | 2*sold_score+0.5*reward - 10*distance(lon,lat,u_posx,u_posy) + if ((flags&2) =2, 2, 0)+if(is_open=5,10,0) + special_score | 销量、配送速度及准点率 、 距离 、 是否繁忙、是否在营业时间 、 人工干预 |
小说-粗排 | static_bm25()*0.7+hh_hot*0.00003 | 文本分、热度 |
小说-精排 | pow(min(0.5,max(text_relevance(category),max(text_relevance(title), text_relevance(author)))),2) + general_score*2 + 1.5*(1/(1+pow(2.718281,-((log10(hh_hot)-2)*2-5))))) | 分类相关性、标题相关性、作者相关性 、 小说质量 、 小说热度 |
电商-粗排 | static_bm25()+general_score*2+timeliness(end_time) | 文本分、宝贝综合分值、过期时间 |
电商-精排 | text_relevance(title)*3+text_relevance(category) + general_score*2+boughtScore*2 + tag_match(ctr_query_value,doc_value,mul,sum,false,true)+.. | 文本相关性、类目相关性 、 宝贝人气、卖家分 、 ctr预估、特征规则分等 |
案例
问:精排表达式text_relevance(seller_id)报找不到字段答:text_relevance()只支持TEXT及SHORT_TEXT类型,其他不可以。
问:查询报2112错误,是什么问题?答:查询语句(query子句)必须与formula相配合,比如query=default:’keyword’,default中包含title和body字段,而formula指定text_relevance(title)+text_relevance(author)则会报错,因为author在default中不存在。
使用技巧
排序表达式的算分是在查询结算对每个文档进行计算的,所以如果跟查询无关的部分的计算可以预先离线计算好,新增一个general_score字段来存放,排序的时候只要使用general_score字段即可,避免大量计算过程,提高查询性能。
tag_match的feature允许用户将query中的特征与doc中特征做多维运算,在电商场景下有着非常广泛的用途,有类似的需求的用户可以研究下。
OpenSearch提供了丰富的function和feature,使用得当可以获得非常强大的功能。
相关性有很多部分共同组成,各项之间的权重需要根据搜索排序效果不断进行调整以达到一个用户满意的搜索效果。