Elasticsearch深入6

深度探秘搜索技术_filter执行原理深度剖析

 (1)在倒排索引中查找搜索串,获取document list

 date来举例

 word                 doc1          doc2          doc3

 2017-01-01       *                *

2017-02-02                         *                *

2017-03-03       *                *                *

 filter:2017-02-02

 到倒排索引中一找,发现2017-02-02对应的document list是doc2,doc3

 (2)为每个在倒排索引中搜索到的结果,构建一个bitset,[0, 0, 0, 1, 0, 1]

 非常重要

 使用找到的doc list,构建一个bitset,就是一个二进制的数组,数组每个元素都是0或1,用来标识一个doc对一个filter条件是否匹配,如果匹配就是1,不匹配就是0

 [0, 1, 1]

 doc1:不匹配这个filter的

doc2和do3:是匹配这个filter的

 尽可能用简单的数据结构去实现复杂的功能,可以节省内存空间,提升性能

 (3)遍历每个过滤条件对应的bitset,优先从最稀疏的开始搜索,查找满足所有条件的document

 后面会讲解,一次性其实可以在一个search请求中,发出多个filter条件,每个filter条件都会对应一个bitset

遍历每个filter条件对应的bitset,先从最稀疏的开始遍历

 [0, 0, 0, 1, 0, 0]:比较稀疏

[0, 1, 0, 1, 0, 1]

 先遍历比较稀疏的bitset,就可以先过滤掉尽可能多的数据

 遍历所有的bitset,找到匹配所有filter条件的doc

请求:filter,postDate=2017-01-01,userID=1

 postDate: [0, 0, 1, 1, 0, 0]

userID:   [0, 1, 0, 1, 0, 1]

 遍历完两个bitset之后,找到的匹配所有条件的doc,就是doc4

 就可以将document作为结果返回给client了

 (4)caching bitset,跟踪query,在最近256个query中超过一定次数的过滤条件,缓存其bitset。对于小segment(<1000,或<3%),不缓存bitset。

 比如postDate=2017-01-01,[0, 0, 1, 1, 0, 0],可以缓存在内存中,这样下次如果再有这个条件过来的时候,就不用重新扫描倒排索引,反复生成bitset,可以大幅度提升性能。

 在最近的256个filter中,有某个filter超过了一定的次数,次数不固定,就会自动缓存这个filter对应的bitset

 segment(上半季),filter针对小segment获取到的结果,可以不缓存,segment记录数<1000,或者segment大小<index总大小的3%

 segment数据量很小,此时哪怕是扫描也很快;segment会在后台自动合并,小segment很快就会跟其他小segment合并成大segment,此时就缓存也没有什么意义,segment很快就消失了

 针对一个小segment的bitset,[0, 0, 1, 0]

 filter比query的好处就在于会caching,但是之前不知道caching的是什么东西,实际上并不是一个filter返回的完整的doc list数据结果。而是filter bitset缓存起来。下次不用扫描倒排索引了。

 (5)filter大部分情况下来说,在query之前执行,先尽量过滤掉尽可能多的数据

 query:是会计算doc对搜索条件的relevance score,还会根据这个score去排序

filter:只是简单过滤出想要的数据,不计算relevance score,也不排序

 (6)如果document有新增或修改,那么cached bitset会被自动更新

 postDate=2017-01-01,[0, 0, 1, 0]

document,id=5,postDate=2017-01-01,会自动更新到postDate=2017-01-01这个filter的bitset中,全自动,缓存会自动更新。postDate=2017-01-01的bitset,[0, 0, 1, 0, 1]

document,id=1,postDate=2016-12-30,修改为postDate-2017-01-01,此时也会自动更新bitset,[1, 0, 1, 0, 1]

 

(7)以后只要是有相同的filter条件的,会直接来使用这个过滤条件对应的cached bitset

 手动控制全文检索结果的精准度

1、为帖子数据增加标题字段

POST /forum/article/_bulk
{ "update": { "_id": "1"} }
{ "doc" : {"title" : "this is java and elasticsearch blog"} }
{ "update": { "_id": "2"} }
{ "doc" : {"title" : "this is java blog"} }
{ "update": { "_id": "3"} }
{ "doc" : {"title" : "this is elasticsearch blog"} }
{ "update": { "_id": "4"} }
{ "doc" : {"title" : "this is java, elasticsearch, hadoop blog"} }
{ "update": { "_id": "5"} }
{ "doc" : {"title" : "this is spark blog"} }

2、搜索标题中包含java或elasticsearch的blog

这个,就跟之前的那个term query,不一样了。不是搜索exact value,是进行full text全文检索。
match query,是负责进行全文检索的。当然,如果要检索的field,是not_analyzed类型的,那么match query也相当于term query

GET /forum/article/_search
{
    "query":{
        "match":{
            "title":"java elasticsearch"
        }
    }
}

使用诸如上面的match query进行多值搜索的时候,es会在底层自动将这个match query转换为bool的语法
bool should,指定多个搜索词,同时使用term query

{
    "bool":{
        "should":[
            {
                "term":{
                    "title":"java"
                }
            },
            {
                "term":{
                    "title":"elasticsearch"
                }
            }
        ]
    }
}

3、搜索标题中包含java和elasticsearch的blog

搜索结果精准控制的第一步:灵活使用and关键字,如果你是希望所有的搜索关键字都要匹配的,那么就用and,可以实现单纯match query无法实现的效果想到了query_string中的and有啥区别吧

GET /forum/article/_search
{
    "query":{
        "match":{
            "title":{
                "query":"java elasticsearch",
                "operator":"and"
            }
        }
    }
}

底层转换

{
    "bool":{
        "must":[
            {
                "term":{
                    "title":"java"
                }
            },
            {
                "term":{
                    "title":"elasticsearch"
                }
            }
        ]
    }
}

4、搜索包含java,elasticsearch,spark,hadoop,4个关键字中,至少3个的blog

控制搜索结果的精准度的第二步:指定一些关键字中,必须至少匹配其中的多少个关键字,才能作为结果返回

GET /forum/article/_search
{
    "query":{
        "match":{
            "title":{
                "query":"java elasticsearch spark hadoop",
                "minimum_should_match":"75%"
            }
        }
    }
}

5、用bool组合多个搜索条件,来搜索title

GET /forum/article/_search
{
    "query":{
        "bool":{
            "must":{
                "match":{
                    "title":"java"
                }
            },
            "must_not":{
                "match":{
                    "title":"spark"
                }
            },
            "should":[
                {
                    "match":{
                        "title":"hadoop"
                    }
                },
                {
                    "match":{
                        "title":"elasticsearch"
                    }
                }
            ]
        }
    }
}

6、bool组合多个搜索条件,如何计算relevance score

must和should搜索对应的分数,加起来,除以must和should的总数

排名第一:java,同时包含should中所有的关键字,hadoop,elasticsearch
排名第二:java,同时包含should中的elasticsearch
排名第三:java,不包含should中的任何关键字

should是可以影响相关度分数的

must是确保说,谁必须有这个关键字,同时会根据这个must的条件去计算出document对这个搜索条件的relevance score
在满足must的基础之上,should中的条件,不匹配也可以,但是如果匹配的更多,那么document的relevance score就会更高

{
    "took":6,
    "timed_out":false,
    "_shards":{
        "total":5,
        "successful":5,
        "failed":0
    },
    "hits":{
        "total":3,
        "max_score":1.3375794,
        "hits":[
            {
                "_index":"forum",
                "_type":"article",
                "_id":"4",
                "_score":1.3375794,
                "_source":{
                    "articleID":"QQPX-R-3956-#aD8",
                    "userID":2,
                    "hidden":true,
                    "postDate":"2017-01-02",
                    "tag":[
                        "java",
                        "elasticsearch"
                    ],
                    "tag_cnt":2,
                    "view_cnt":80,
                    "title":"this is java, elasticsearch, hadoop blog"
                }
            },
            {
                "_index":"forum",
                "_type":"article",
                "_id":"1",
                "_score":0.53484553,
                "_source":{
                    "articleID":"XHDK-A-1293-#fJ3",
                    "userID":1,
                    "hidden":false,
                    "postDate":"2017-01-01",
                    "tag":[
                        "java",
                        "hadoop"
                    ],
                    "tag_cnt":2,
                    "view_cnt":30,
                    "title":"this is java and elasticsearch blog"
                }
            },
            {
                "_index":"forum",
                "_type":"article",
                "_id":"2",
                "_score":0.19856805,
                "_source":{
                    "articleID":"KDKE-B-9947-#kL5",
                    "userID":1,
                    "hidden":false,
                    "postDate":"2017-01-02",
                    "tag":[
                        "java"
                    ],
                    "tag_cnt":1,
                    "view_cnt":50,
                    "title":"this is java blog"
                }
            }
        ]
    }
}

7、搜索java,hadoop,spark,elasticsearch,至少包含其中3个关键字

默认情况下,should是可以不匹配任何一个的,比如上面的搜索中,this is java blog,就不匹配任何一个should条件
但是有个例外的情况,如果没有must的话,那么should中必须至少匹配一个才可以
比如下面的搜索,should中有4个条件,默认情况下,只要满足其中一个条件,就可以匹配作为结果返回

但是可以精准控制,should的4个条件中,至少匹配几个才能作为结果返回

GET /forum/article/_search
{
    "query":{
        "bool":{
            "should":[
                {
                    "match":{
                        "title":"java"
                    }
                },
                {
                    "match":{
                        "title":"elasticsearch"
                    }
                },
                {
                    "match":{
                        "title":"hadoop"
                    }
                },
                {
                    "match":{
                        "title":"spark"
                    }
                }
            ],
            "minimum_should_match":3
        }
    }
}

boost 

搜索条件的权重,boost,可以将某个搜索条件的权重加大,此时当匹配这个搜索条件和匹配另一个搜索条件的document,计算relevance score时,匹配权重更大的搜索条件的document,relevance score会更高,当然也就会优先被返回回来

默认情况下,搜索条件的权重都是一样的,都是1

doc1 java jdk7 

doc2 java jdk8

doc3 java jdk9

doc4 java jdk9

需求搜索 必须包含java的doc但是should中有三个条件 jdk7 jdk8 jdk9 所以doc 1,2,3都会被返回但是分数不一致,但是现在我就想对jdk8更加上心想让jdk8分数最高排在最前使用boost

{
    "match":{
        "title":{
            "query":"jdk8",
            "boost":5
        }
    }
}

 

案例实战基于dis_max实现best fields策略进行多字段搜索

 

1、为帖子数据增加content字段

 

POST /forum/article/_bulk

{ "update": { "_id": "1"} }

{ "doc" : {"content" : "i like to write best elasticsearch article"} }

{ "update": { "_id": "2"} }

{ "doc" : {"content" : "i think java is the best programming language"} }

{ "update": { "_id": "3"} }

{ "doc" : {"content" : "i am only an elasticsearch beginner"} }

{ "update": { "_id": "4"} }

{ "doc" : {"content" : "elasticsearch and hadoop are all very good solution, i am a beginner"} }

{ "update": { "_id": "5"} }

{ "doc" : {"content" : "spark is best big data solution based on scala ,an programming language similar to java"} }

 2、搜索title或content中包含java或solution的帖子

 下面这个就是multi-field搜索,多字段搜索

 GET /forum/article/_search

{

    "query": {

        "bool": {

            "should": [

                { "match": { "title": "java solution" }},

                { "match": { "content":  "java solution" }}

            ]

        }

    }

}

 

3、结果分析

 期望的是doc5,结果是doc2,doc4排在了前面

 计算每个document的relevance score:每个query的分数,乘以matched query数量(匹配的query),除以总query数量

 算一下doc4的分数

 { "match": { "title": "java solution" }},针对doc4,是有一个分数的

{ "match": { "content":  "java solution" }},针对doc4,也是有一个分数的

 所以是两个分数加起来,比如说,1.1 + 1.2 = 2.3

matched query数量 = 2

总query数量 = 2

 2.3 * 2 / 2 = 2.3

 算一下doc5的分数

 { "match": { "title": "java solution" }},针对doc5,是没有分数的

{ "match": { "content":  "java solution" }},针对doc5,是有一个分数的

 所以说,只有一个query是有分数的,比如2.3

matched query数量 = 1

总query数量 = 2

 2.3 * 1 / 2 = 1.15

 doc5的分数 = 1.15 < doc4的分数 = 2.3

 4、best fields策略,dis_max

 best fields策略,就是说,搜索到的结果,应该是某一个field中匹配到了尽可能多的关键词,被排在前面;而不是尽可能多的field匹配到了少数的关键词,排在了前面  一句话概括就是 doc1如果一个filed的能够匹配多个关键字比多个filed匹配多个关键字权重要高>_<

 dis_max语法,直接取多个query中,分数最高的那一个query的分数即可

 { "match": { "title": "java solution" }},针对doc4,是有一个分数的,1.1

{ "match": { "content":  "java solution" }},针对doc4,也是有一个分数的,1.2

取最大分数,1.2

 { "match": { "title": "java solution" }},针对doc5,是没有分数的

{ "match": { "content":  "java solution" }},针对doc5,是有一个分数的,2.3

取最大分数,2.3

 然后doc4的分数 = 1.2 < doc5的分数 = 2.3,所以doc5就可以排在更前面的地方,符合我们的需要

 GET /forum/article/_search

{

    "query": {

        "dis_max": {

            "queries": [

                { "match": { "title": "java solution" }},

                { "match": { "content":  "java solution" }}

            ]

        }

    }

}

 id为5的doc就会排在前面去了

 可能在实际场景中出现的一个情况是这样的:

(1)某个帖子,doc1,title中包含java,content不包含java beginner任何一个关键词
(2)某个帖子,doc2,content中包含beginner,title中不包含任何一个关键词
(3)某个帖子,doc3,title中包含java,content中包含beginner
(4)最终搜索,可能出来的结果是,doc1和doc2排在doc3的前面,而不是我们期望的doc3排在最前面

dis_max,只是取分数最高的那个query的分数而已。

2、dis_max只取某一个query最大的分数,完全不考虑其他query的分数

3、使用tie_breaker将其他query的分数也考虑进去

tie_breaker参数的意义,在于说,将其他query的分数,乘以tie_breaker,然后综合与最高分数的那个query的分数,综合在一起进行计算
除了取最高分以外,还会考虑其他的query的分数
tie_breaker的值,在0~1之间,是个小数,就ok

GET /forum/article/_search
{
    "query":{
        "dis_max":{
            "queries":[
                {
                    "match":{
                        "title":"java beginner"
                    }
                },
                {
                    "match":{
                        "body":"java beginner"
                    }
                }
            ],
            "tie_breaker":0.3
        }
    }
}

 

 

原文地址:https://www.cnblogs.com/jiahaoJAVA/p/11028720.html