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

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"关键字的作用类似
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352