从 best-fields 换成 most-fields 策略
best-fields 策略, 主要是说将某一个 field 匹配尽可能多的关键词的 doc 优先返回回来
most-fields 策略, 主要是说尽可能返回更多 field 匹配到某个关键词的 doc, 优先返回回来
与 best_fields 的区别
(1)best_fields, 是对多个 field 进行搜索, 挑选某个 field 匹配度最高的那个分数, 同时在多个 query 最高分相同的情况下, 在一定程度上考虑其他 query 的分数. 简单来说, 你对多个 field 进行搜索, 就想搜索到某一个 field 尽可能包含更多关键字的数据
优点: 通过 best_fields 策略, 以及综合考虑其他 field, 还有 minimum_should_match 支持, 可以尽可能精准地将匹配的结果推送到最前面
缺点: 除了那些精准匹配的结果, 其他差不多大的结果, 排序结果不是太均匀, 没有什么区分度了
实际的例子: 百度之类的搜索引擎, 最匹配的到最前面, 但是其他的就没什么区分度了
(2)most_fields, 综合多个 field 一起进行搜索, 尽可能多地让所有 field 的 query 参与到总分数的计算中来, 此时就会是个大杂烩, 出现类似 best_fields 案例最开始的那个结果, 结果不一定精准, 某一个 document 的一个 field 包含更多的关键字, 但是因为其他 document 有更多 field 匹配到了, 所以排在了前面; 所以需要建立类似 sub_title.std 这样的 field, 尽可能让某一个 field 精准匹配 query string, 贡献更高的分数, 将更精准匹配的数据排到前面
优点: 将尽可能匹配更多 field 的结果推送到最前面, 整个排序结果是比较均匀的
缺点: 可能那些精准匹配的结果, 无法推送到最前面
实际的例子: wiki, 明显的 most_fields 策略, 搜索结果比较均匀, 但是的确要翻好几页才能找到最匹配的结果
问题 1: 只是找到尽可能多的 field 匹配的 doc, 而不是某个 field 完全匹配的 doc
问题 2:most_fields, 没办法用 minimum_should_match 去掉长尾数据, 就是匹配的特别少的结果
问题 3:TF/IDF 算法, 比如 Peter Smith 和 Smith Williams, 搜索 Peter Smith 的时候, 由于 first_name 中很少有 Smith 的, 所以 query 在所有 document 中的频率很低, 得到的分数很高, 可能 Smith Williams 反而会排在 Peter Smith 前面
---------------------------------------------------------
使用原生 cross-fiels 技术解决搜索弊端解决方案
- GET /forum/article/_search
- {
- "query": {
- "multi_match": {
- "query": "Peter Smith",
- "type": "cross_fields",
- "operator": "and",
- "fields": ["author_first_name", "author_last_name"]
- }
- }
- }
问题 1: 只是找到尽可能多的 field 匹配的 doc, 而不是某个 field 完全匹配的 doc --> 解决, 要求每个 term 都必须在任何一个 field 中出现
Peter,Smith
要求 Peter 必须在 author_first_name 或 author_last_name 中出现
要求 Smith 必须在 author_first_name 或 author_last_name 中出现
Peter Smith 可能是横跨在多个 field 中的, 所以必须要求每个 term 都在某个 field 中出现, 组合起来才能组成我们想要的标识, 完整的人名
原来 most_fiels, 可能像 Smith Williams 也可能会出现, 因为 most_fields 要求只是任何一个 field 匹配了就可以, 匹配的 field 越多, 分数越高
问题 2:most_fields, 没办法用 minimum_should_match 去掉长尾数据, 就是匹配的特别少的结果 --> 解决, 既然每个 term 都要求出现, 长尾肯定被去除掉了
java hadoop spark --> 这 3 个 term 都必须在任何一个 field 出现了
比如有的 document, 只有一个 field 中包含一个 java, 那就被干掉了, 作为长尾就没了
问题 3:TF/IDF 算法, 比如 Peter Smith 和 Smith Williams, 搜索 Peter Smith 的时候, 由于 first_name 中很少有 Smith 的, 所以 query 在所有 document 中的频率很低, 得到的分数很高, 可能 Smith Williams 反而会排在 Peter Smith 前面 --> 计算 IDF 的时候, 将每个 query 在每个 field 中的 IDF 都取出来, 取最小值, 就不会出现极端情况下的极大值了
- Peter Smith
- Peter
- Smith
Smith, 在 author_first_name 这个 field 中, 在所有 doc 的这个 Field 中, 出现的频率很低, 导致 IDF 分数很高; Smith 在所有 doc 的 author_last_name field 中的频率算出一个 IDF 分数, 因为一般来说 last_name 中的 Smith 频率都较高, 所以 IDF 分数是正常的, 不会太高; 然后对于 Smith 来说, 会取两个 IDF 分数中, 较小的那个分数. 就不会出现 IDF 分过高的情况.
实战中掌握 phrase matching 搜索技术
- GET /forum/article/_search
- {
- "query":{
- "match_phrase":{
- "title":{
- "query":"java spark",
- "slop":3
- }
- }
- }
- }
假如 Filed 是 java is very good and spark 我们要匹配 java spack
- java is very good and spark
- java spack
Slop 最多移动几位进行匹配这是就要设置成 4 否则无法匹配 Slop 移动次数越低分数越高反之就越低
------------------------------------------------------------
混合使用 match 和近似匹配实现召回率与精准度的平衡
召回率
比如你搜索一个 java spark, 总共有 100 个 doc, 能返回多少个 doc 作为结果, 就是召回率, recall
精准度
比如你搜索一个 java spark, 能不能尽可能让包含 java spark, 或者是 java 和 spark 离的很近的 doc, 排在最前面, precision
直接用 match_phrase 短语搜索, 会导致必须所有 term 都在 doc field 中出现, 而且距离在 slop 限定范围内, 才能匹配上
match phrase,proximity match, 要求 doc 必须包含所有的 term, 才能作为结果返回; 如果某一个 doc 可能就是有某个 term 没有包含, 那么就无法作为结果返回
java spark --> hello world java --> 就不能返回了
java spark --> hello world, java spark --> 才可以返回
近似匹配的时候, 召回率比较低, 精准度太高了
但是有时可能我们希望的是匹配到几个 term 中的部分, 就可以作为结果出来, 这样可以提高召回率. 同时我们也希望用上 match_phrase 根据距离提升分数的功能, 让几个 term 距离越近分数就越高, 优先返回
就是优先满足召回率, 意思, java spark, 包含 java 的也返回, 包含 spark 的也返回, 包含 java 和 spark 的也返回; 同时兼顾精准度, 就是包含 java 和 spark, 同时 java 和 spark 离的越近的 doc 排在最前面
此时可以用 bool 组合 match query 和 match_phrase query 一起, 来实现上述效果
- GET /forum/article/_search
- {
- "query": {
- "bool": {
- "must": {
- "match": {
- "title": {
"query": "java spark" --> java 或 spark 或 java spark,java 和 spark 靠前, 但是没法区分 java 和 spark 的距离, 也许 java 和 spark 靠的很近, 但是没法排在最前面
- }
- }
- },
- "should": {
"match_phrase": { --> 在 slop 以内, 如果 java spark 能匹配上一个 doc, 那么就会对 doc 贡献自己的 relevance score, 如果 java 和 spark 靠的越近, 那么就分数越高
- "title": {
- "query": "java spark",
- "slop": 50
- }
- }
- }
- }
- }
- }
match 和 phrase match(proximity match) 区别
match --> 只要简单的匹配到了一个 term, 就可以理解将 term 对应的 doc 作为结果返回, 扫描倒排索引, 扫描到了就 ok
phrase match --> 首先扫描到所有 term 的 doc list; 找到包含所有 term 的 doc list; 然后对每个 doc 都计算每个 term 的 position, 是否符合指定的范围; slop, 需要进行复杂的运算, 来判断能否通过 slop 移动, 匹配一个 doc
match query 的性能比 phrase match 和 proximity match(有 slop) 要高很多. 因为后两者都要计算 position 的距离.
match query 比 phrase match 的性能要高 10 倍, 比 proximity match 的性能要高 20 倍.
但是别太担心, 因为 es 的性能一般都在毫秒级别, match query 一般就在几毫秒, 或者几十毫秒, 而 phrase match 和 proximity match 的性能在几十毫秒到几百毫秒之间, 所以也是可以接受的.
优化 proximity match 的性能, 一般就是减少要进行 proximity match 搜索的 document 数量. 主要思路就是, 用 match query 先过滤出需要的数据, 然后再用 proximity match 来根据 term 距离提高 doc 的分数, 同时 proximity match 只针对每个 shard 的分数排名前 n 个 doc 起作用, 来重新调整它们的分数, 这个过程称之为 rescoring, 重计分. 因为一般用户会分页查询, 只会看到前几页的数据, 所以不需要对所有结果进行 proximity match 操作.
用我们刚才的说法, match + proximity match 同时实现召回率和精准度
默认情况下, match 也许匹配了 1000 个 doc,proximity match 全都需要对每个 doc 进行一遍运算, 判断能否 slop 移动匹配上, 然后去贡献自己的分数
但是很多情况下, match 出来也许 1000 个 doc, 其实用户大部分情况下是分页查询的, 所以可能最多只会看前几页, 比如一页是 10 条, 最多也许就看 5 页, 就是 50 条
proximity match 只要对前 50 个 doc 进行 slop 移动去匹配, 去贡献自己的分数即可, 不需要对全部 1000 个 doc 都去进行计算和贡献分数
rescore: 重打分
match:1000 个 doc, 其实这时候每个 doc 都有一个分数了; proximity match, 前 50 个 doc, 进行 rescore, 重打分, 即可; 让前 50 个 doc,term 举例越近的, 排在越前面
- GET /forum/article/_search
- {
- "query": {
- "match": {
- "content": "java spark"
- }
- },
- "rescore": {
- "window_size": 50,
- "query": {
- "rescore_query": {
- "match_phrase": {
- "content": {
- "query": "java spark",
- "slop": 50
- }
- }
- }
- }
- }
- }
来源: http://www.bubuko.com/infodetail-3094865.html