ES入门:查询和聚合的基础使用

从索引文档开始

索引一个文档,向某个索引中插入一个文档。

PUT /customer/_doc/1
{
  "name": "John Doe"
}

图1:ES新文档插入图例

ES新文档插入图例

返回结果含义已经在截图中注解出来。

查询刚才插入的文档 GET /customer/_doc/1

输出结果为:

{
    "_index": "customer",
    "_type": "_doc",
    "_id": "1",
    "_version": 1,
    "_seq_no": 0,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "name": "John Doe"
    }
}

学习准备:批量索引文档ES

ES 还提供了批量操作,比如这里我们可以使用批量操作来插入一些数据,供我们在后面学习使用。

使用批量来批处理文档操作比单独提交请求要快得多,因为它减少了网络往返。

  • 下载测试数据
    数据是index为bank,accounts.json,下载地址

数据的格式如下:

{
  "account_number": 0,
  "balance": 16623,
  "firstname": "Bradshaw",
  "lastname": "Mckenzie",
  "age": 29,
  "gender": "F",
  "address": "244 Columbus Place",
  "employer": "Euron",
  "email": "bradshawmckenzie@euron.com",
  "city": "Hobucken",
  "state": "CO"
}
  • 批量插入数据
    accounts.json拷贝至指定目录,我这里放在/opt/下面, 然后执行
    curl -v 'localhost:9200/bank/_bulk?format=yaml' -H 'Content-type: application/x-ndjson' --data-binary accounts.json

注意地址,替换里面路径。 如果按照上面的操作会遇到如下错误: 与ES版本有关系。

error:
  root_cause:
  - type: "illegal_argument_exception"
    reason: "The bulk request must be terminated by a newline [\\n]"
  type: "illegal_argument_exception"
  reason: "The bulk request must be terminated by a newline [\\n]"
status: 400

解决上面的办法可以通过 postman 上传来解决。 插入成功后会返回执行的结果,返回结果是yaml格式的数据:

took: 526
errors: false
items:
- index:
    _index: "bank"
    _type: "_doc"
    _id: "1"
    _version: 1
    result: "created"
    _shards:
      total: 2
      successful: 1
      failed: 0
    _seq_no: 0
    _primary_term: 1
    status: 201

查询数据

match_all: 表示查询所有数据,sort 表示字段排序。

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ]
}

返回结果如下:

{
    "took": 26,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": {
            "value": 1000,
            "relation": "eq"
        },
        "max_score": null,
        "hits": [
            {
                "_index": "bank",
                "_type": "_doc",
                "_id": "0",
                "_score": null,
                "_source": {
                    "account_number": 0,
                    "balance": 16623,
                    "firstname": "Bradshaw",
                    "lastname": "Mckenzie",
                    "age": 29,
                    "gender": "F",
                    "address": "244 Columbus Place",
                    "employer": "Euron",
                    "email": "bradshawmckenzie@euron.com",
                    "city": "Hobucken",
                    "state": "CO"
                },
                "sort": [
                    0
                ]
            },
            ... // 这里还有很多条
        ]
    }
}

相关字段解释

  • took – Elasticsearch运行查询所花费的时间(以毫秒为单位)
  • timed_out – 搜索请求是否超时
  • _shards - 搜索了多少个碎片,以及成功,失败或跳过了多少个碎片的细目分类。
  • max_score – 找到的最相关文档的分数
  • hits.total.value 找到了多少个匹配的文档
  • hits.sort 文档的排序位置(不按相关性得分排序时)
  • hits._score 文档的相关性得分(使用match_all时不适用)

分页查询(from+size)

本质上就是from和size两个字段

GET /bank/_search
{
  "query": { "match_all": {} },
  "sort": [
    { "account_number": "asc" }
  ],
  "from": 10,
  "size": 10
}

指定字段查询:match

如果要在字段中搜索特定字词,可以使用match;

如下语句将查询address 字段中包含 mill 或者 lane的数据

GET /bank/_search
{
  "query": { "match": { "address": "mill lane" } }
}

(由于ES底层是按照分词索引的,所以上述查询结果是address 字段中包含 mill 或者 lane的数据)

查询段落匹配:match_phrase

如果我们希望查询的条件是 address字段中包含 "mill lane",则可以使用match_phrase

GET /bank/_search
{
  "query": { "match_phrase": { "address": "mill lane" } }
}

多条件查询: bool

如果要构造更复杂的查询,可以使用bool查询来组合多个查询条件。

例如,以下请求在bank索引中搜索40岁客户的帐户,但不包括居住在爱达荷州(ID)的任何人

GET /bank/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}

must, should, must_notfilter 都是bool查询的子句。

那么filter和上述query子句有啥区别呢?

查询条件:query or filter

先看下如下查询, 在bool查询的子句中同时具备query/mustfilter

{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "state": "ND"
                    }
                }
            ],
            "filter": [
                {
                    "term": {
                        "age": "40"
                    }
                },
                {
                    "range": {
                        "balance": {
                            "gte": 20000,
                            "lte": 30000
                        }
                    }
                }
            ]
        }
    }
}

两者都可以写查询条件,而且语法也类似。区别在于:

  • query 上下文的条件是用来给文档打分的,匹配越好 _score 越高;
  • filter 的条件只产生两种结果:符合与不符合,后者被过滤掉。

所以,我们进一步看只包含filter的查询

{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "age": "40"
          }
        },
        {
          "range": {
            "balance": {
              "gte": 20000,
              "lte": 30000
            }
          }
        }
      ]
    }
  }
}

结果,显然无_score

聚合查询:Aggregation

我们知道SQL中有group by,在ES中它叫Aggregation,即聚合运算。

比如我们希望计算出account每个州的统计数量, 使用aggs关键字对state字段聚合,被聚合的字段无需对分词统计,所以使用state.keyword对整个字段统计。

{
    "size": 0,
    "aggs": {
        "group_by_state": {
            "terms": {
                "field": "state.keyword"
            }
        }
    }
}

因为无需返回条件的具体数据, 所以设置size=0,返回hits为空。doc_count表示bucket中每个州的数据条数。

嵌套聚合

ES还可以处理个聚合条件的嵌套。

比如承接上个例子, 计算每个州的平均结余。涉及到的就是在对state分组的基础上,嵌套计算avg(balance):

{
    "size": 0,
    "aggs": {
        "group_by_state": {
            "terms": {
                "field": "state.keyword"
            },
            "aggs": {
                "average_balance": {
                    "avg": {
                        "field": "balance"
                    }
                }
            }
        }
    }
}

对聚合结果排序

可以通过在aggs中对嵌套聚合的结果进行排序

比如承接上个例子, 对嵌套计算出的avg(balance),这里是average_balance,进行排序。

{
  "size": 0,
  "aggs": {
    "group_by_state": {
      "terms": {
        "field": "state.keyword",
        "order": {
          "average_balance": "desc"
        }
      },
      "aggs": {
        "average_balance": {
          "avg": {
            "field": "balance"
          }
        }
      }
    }
  }
}
关于我
loading