zoukankan      html  css  js  c++  java
  • ES 入门

    基于es 5.4和es 5.6,列举的是个人工作中经常用到的查询(只是工作中使用的是Java API),如果需要看完整的,可以参考官方相关文档
    https://www.elastic.co/guide/en/elasticsearch/reference/5.4/search.html。

    1、查询

    先使用一个快速入门来引入,然后后面列出的各种查询都是用得比较多的(在我的工作环境是这样),其它没怎么用的这里就不列出了。

    1.1 快速入门

    1.1.1 查询全部

    GET index/type/_search
    {
        "query":{
            "match_all":{}
        }
    }
    

    GET index/type/_search
    

    1.1.2 分页(以term为例)

    GET index/type/_search
    {
        "from":0,
        "size":100,
        "query":{
            "term":{
                "area":"GuangZhou"
            }
        }
    }
    

    1.1.3 包含指定字段(以term为例)

    GET index/type/_search
    {
        "_source":["hobby", "name"],
        "query":{
            "term":{
                "area":"GuangZhou"
            }
        }
    }
    

    1.1.4 排序(以term为例)

    单个字段排序:

    GET index/type/_search
    {
        "query":{
            "term":{
                "area":"GuangZhou"
            }
        },
        "sort":[
            {"user_id":{"order":"asc"}},
            {"salary":{"order":"desc"}}
        ]
    }
    

    1.2 全文查询

    查询字段会被索引和分析,在执行之前将每个字段的分词器(或搜索分词器)应用于查询字符串。

    1.2.1 match query

    {
      "query": {
        "match": {
          "content": {
            "query": "里皮恒大",
            "operator": "and"
          }
        }
      }
    }
    

    operator默认是or,也就是说,“里皮恒大”被分词为“里皮”和“恒大”,只要content中出现两个之一,都会搜索到;设置为and之后,只有同时出现都会被搜索到。

    1.2.2 match_phrase query

    文档同时满足下面两个条件才会被搜索到:

    • (1)分词后所有词项都要出现在该字段中
    • (2)字段中的词项顺序要一致
    {
      "query": {
        "match_phrase": {
          "content": "里皮恒大"
        }
      }
    }
    

    1.3 词项查询

    词项搜索时对倒排索引中存储的词项进行精确匹配,词项级别的查询通过用于结构化数据,如数字、日期和枚举类型。

    1.3.1 term query

    {
      "query": {
        "term": {
          "postdate": "2015-12-10 00:41:00"
        }
      }
    }
    

    1.3.2 terms query

    term的升级版,如上面查询的postdate字段,可以设置多个。

    {
      "query": {
        "terms": {
          "postdate": [
            "2015-12-10 00:41:00",
            "2016-02-01 01:39:00"
          ]
        }
      }
    }
    

    因为term是精确匹配,所以不要问,[]中的关系怎么设置and?这怎么可能,既然是精确匹配,一个字段也不可能有两个不同的值。

    1.3.3 range query

    匹配某一范围内的数据型、日期类型或者字符串型字段的文档,注意只能查询一个字段,不能作用在多个字段上。

    数值:

    {
      "query": {
        "range": {
          "reply": {
            "gte": 245,
            "lte": 250
          }
        }
      }
    }
    

    支持的操作符如下:

    gt:大于,gte:大于等于,lt:小于,lte:小于等于

    日期:

    {
      "query": {
        "range": {
          "postdate": {
            "gte": "2016-09-01 00:00:00",
            "lte": "2016-09-30 23:59:59",
            "format": "yyyy-MM-dd HH:mm:ss"
          }
        }
      }
    }
    

    format不加也行,如果写的时间格式正确。

    1.3.4 exists query

    返回对应字段中至少有一个非空值的文档,也就是说,该字段有值(待会会说明这个概念)。

    {
      "query": {
        "exists": {
          "field": "user"
        }
      }
    }
    

    参考《从Lucene到Elasticsearch:全文检索实战》中的说明。

    以下文档会匹配上面的查询:

    文档 说明
    {"user":"jane"} 有user字段,且不为空
    {"user":""} 有user字段,值为空字符串
    {"user":"-"} 有user字段,值不为空
    {"user":["jane"]} 有user字段,值不为空
    {"user":["jane",null]} 有user字段,至少一个值不为空即可

    下面的文档不会被匹配:

    文档 说明
    {"user":null} 虽然有user字段,但是值为空
    {"user":[]} 虽然有user字段,但是值为空
    {"user":[null]} 虽然有user字段,但是值为空
    {"foo":"bar"} 没有user字段

    1.3.5 ids query

    查询具有指定id的文档。

    {
      "query": {
        "ids": {
          "type": "news",
          "values": "2101"
        }
      }
    }
    

    类型是可选的,也可以以数据的方式指定多个id。

    {
      "query": {
        "ids": {
          "values": [
            "2101",
            "2301"
          ]
        }
      }
    }
    

    1.4 复合查询

    1.4.1 bool query

    因为工作中接触到关于es是做聚合、统计、分类的项目,经常要做各种复杂的多条件查询,所以实际上,bool query用得非常多,因为查询条件个数不定,所以处理的逻辑思路时,外层用一个大的bool query来进行承载。(当然,项目中是使用其Java API)

    bool query可以组合任意多个简单查询,各个简单查询之间的逻辑表示如下:

    属性 说明
    must 文档必须匹配must选项下的查询条件,相当于逻辑运算的AND
    should 文档可以匹配should选项下的查询条件,也可以不匹配,相当于逻辑运算的OR
    must_not 与must相反,匹配该选项下的查询条件的文档不会被返回
    filter 和must一样,匹配filter选项下的查询条件的文档才会被返回,但是filter不评分,只起到过滤功能

    一个例子如下:

    {
      "query": {
        "bool": {
          "must": {
            "match": {
              "content": "里皮"
            }
          },
          "must_not": {
            "match": {
              "content": "中超"
            }
          }
        }
      }
    }
    

    需要注意的是,同一个bool下,只能有一个must、must_not、should和filter。

    如果希望有多个must时,比如希望同时匹配"里皮"和"中超",但是又故意分开这两个关键词(因为事实上,一个must,然后使用match,并且operator为and就可以达到目的),怎么操作?注意must下使用数组,然后里面多个match对象就可以了:

    {
      "size": 1,
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "content": "里皮"
              }
            },
            {
              "match": {
                "content": "恒大"
              }
            }
          ]
        }
      },
      "sort": [
        {
          "id": {
            "order": "desc"
          }
        }
      ]
    }
    

    当然must下的数组也可以是多个bool查询条件,以进行更加复杂的查询。

    上面的查询等价于:

    {
      "query": {
        "bool": {
          "must": {
            "match": {
              "content": {
                "query": "里皮恒大",
                "operator": "and"
              }
            }
          }
        }
      },
      "sort": [
        {
          "id": {
            "order": "desc"
          }
        }
      ]
    }
    

    1.5 嵌套查询

    先添加下面一个索引:

    PUT /my_index
    {
      "mappings": {
        "my_type": {
          "properties": {
            "user":{
              "type": "nested",
              "properties": {
                "first":{"type":"keyword"},
                "last":{"type":"keyword"}
              }
            },
            "group":{
              "type": "keyword"
            }
          }
        }
      }
    }
    

    添加数据:

    PUT my_index/my_type/1
    {
      "group":"GuangZhou",
      "user":[
        {
          "first":"John",
          "last":"Smith"
        },
        {
          "first":"Alice",
          "last":"White"
        }
      ]
    }
    
    PUT my_index/my_type/2
    {
      "group":"QingYuan",
      "user":[
        {
          "first":"Li",
          "last":"Wang"
        },
        {
          "first":"Yonghao",
          "last":"Ye"
        }
      ]
    }
    

    查询:

    较简单的查询:

    {
      "query": {
        "nested": {
          "path": "user",
          "query": {
            "term": {
              "user.first": "John"
            }
          }
        }
      }
    }
    
    

    较复杂的查询:

    {
      "query": {
        "bool": {
          "must": [
            {"nested": {
              "path": "user",
              "query": {
                "term": {
                  "user.first": {
                    "value": "Li"
                  }
                }
              }
            }},
            {
              "nested": {
                "path": "user",
                "query": {
                  "term": {
                    "user.last": {
                      "value": "Wang"
                    }
                  }
                }
              }
            }
          ]
        }
      }
    }
    
    

    1.6 补充:数组查询与测试

    添加一个索引:

    PUT my_index2
    {
      "mappings": {
        "my_type2":{
          "properties": {
            "message":{
              "type": "text"
            },
            "keywords":{
              "type": "keyword"
            }
          }
        }
      }
    }
    
    

    添加数据:

    PUT /my_index2/my_type/1
    {
      "message":"keywords test1",
      "keywords":["美女","动漫","电影"]
    }
    
    PUT /my_index2/my_type/2
    {
      "message":"keywords test2",
      "keywords":["电影","美妆","广告"]
    }
    
    

    搜索:

    {
      "query": {
        "term": {
          "keywords": "广告"
        }
      }
    }
    
    

    Note1:注意设置字段类型时,keywords设置为keyword,所以使用term查询可以精确匹配,但设置为text,则不一定——如果有添加分词器,则可以搜索到;如果没有,而是使用默认的分词器,只是将其分为一个一个的字,就不会被搜索到。这点尤其需要注意到。

    Note2:对于数组字段,也是可以做桶聚合的,做桶聚合的时候,其每一个值都会作为一个值去进行分组,而不是整个数组进行分组,可以使用上面的进行测试,不过需要注意的是,其字段类型不能为text,否则聚合会失败。

    Note3:所以根据上面的提示,一般纯数组比较适合存放标签类的数据,就像上面的案例一样,同时字段类型设置为keyword,而不是text,搜索时进行精确匹配就好了。

    1.7 滚动查询scroll

    如果一次性要查出来比如10万条数据,那么性能会很差,此时一般会采取用scoll滚动查询,一批一批的查,直到所有数据都查询完处理完(es返回的scrollId,可以理解为是es进行此次查询的操作句柄标识,每发送一次该scrollId,es都会操作一次,或者说循环一次,直到时间窗口到期)。

    使用scoll滚动搜索,可以先搜索一批数据,然后下次再搜索一批数据,以此类推,直到搜索出全部的数据来,scoll搜索会在第一次搜索的时候,保存一个当时的视图快照,之后只会基于该旧的视图快照提供数据搜索,如果这个期间数据变更,是不会让用户看到的,每次发送scroll请求,我们还需要指定一个scoll参数,指定一个时间窗口,每次搜索请求只要在这个时间窗口内能完成就可以了(也就是说,该scrollId只在这个时间窗口内有效,视图快照也是)。

    GET spnews/news/_search?scroll=1m
    {
      "query": {
        "match_all": {}
      },
      "size": 10,
      "_source": ["id"]
    }
    
    GET _search/scroll
    {
      "scroll":"1m",
      "scroll_id":"DnF1ZXJ5VGhlbkZldGNoAwAAAAAAADShFmpBMjJJY2F2U242RFU5UlAzUzA4MWcAAAAAAAA0oBZqQTIySWNhdlNuNkRVOVJQM1MwODFnAAAAAAAANJ8WakEyMkljYXZTbjZEVTlSUDNTMDgxZw=="
    }
    
    

    2、聚合

    2.1 指标聚合

    相当于MySQL的聚合函数。

    max

    {
      "size": 0,
      "aggs": {
        "max_id": {
          "max": {
            "field": "id"
          }
        }
      }
    }
    
    

    size不设置为0,除了返回聚合结果外,还会返回其它所有的数据。

    min

    {
      "size": 0,
      "aggs": {
        "min_id": {
          "min": {
            "field": "id"
          }
        }
      }
    }
    
    

    avg

    {
      "size": 0,
      "aggs": {
        "avg_id": {
          "avg": {
            "field": "id"
          }
        }
      }
    }
    
    

    sum

    {
      "size": 0,
      "aggs": {
        "sum_id": {
          "sum": {
            "field": "id"
          }
        }
      }
    }
    
    

    stats

    {
      "size": 0,
      "aggs": {
        "stats_id": {
          "stats": {
            "field": "id"
          }
        }
      }
    }
    
    

    2.2 桶聚合

    相当于MySQL的group by操作,所以不要尝试对es中text的字段进行桶聚合,否则会失败。

    Terms

    相当于分组查询,根据字段做聚合。

    {
      "size": 0,
      "aggs": {
        "per_count": {
          "terms": {
            "size":100,
            "field": "vtype",
            "min_doc_count":1
          }
        }
      }
    }
    
    

    在桶聚合的过程中还可以进行指标聚合,相当于mysql做group by之后,再做各种max、min、avg、sum、stats之类的:

    {
      "size": 0,
      "aggs": {
        "per_count": {
          "terms": {
            "field": "vtype"
          },
          "aggs": {
            "stats_follower": {
              "stats": {
                "field": "realFollowerCount"
              }
            }
          }
        }
      }
    }
    
    

    Filter

    相当于是MySQL根据where条件过滤出结果,然后再做各种max、min、avg、sum、stats操作。

    {
      "size": 0,
      "aggs": {
        "gender_1_follower": {
          "filter": {
            "term": {
              "gender": 1
            }
          },
          "aggs": {
            "stats_follower": {
              "stats": {
                "field": "realFollowerCount"
              }
            }
          }
        }
      }
    }
    
    

    上面的聚合操作相当于是:查询gender为1的各个指标。

    Filters

    在Filter的基础上,可以查询多个字段各自独立的各个指标,即对每个查询结果分别做指标聚合。

    {
      "size": 0,
      "aggs": {
        "gender_1_2_follower": {
          "filters": {
            "filters": [
              {
                "term": {
                  "gender": 1
                }
              },
              {
                "term": {
                  "gender": 2
                }
              }
            ]
          },
          "aggs": {
            "stats_follower": {
              "stats": {
                "field": "realFollowerCount"
              }
            }
          }
        }
      }
    }
    
    

    Range

    {
      "size": 0,
      "aggs": {
        "follower_ranges": {
          "range": {
            "field": "realFollowerCount",
            "ranges": [
              {
                "to": 500
              },
              {
                "from": 500,
                "to": 1000
              },
              {
                "from": 1000,
                "to": 1500
              },
              {
                "from": "1500",
                "to": 2000
              },
              {
                "from": 2000
              }
            ]
          }
        }
      }
    }
    
    

    to:小于,from:大于等于

    Date Range

    跟上面一个类似的,其实只是字段为日期类型的,然后范围值也是日期。

    Date Histogram Aggregation

    这个功能十分有用,可以根据年月日来对数据进行分类。
    索引下面的文档:

    DELETE my_blog
    
    PUT my_blog
    {
      "mappings": {
        "article":{
          "properties": {
            "title":{"type": "text"},
            "postdate":{
              "type": "date"
              , "format": "yyyy-MM-dd HH:mm:ss"
            }
          }
        }
      }
    }
    
    PUT my_blog/article/1
    {
      "title":"Elasticsearch in Action",
      "postdate":"2014-09-23 23:34:12"
    }
    
    PUT my_blog/article/2
    {
      "title":"Spark in Action",
      "postdate":"2015-09-13 14:12:22"
    }
    
    PUT my_blog/article/3
    {
      "title":"Hadoop in Action",
      "postdate":"2016-08-23 23:12:22"
    }
    
    

    按年对数据进行聚合:

    GET my_blog/article/_search
    {
      "size": 0, 
      "aggs": {
        "agg_year": {
          "date_histogram": {
            "field": "postdate",
            "interval": "year",
            "order": {
              "_key": "asc"
            }
          }
        }
      }
    }
    
    {
      "took": 18,
      "timed_out": false,
      "_shards": {
        "total": 5,
        "successful": 5,
        "failed": 0
      },
      "hits": {
        "total": 3,
        "max_score": 0,
        "hits": []
      },
      "aggregations": {
        "agg_year": {
          "buckets": [
            {
              "key_as_string": "2014-01-01 00:00:00",
              "key": 1388534400000,
              "doc_count": 1
            },
            {
              "key_as_string": "2015-01-01 00:00:00",
              "key": 1420070400000,
              "doc_count": 1
            },
            {
              "key_as_string": "2016-01-01 00:00:00",
              "key": 1451606400000,
              "doc_count": 1
            }
          ]
        }
      }
    }
    
    

    按月对数据进行聚合:

    GET my_blog/article/_search
    {
      "size": 0, 
      "aggs": {
        "agg_year": {
          "date_histogram": {
            "field": "postdate",
            "interval": "month",
            "order": {
              "_key": "asc"
            }
          }
        }
      }
    }
    
    

    这样聚合的话,包含的年份的每一个月的数据都会被分类,不管其是否包含文档。

    按日对数据进行聚合:

    GET my_blog/article/_search
    {
      "size": 0, 
      "aggs": {
        "agg_year": {
          "date_histogram": {
            "field": "postdate",
            "interval": "day",
            "order": {
              "_key": "asc"
            }
          }
        }
      }
    }
    
    

    这样聚合的话,包含的年份的每一个月的每一天的数据都会被分类,不管其是否包含文档。

  • 相关阅读:
    CSS规范
    CSS规范
    CSS规范
    CSS function--(来自网易)
    CSS reset--(来自网易)
    js截取图片上传(仅原理)----闲的无聊了代码就不共享了!写的难看,不好意思给你们看了(囧)
    作业:JavaScript(数组篇-poker)给我的徒弟出个题。。。记得早点写完,然后大家3人可以早点打牌了
    BAT及各大互联网公司2014前端笔试面试题--JavaScript篇(昨天某个群友表示写的简单了点,然后我无情的把他的抄了一遍)
    BAT及各大互联网公司2014前端笔试面试题--Html,Css篇(昨天有个群友表示写的简单了点,然后我无情的把他的抄了一遍)
    本日吐槽!“人傻钱多”的P2P公司是否是程序员的合适选择(群聊天记录的娱乐)
  • 原文地址:https://www.cnblogs.com/geoffreygao/p/12807722.html
Copyright © 2011-2022 走看看