前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >022.基于IT论坛案例学习Elasticsearch(一):Filter相关知识

022.基于IT论坛案例学习Elasticsearch(一):Filter相关知识

作者头像
CoderJed
发布2020-07-21 10:08:17
4350
发布2020-07-21 10:08:17
举报

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"关键字的作用类似
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 准备测试数据
  • 2. term filter
  • 3. filter执行原理剖析
  • 4. 基于bool组合多个filter条件
  • 5. 使用terms搜索多个值
  • 6. 基于range filter来进行范围过滤
相关产品与服务
Elasticsearch Service
腾讯云 Elasticsearch Service(ES)是云端全托管海量数据检索分析服务,拥有高性能自研内核,集成X-Pack。ES 支持通过自治索引、存算分离、集群巡检等特性轻松管理集群,也支持免运维、自动弹性、按需使用的 Serverless 模式。使用 ES 您可以高效构建信息检索、日志分析、运维监控等服务,它独特的向量检索还可助您构建基于语义、图像的AI深度应用。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档