ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Elasticsearch的CRUD

2021-06-08 16:02:21  阅读:231  来源: 互联网

标签:search nfc name GET CRUD product Elasticsearch query


在http://192.168.232.128:5601/app/dev_tools#/console连接中进行直接数据操作(安装了Kibana)
没有安装的直接连接也可用postman直接连接ES

新增数据

#相关测测试数据
PUT /product/_doc/1
{
    "name" : "xiaomi phone",
    "desc" :  "shouji zhong de zhandouji",
    "price" :  3999,
    "tags": [ "xingjiabi", "fashao", "buka" ]
}

PUT /product/_doc/2
{
    "name" : "xiaomi nfc phone",
    "desc" :  "zhichi quangongneng nfc,shouji zhong de jianjiji",
    "price" :  4999,
    "tags": [ "xingjiabi", "fashao", "gongjiaoka" ]
}
PUT /product/_doc/3
{
    "name" : "nfc phone",
    "desc" :  "shouji zhong de hongzhaji",
    "price" :  2999,
    "tags": [ "xingjiabi", "fashao", "menjinka" ]
}

PUT /product/_doc/4
{
    "name" : "xiaomi erji",
    "desc" :  "erji zhong de huangmenji",
    "price" :  999,
    "tags": [ "low", "bufangshui", "yinzhicha" ]
}

PUT /product/_doc/5
{
    "name" : "hongmi erji",
    "desc" :  "erji zhong de kendeji",
    "price" :  399,
    "tags": [ "lowbee", "xuhangduan", "zhiliangx" ]
}
PUT /product/_doc/6
{
  "name": "xiaomi 10",
  "desc": "zui neng da de xiaomishouji",
  "price": 5999,
  "tags": ["fashao","xingjiabi"]
}

修改/删除数据

#全量更新id为1的数据
#产生的问题是如果没有全部写上字段,会导致字段数据丢失
PUT /product/_doc/1
{
    "name" : "xiaomi phone",
    "desc" :  "shouji zhong de zhandouji",
    "price" :  3999,
    "tags": [ "xingjiabi", "fashao", "buka" ]
}

#部分字段更新
POST /product/_update/1
{
  "doc":{
    "price":2999
  }
}
#删除数据
#显示逻辑删除,改变version,在一定时间之后会进行物理删除
DELETE /product/_doc/6

获取数据

#查询所有
GET /product/_search

#Timeout机制:假设用户查询结果有1W条数据,但是需要10ms 才能查询完毕,但是用户设置了1ms 的timeout,那么不管当前一共查询到了多少数据,都会在1ms后ES讲停止查询,并返回当前数据。
#GET /_search?timeout=1s/ms/m
GET /product/_search?timeout=1ms

#分页查询 
GET /product/_search?from=0&size=2&sort=price:asc

#带参数查询 name字段包含phone
GET /product/_search?q=name:phone

Query DSL

match all:匹配所有


#获取全部数据
GET /product/_search
{
  "query":{
    "match_all": {}
  }
}


math :包含

#包含nfc数据
GET /product/_search
{
  "query": {
    "match": {
      "name": "nfc"
    }
  }
}


sort:排序

#按照价格排序
GET /product/_search
{
  "query": {
    "multi_match": {
      "query": "nfc",
      "fields": ["name","desc"]
    }
  },
  "sort": [
    {
      "price": "desc"
    }
  ]
}

multi_match:根据多个字段查询一个关键词

#name和descz字段中包含“nfc”的doc
GET /product/_search
{
  "query": {
    "multi_match": {
      "query": "nfc",
      "fields": ["name","desc"]
    }
  },
  "sort": [
    {
      "price": "desc"
    }
  ]
}

_source 元数据:只返回想要的字段

#查询包含nfc的数据,但是只返回“name”和“price”字段。
GET /product/_search
{
  "query":{
    "match": {
      "name": "nfc"
    }
  },
  "_source": ["name","price"]
}

分页(deep-paging)

#查询第一页(每页两条数据)
GET /product/_search
{
  "query":{
    "match_all": {}
  },
  "sort": [
    {
      "price": "asc"
    }
  ], 
  "from": 0,
  "size": 2
}

Full-text queries:全文检索

query-term:不会被分词
GET /product/_search
{
  "query": {
    "term": {
      "name": "nfc"
    }
  }
}

match:会被分词
#这里因为没有分词,所以查询没有结果
GET /product/_search
{
  "query": {
    "term": {
      "name": "nfc phone" 
    }
  }
}

#查找必须包含nfc 或phone字段的数据
GET /product/_search
{
  "query": {
    "bool": {
      "must": [
        {"term":{"name":"nfc"}},
        {"term":{"name":"phone"}}
      ]
    }
  }
}
#和上面相同,用数组传递
GET /product/_search
{
  "query": {
    "terms": {
      "name":["nfc","phone"]
    }
  }
}

# match会自动分词
GET /product/_search
{
  "query": {
    "match": {
      "name": "nfc phone" 
    }
  }
}

全文检索
#查询包含四个单词任意一个的数据
GET /product/_search
{
  "query": {
    "match": {
      "name": "xiaomi nfc zhineng phone"
    }
  }
}
#验证"xiaomi nfc zhineng phone"会被分成几个词
GET /product/_analyze
{
 "analyzer": "standard",
 "text": "xiaomi nfc zhineng phone"
 
}

Phrase search:短语搜索
#“nfc phone”会作为一个短语去检索,不会进行分词
GET /product/_search
{
  "query": {
    "match_phrase": {
      "name": "nfc phone"
    }
  }
}

Query and filter:查询和过滤

bool:可以组合多个查询条件,bool查询也是采用more_matches_is_better的机制,因此满足must和should子句的文档将会合并起来计算分值。

  1. must必须满足子句(查询)

    必须出现在匹配的文档中,并将有助于得分。

  2. filter过滤器 不计算相关度分数,cache

    子句(查询)必须出现在匹配的文档中。但是不像 must查询的分数将被忽略。Filter子句在filter上下文中执行,这意味着计分被忽略,并且子句被考虑用于缓存。

  3. should:可能满足 or

  4. must_not必须不满足 不计算相关度分数

    子句(查询)不得出现在匹配的文档中。子句在过滤器上下文中执行,这意味着计分被忽略,并且子句被视为用于缓存。由于忽略计分,0因此将返回所有文档的分数。

  5. minimum_should_match:最小匹配度

**如果为0.8, 4个词中必须要命中3个词的就会返回**。 (4*0.8向下取整)

```json
#首先筛选name包含“xiaomi phone”并且价格大于1999的数据(不排序),
#然后搜索name包含“xiaomi”and desc 包含“shouji

GET /product/_search
{
  "query": {
    "bool":{
      "must": [
        {"match": { "name": "xiaomi"}},
        {"match": {"desc": "shouji"}}
      ],
      "filter": [
        {"match_phrase":{"name":"xiaomi phone"}},
        {"range": {
          "price": {
            "gt": 1999
          }
        }}
      ]
    }
  }
}

# bool多条件 name包含xiaomi 不包含erji 描述里包不包含nfc都可以,价钱要大于等于4999

GET /product/_search
{
  "query": {
"bool":{
    #name中必须包含“xiaomi”

      "must": [
        {"match": { "name": "xiaomi"}}
      ],
#name中必须不能包含“erji”
      "must_not": [
        {"match": { "name": "erji"}}
      ],
#should中至少满足0个条件,参见下面的minimum_should_match的解释
      "should": [
        {"match": {
          "desc": "nfc"
        }}
      ], 
#筛选价格大于4999的doc
      "filter": [		
        {"range": {
          "price": {
            "gt": 4999   
          }
        }}
      ]
    }
  }
}
#查询必须包含nfc,加个在1999~3999之间,最少命中1个词
GET /product/_search
{
  "query": {
    "bool":{
      "must": [
        {"match": { "name": "nfc"}}
      ],
      "should": [
        {"range": {
          "price": {"gt":1999}
        }},
         {"range": {
          "price": {"gt":3999}
        }}
      ],
      "minimum_should_match": 1
    }
  }
}

```
#高亮返回
GET /product/_search
{
    "query" : {
        "match_phrase" : {
            "name" : "nfc phone"
        }
    },
    "highlight":{
      "fields":{
         "name":{}
      }
    }
}

Compound queries:组合查询

#一台带NFC功能的 或者 小米的手机 但是不要耳机
GET /product/_search
{
  "query": {
    "constant_score":{
      "filter": {
        "bool": {
          "should":[
            {"term":{"name":"xiaomi"}},
            {"term":{"name":"nfc"}}
            ],
          "must_not":[
            {"term":{"name":"erji"}}
            ]
        }
      },
      "boost": 1.2
    }
  }
}
#搜索一台xiaomi nfc phone或者一台满足 是一台手机 并且 价格小于等于2999
GET /product/_search
{
  "query": {
    "constant_score": {
      "filter": { 
        "bool":{
          "should":[
            {"match_phrase":{"name":"xiaomi nfc phone"}},
            {
              "bool":{
                "must":[
                  {"term":{"name":"phone"}},
                  {"range":{"price":{"lte":"2999"}}}
                  ]
              }
            }
          ]
        }
      }
    }
  }
}

Deep paging 深度分页查询

比如一个索引有三个 primary shard,分别存储了6000条数据,我们要得到第100页的数据(每页10条),类似这种情况就叫deep paging。每个 shard 把0到999条数据全部搜索出来(按排序顺序),然后全部返回给 coordinate node,由 coordinate node 按 _score 分数排序后,取出第100页的10条数据,然后返回给客户端。

image-20210524163801851

问题

  1. 消耗网络带宽,因为所搜过深的话,各 shard 要把数据传递给 coordinate node,这个过程是有大量数据传递的,消耗网络。
  2. 消耗内存,各 shard 要把数据传送给 coordinate node,这个传递回来的数据,是被 coordinate node 保存在内存中的,这样会大量消耗内存。
  3. 消耗cup,coordinate node 要把传回来的数据进行排序,这个排序过程很消耗cpu。
    鉴于deep paging的性能问题,所有应尽量减少使用。

解决方案

Elasticsearch 使用scroll滚动技术实现大数据量搜索、深度分页问题 和 search_after 实现深度分页

基于scroll滚动技术实现大数据量搜索

  1. scroll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的
  2. 采用基于_doc(不使用_score)进行排序的方式,性能较高
  3. 每次发送scroll请求,我们还需要指定一个scroll参数,指定一个时间窗口,每次搜索请求只要在这个事件窗口内能完成就可以了
GET /product/info/_search?scroll=2m
{
	"query":{
		"match_all":{
		}
	},
	"sort":["_doc"]
}

# 返回结果
#! [types removal] Specifying types in search requests is deprecated.
{
  "_scroll_id" : "FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFjFKVnZMQ3lvVEw2aXkxaGs1UXN4SUEAAAAAAAASBRY4Nnl2eHRoNlNXU01sTDRXY2JDT0t3",
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  }
}

}# 第二次查询通过第一次的快照ID来查询,后面以此类推
GET /_search/scroll
{
"scroll":"1m",
"scroll_id":"FGluY2x1ZGVfY29udGV4dF91dWlkDXF1ZXJ5QW5kRmV0Y2gBFjFKVnZMQ3lvVEw2aXkxaGs1UXN4SUEAAAAAAAASBRY4Nnl2eHRoNlNXU01sTDRXY2JDT0t3"
}

scroll_id 的生成可以理解为建立了一个临时的历史快照,在此之后的增删改查等操作不会影响到这个快照的结果。

filter执行原理

场景

举个例子,假设有个字段是date类型的,在倒排索引中:

word document1 document2 document3
2017-01-01
2017-02-02
2017-03-03
  1. 在倒排索引中查找搜索串,获取document list

    这时候一个filter查询:2017-02-02,在倒排索引里面找,对应的document list是doc2,doc3

  2. 为每个在倒排索引中搜索到的结果构建一个bitset

    这点非常重要, 使用找到的document list构建一个bitset,一个二进制数组,数组每个元素都是0或1,用来标识一个doc对一个filter条件是否匹配,如果匹配就是1,不匹配就是0.
    上面的例子中,构建的bitset就是[0,1,1]

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

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

    在一个search请求中,可以发出多个filter条件(这个后面再具体说),每个filter会对应一个bitset
    遍历每个filter条件对应的bitset,先从最稀疏的开始遍历.怎么算稀疏呢?
    [0,0,0,1,0,0] – 比较稀疏
    [0,1,0,1,0,1]
    先遍历比较稀疏的bitset,可以过滤掉尽可能多的数据

    比如现在有个请求 filter: postDate=2017-01,userID=1,然后构建的两个bitset分别是:
    [0,0,1,1,0,0]
    [0,1,0,1,0,1]
    遍历玩两个bitset之后,找到匹配所有条件的document,就是第4个,这个时候就可以将符合结果document返回给客户端了

  4. caching bitset 跟踪query

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

    举个例子, 在最近的256次查询中,postDate=2017-02-02这个条件出现超过了一定的次数(不固定), 就会自动缓存这个filter对应的bitset

    filter对于小的segment中获取到的结果可以不缓存,segment中记录数小于1000的和segment大小小于index总大小的3%的

    segment数据量很小的时候,扫描是很快的,而且我们之前有说过,segment会在后台自动合并的,小的segment很快会和其他小的segment合并,此时缓存也就没有什么意义了

  5. 大部分情况下 filter会在query之前执行

    filter先执行可以先过滤掉一部分数据,之前说过query是会计算相关度分数,然后去排序的,而filter是不计算分数,也不排序,所以先执行filter过滤掉尽可能多的数据

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

    举个例子,之前有个filter 过滤条件是postDate=2017-02-02,然后他的bitset是[0,0,0,1]
    这个时候如果新增了一条document进来 postDate也是 2017-02-02,id是5, 那么这个bitset会自动更新为[0,0,0,1,1]
    同理,如果id = 1的document的postDate更新为2017-02-02 那么bitset也会更新为[1,0,0,1,1]

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

标签:search,nfc,name,GET,CRUD,product,Elasticsearch,query
来源: https://www.cnblogs.com/CNRF/p/14863178.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有