在上一篇match query讨论了全文检索
比如,有如下查询
{
"match": {
"content": "java spark"
}
}
match query,只能搜索到包含java和spark的document,但是不知道java和spark是不是离的很近。
如果希望搜索java spark,中间不能插入任何其他的字符,那这个时候match去做全文检索是无法做到的,此时需要使用match_phrase
GET /forum/_search
{
"query": {
"match_phrase": {
"content": "java spark"
}
}
}
doc中只有content包含java spark的文档才会返回来
1、term position(分词后的position)
GET _analyze
{
"text": "hello world, java spark",
"analyzer": "standard"
}
查看text内容经过standard分词器之后的position情况
2、match_phrase的基本原理
假设有如下两个doc
doc1:hello world, java spark
doc2:hi, spark java
那么分完词之后的position如下:
hello doc1(0)
wolrd doc1(1)
java doc1(2) doc2(2)
spark doc1(3) doc2(1)
当使用match_phrase查询java spark时:
java 匹配到: doc1(2) doc2(2)
spark 匹配到:doc1(3) doc2(1)
一个doc,必须包含每个term,才能拿出来继续计算
对于doc1:
spark position恰巧比java大1 ,恰好满足条件
对于doc2:
spark position比java position小1,而不是大1,不符合条件
最终将doc1返回
3、slop
要经过几次移动才能与一个document的field中的匹配,这个移动的次数,就是slop
举例:
hello world, java is very good, spark is also very good.
使用match_phrase检索java spark,遗憾的检索不到,此时es提供了slop,允许java spark进行移动,来尝试与doc进行匹配
java is very good spark is
java spark slop=0
java spark slop=1
java spark slop=2
java spark slop=3
spark移动了3次,和匹配到了文档
GET /forum/_search
{
"query": {
"match_phrase": {
"title": {
"query": "java spark",
"slop": 3 //最多移动3次
}
}
}
}
4、召回率和精准度
召回率:搜索一个java spark,总共有100个doc,能返回多少个doc作为结果
精准度:搜索一个java spark,尽可能让包含java spark,或者是java和spark离的很近的doc,排在最前面
match_phrase短语搜索必须所有term都在doc field中出现,如果某一个doc可能就是有某个term没有包含,那么就无法作为结果返回,导致召回率比较低,精准度高
java spark 匹配:hello world java (不能被召回)
java spark 匹配:hello world, java spark (能被召回)
如果想提高召回率,同时也兼顾精准度
可以使用如下方式:
GET /forum/article/_search
{
"query": {
"bool": {
"must": {
"match": {
"title": {
"query": "java spark" //单纯的match是没法区分java和spark的距离,所以要配合match_phrase
}
}
},
"should": {
"match_phrase": {
"title": {
"query": "java spark",
"slop": 50
}
}
}
}
}
}
在slop以内,如果java spark能匹配上一个doc,那么就会对doc贡献自己的relevance score,如果java和spark靠的越近,那么就分数越高
5、重打分
match query比phrase match的性能要高10倍,比proximity match(phrase match+slop)的性能要高20倍。
优化proximity match的性能,一般就是减少要进行proximity match搜索的document数量。
用match query先过滤出需要的数据,然后再用proximity match来根据term距离提高doc的分数,同时proximity match只针对每个shard的分数排名前n个doc起作用,来重新调整它们的分数,这个过程称之为rescoring,重计分。因为一般会分页查询,只会看到前几页的数据,所以不需要对所有结果进行proximity match操作。此种方式叫做重打分。
默认情况下,match匹配了1000个doc,使用proximity match会对每个doc进行一遍运算,判断能否slop移动匹配上,然后去贡献自己的分数,这样性能太低。
GET /forum/_search
{
"query": {
"match": {
"content": "java spark"
}
},
"rescore": {
"window_size": 50, //只对match检索出来的前50条数据重新打分
"query": {
"rescore_query": {
"match_phrase": {
"content": {
"query": "java spark",
"slop": 50
}
}
}
}
}
}