美文网首页
022.基于IT论坛案例学习Elasticsearch(一):F

022.基于IT论坛案例学习Elasticsearch(一):F

作者: CoderJed | 来源:发表于2020-07-17 15:57 被阅读0次

    1. 准备测试数据

    POST /article/_doc/_bulk
    {"index": {"_id": 1}}
    {"articleId": "XHDK-A-1293-#fJ3", "userId": 1, "hidden": false, "postDate": "2017-01-01"}
    {"index": {"_id": 2}}
    {"articleId": "KDKE-B-9947-#kL5", "userId": 1, "hidden": false, "postDate": "2017-01-02"}
    {"index": {"_id": 3}}
    {"articleId": "JODL-X-1937-#pV7", "userId": 2, "hidden": false, "postDate": "2017-01-01"}
    {"index": {"_id": 4}}
    {"articleId": "QQPX-R-3956-#aD8", "userId": 2, "hidden": true, "postDate": "2017-01-02"}
    
    GET /article/_mapping/_doc
    
    {
      "article" : {
        "mappings" : {
          "_doc" : {
            "properties" : {
              "articleId" : {
                "type" : "text",
                "fields" : {
                  # 不分词,最多保留256个字符
                  "keyword" : {
                    "type" : "keyword",
                    "ignore_above" : 256
                  }
                }
              },
              "hidden" : {
                "type" : "boolean"
              },
              "postDate" : {
                "type" : "date"
              },
              "userId" : {
                "type" : "long"
              }
            }
          }
        }
      }
    }
    

    说明:测试数据会随着练习而不断加入新数据

    2. term filter

    输入的搜索文本不分词,直接拿去倒排索引中进行精确匹配

    # 根据用户ID搜索帖子(返回2条结果)
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "term": {
              "userId": 1
            }
          }
        }
      }
    }
    
    # 搜索没有隐藏的帖子(返回3条结果)
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "term": {
              "hidden": false
            }
          }
        }
      }
    }
    
    # 根据发帖日期搜索帖子(返回2条结果)
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "term": {
              "postDate": "2017-01-01"
            }
          }
        }
      }
    }
    
    # 根据帖子ID搜索帖子(无结果)
    # text类型的field,建立倒排索引的时候,就会进行分词
    # 分词以后,原本的articleID就没有了,只有分词后的各个单词存在于倒排索引中(qqpx、r、3956、ad8)
    # term,是不对搜索文本分词的,所有精确匹配是匹配不到的
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "term": {
              "articleId": "QQPX-R-3956-#aD8"
            }
          }
        }
      }
    }
    
    # 根据帖子ID搜索帖子(1条结果)
    # articleID.keyword,是es内置建立的field,就是不分词的。
    # 所以一个articleID过来的时候,会建立两次索引
    # 一次是要分词的,分词后放入倒排索引
    # 另外一次是基于articleID.keyword,不分词,最多保留256个字符最多,直接将字符串本身放入倒排索引中
    # 所以term filter,对text过滤,可以考虑使用内置的field.keyword来进行匹配
    # 但是有个问题,默认就保留256个字符,所以尽可能还是自己去手动建立索引,将type设置为keyword
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "term": {
              "articleId.keyword": "QQPX-R-3956-#aD8"
            }
          }
        }
      }
    }
    
    # 重建索引
    DELETE article
    # 手动指定articleId的类型,这样直接将type设置为keyword,是没有保留字符串的长度限制的
    PUT article
    {
      "mappings": {
        "_doc": {
          "properties": {
            "articleId": {
              "type": "keyword"
            }
          }
        }
      }
    }
    # 重新插入数据
    POST /article/_doc/_bulk
    {"index": {"_id": 1}}
    {"articleId": "XHDK-A-1293-#fJ3", "userId": 1, "hidden": false, "postDate": "2017-01-01"}
    {"index": {"_id": 2}}
    {"articleId": "KDKE-B-9947-#kL5", "userId": 1, "hidden": false, "postDate": "2017-01-02"}
    {"index": {"_id": 3}}
    {"articleId": "JODL-X-1937-#pV7", "userId": 2, "hidden": false, "postDate": "2017-01-01"}
    {"index": {"_id": 4}}
    {"articleId": "QQPX-R-3956-#aD8", "userId": 2, "hidden": true, "postDate": "2017-01-02"}
    
    # 然后直接使用articleId来进行term filter是有返回结果的
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "term": {
              "articleId": "QQPX-R-3956-#aD8"
            }
          }
        }
      }
    }
    

    知识点总结:

    • term filter:对输入的内容进行精确匹配,数字、boolean、date天然支持精确匹配,而text类型的字段则要设置为keyword类型才可以使用term filter

    3. filter执行原理剖析

    查询条件:假设查询"2017-02-02"这个日期,{filter: {term: "2017-02-02"}}且倒排索引中的数据如下,*代表存在于该文档中:

    word doc1 doc2 doc3
    2017-01-01 * *
    2017-02-02 * *
    2017-03-03 * * *
    • 到倒排索引中查询,发现"2017-02-02"对应的document list是doc2,doc3

    • ES为每个在倒排索引中搜索到的结果,构建一个bitset,使用找到的doc list,构建一个bitset,就是一个二进制的数组,数组每个元素都是0或1,用来标识一个doc对一个filter条件是否匹配,如果匹配就是1,不匹配就是0,例如为上述filter创建的bitset为[0, 1, 1],就是代表,doc1不满足filter条件,而doc2和doc3满足filter条件,使用bitset这种简单的数据结构去实现复杂的功能,可以节省内存空间,提升性能

    • 假设一次查询中有多个filter条件,遍历每个filter条件对应的bitset,优先从最稀疏的开始搜索,查找满足所有条件的document,优先从最稀疏的开始遍历,例如[0, 0, 0, 1, 0, 0]就比[1, 0, 1, 0, 0]稀疏,先遍历比较稀疏的bitset,就可以先过滤掉尽可能多的数据,遍历所有的bitset,找到匹配所有filter条件的doc

      假设有两个filter条件,postDate=2017-01-01,userID=1,每个filter的bitset如下:

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

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

      可以看到,同事满足这两个条件的doc为doc4(下标为3),于是返回doc4给客户端

    • bitset的缓存:在最近256个query中超过一定次数的过滤条件,就会缓存其bitset,对于小segment则不缓存bitset

      比如postDate=2017-01-01,bitset为[0, 0, 1, 1, 0, 0],缓存在内存中,这样下次如果再有这个条件的filter的时候,就不用重新扫描倒排索引,而是直接从缓存中获取满足条件的doc list,这样可以大幅度提升性能,在最近的256个filter中,有某个filter被查询超过了一定的次数(次数不固定),就会自动缓存这个filter对应的bitset

      filter针对小segment(一个index由多个segment文件组成)获取到的结果,则不会缓存,小segment是指其文档数<1000,或者它的大小<index总大小的3%,这样的segment数据量很小,哪怕是扫描其全部数据获取结果也很快,segment会在后台自动合并,小segment很快就会跟其他小segment合并成大segment,此时就缓存也没有什么意义了

      filter(只是简单过滤数据,不计算评分也不排序)比query(返回结果进行评分、相关度排序)的优势就在于它的bitset的缓存,使得filter比query的性能更好

    • filter在大部分情况下会在query之前执行,这样可以先过滤掉尽可能多的数据

    • 如果document有新增或修改,那么缓存的bitset会被自动更新

    • 后续只要是相同的filter条件,都会直接使用这个过滤条件的缓存bitset来进行查询

    4. 基于bool组合多个filter条件

    # 搜索发帖日期为2017-01-01,或者帖子ID为XHDK-A-1293-#fJ3的帖子,同时要求帖子的发帖日期绝对不为2017-01-02
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "bool": {
              "should": [
                {"term": {"postDate": "2017-01-01"}},
                {"term": {"articleId": "XHDK-A-1293-#fJ3"}}
              ],
              "must_not": [
                {"term": {"postDate": "2017-01-02"}}
              ]
            }
          }
        }
      }
    }
    
    # must:所有的条件都必须匹配
    # should:其中的条件匹配任意一个即可
    # must_not:所有的条件都必须不匹配
    
    # 搜索帖子ID为XHDK-A-1293-#fJ3,或者是帖子ID为JODL-X-1937-#pV7而且发帖日期为2017-01-01的帖子
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "bool": {
              "should": [
                {"term": {"articleId": "XHDK-A-1293-#fJ3"}},
                {
                  "bool": {
                    "must": [
                      {"term": {"articleId": {"value": "JODL-X-1937-#pV7"}}},
                      {"term": {"postDate": {"value": "2017-01-01"}}}
                    ]
                  }
                }
              ]
            }
          }
        }
      }
    }
    

    知识点总结:

    • bool:must、must_not、should,组合多个过滤条件
    • bool可以嵌套
    • must、must_not、should之间的关系是and的关系

    5. 使用terms搜索多个值

    # 增加tag字段
    POST /article/_doc/_bulk
    {"update": {"_id": "1"}}
    {"doc": {"tag": ["java", "hadoop"]}}
    {"update": {"_id": "2"}}
    {"doc": {"tag": ["java"]}}
    {"update": {"_id": "3"}}
    {"doc": {"tag": ["hadoop"]}}
    {"update": {"_id": "4"}}
    {"doc": {"tag": ["java", "elasticsearch"]}}
    
    # 搜索articleID为KDKE-B-9947-#kL5或QQPX-R-3956-#aD8的帖子
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "terms": {
              "articleId": ["KDKE-B-9947-#kL5", "QQPX-R-3956-#aD8"]
            }
          }
        }
      }
    }
    
    # 搜索tag中包含java的帖子
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "terms": {
              "tag": ["java"]
            }
          }
        }
      }
    }
    
    # 增加一个tag_cnt字段,统计tag的个数
    POST /article/_doc/_bulk
    {"update": {"_id": "1"}}
    {"doc": {"tag_cnt": 2}}
    {"update": {"_id": "2"}}
    {"doc": {"tag_cnt": 1}}
    {"update": {"_id": "3"}}
    {"doc": {"tag_cnt": 1}}
    {"update": {"_id": "4"}}
    {"doc": {"tag_cnt": 2}}
    
    # 搜索tag中只包含java的帖子
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "bool": {
              "must": [
                {"term": {"tag_cnt": 1}},
                {"terms": {"tag": ["java"]}}
              ]
            }
          }
        }
      }
    }
    

    知识点总结:

    • terms:多值搜索

    • terms的功能与SQL中"in"关键字类似

    6. 基于range filter来进行范围过滤

    # 为帖子数据增加浏览量的字段
    POST /article/_doc/_bulk
    {"update": {"_id": "1"} }
    {"doc": {"view_cnt": 30}}
    {"update": {"_id": "2"}}
    {"doc": {"view_cnt": 50}}
    {"update": {"_id": "3"}}
    {"doc": {"view_cnt": 100}}
    {"update": { "_id": "4"}}
    {"doc": {"view_cnt": 80}}
    
    # 搜索浏览量在30~60之间的帖子
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "range": {
              "view_cnt": {
                "gt": 30,
                "lt": 60
              }
            }
          }
        }
      }
    }
    
    # 增加一条测试数据
    PUT /article/_doc/5
    {
      "articleID": "DHJK-B-1395-#Ky5", 
      "userID": 3, 
      "hidden": false, 
      "postDate": "2017-03-01", 
      "tag": ["elasticsearch"], 
      "tag_cnt": 1, 
      "view_cnt": 10 
    }
    
    # 搜索发帖日期在最近1个月的帖子(假设今天是2017-03-10)
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "range": {
              "postDate": {
                "gt": "2017-03-10||-30d"
              }
            }
          }
        }
      }
    }
    # 当天日期可以使用now来获取
    GET /article/_doc/_search
    {
      "query": {
        "constant_score": {
          "filter": {
            "range": {
              "postDate": {
                "gt": "now-30d"
              }
            }
          }
        }
      }
    }
    

    知识点总结:

    • range:范围过滤
    • range与SQL中的"between"关键字的作用类似

    相关文章

      网友评论

          本文标题:022.基于IT论坛案例学习Elasticsearch(一):F

          本文链接:https://www.haomeiwen.com/subject/legghktx.html