elasticsearch要点及常用查询
查询与过滤
明确查询和过滤各自的优缺点,以及适用场景。
过滤语句会询问每个文档的字段值是否包含着特定值,查询语句会询问每个文档的字段值与特定值的匹配程度如何。这一点较好区分,一条查询语句会计算每个文档与查询语句的相关性,会给出一个相关性评分 _score,并且 按照相关性对匹配到的文档进行排序。 这种评分方式非常适用于一个没有完全配置结果的全文本搜索。
性能上的差异
使用过滤语句得到的结果集 -- 一个简单的文档列表,快速匹配运算并存入内存是十分方便的, 每个文档仅需要1个字节。这些缓存的过滤结果集与后续请求的结合使用是非常高效的。
查询语句不仅要查找相匹配的文档,还需要计算每个文档的相关性,所以一般来说查询语句要比 过滤语句更耗时,并且查询结果也不可缓存。
幸亏有了倒排索引,一个只匹配少量文档的简单查询语句在百万级文档中的查询效率会与一条经过缓存 的过滤语句旗鼓相当,甚至略占上风。 但是一般情况下,一条经过缓存的过滤查询要远胜一条查询语句的执行效率。
过滤语句的目的就是缩小匹配的文档结果集,所以需要仔细检查过滤条件。
适用场景
原则上来说,使用查询语句做全文本搜索或其他需要进行相关性评分的时候,剩下的全部用过滤语句
1.kibana 中操作es-查询
Elasticsearch采用Rest风格API,其API就是一次http请求,你可以用任何工具发起http请求
创建索引的请求格式:
请求方式:PUT/GET/POST/DELETE等
请求路径:/索引库名
-
请求参数:json格式:
{
"settings": {
"属性名": "属性值"
}
}settings:就是索引库设置,其中可以定义索引库的各种属性
Mapping映射基础
参考mapping
参考[elasticsearch中的mapping简介]**
映射是创建索引的时候,可以预先定义字段的类型以及相关属性
Elasticsearch会根据JSON源数据的基础类型去猜测你想要的字段映射。将输入的数据变成可搜索的索引项。Mapping就是我们自己定义字段的数据类型,同时告诉Elasticsearch如何索引数据以及是否可以被搜索。
作用:会让索引建立的更加细致和完善
类型:静态映射和动态映射
静态映射
创建索引时具体定义字段类型的映射称之为静态映射或显示映射(Explicit mapping)。
动态映射
什么是动态映射
文档中碰到一个以前没见过的字段时,动态映射可以自动决定该字段的类型,并对该字段添加映射
如何配置动态映射
- 通过dynamic属性进行控制
- true:默认值,动态添加字段; false:忽略新字段; strict:碰到陌生字段,抛出异常
适用范围
适用在根对象上或者object类型的任意字段上
内置类型
string类型: text,keyword(string类型在es5已经被弃用)
text
1:支持分词,全文检索,支持模糊、精确查询,不支持聚合,排序操作;
2:test类型的最大支持的字符长度无限制,适合大字段存储;
使用场景:
存储全文搜索数据, 例如: 邮箱内容、地址、代码块、博客文章内容等。
默认结合standard analyzer(标准解析器)对文本进行分词、倒排索引。
默认结合标准分析器进行词命中、词频相关度打分。
keyword
1:不进行分词,直接索引,支持模糊、支持精确匹配,支持聚合、排序操作。
2:keyword类型的最大支持的长度为——32766个UTF-8类型的字符,可以通过设置ignore_above指定自持字符长度,超过给定长度后的数据将不被索引,无法通过term精确匹配检索返回结果。
使用场景:
存储邮箱号码、url、name、title,手机号码、主机名、状态码、邮政编码、标签、年龄、性别等数据。
用于筛选数据(例如: select * from x where status='open')、排序、聚合(统计)。
直接将完整的文本保存到倒排索引中。
数字类型:long, integer, short, byte, double, float
日期类型: date
bool类型: boolean
binary类型: binary
复杂类型: object ,nested
geo类型: point , geo-shape
专业类型: ip, competion
mapping 限制的type
mapping操作
创建mapping
PUT lagou/info
{
"mappings": {
"job":{ // doc_type
"properties": { // 具体 field
"title":{ //title 字段
"type":"text" //类型设置,text类型可分词
},
"salary_min":{
"type":"integer"
},
"city":{
"type": "keyword" //keyword关键词,不可分词
},
"company":{ //该字段下有多个键值对
"properties": {
"name":{
"type":"text"
},
"company_addr":{
"type":"text"
},
"employee_count":{
"type":"integer"
}
}
},
"publish_date":{
"type":"date",
"format": "yyyy-MM-dd"
},
"comments":{
"type": "integer"
}
}
}
}
}
另一种实现
// 另外一种模板实现
PUT /索引库名/_mapping/类型名称
{
"properties": {
"字段名": {
"type": "类型",
"index": true,
"store": true,
"analyzer": "分词器"
}
}
}
类型名称:就是前面将的type的概念,类似于数据库中的表字段名:任意填写,下面指定许多属性,例如:默认值可以通过查看@Field注解
type:类型,可以是text、keyword、long、short、date、integer、object等
index:是否索引,默认为true
store:是否存储,默认为false
analyzer:分词器,这里的ik_max_word即使用ik分词器
// 发起请求:
PUT hema/_mapping/goods
{
"properties": {
"title": {
"type": "text",
"analyzer": "ik_max_word"
},
"images": {
"type": "keyword",
"index": "false"
},
"price": {
"type": "float"
}
}
}
创建索引
#当类型不符合的时候他会尝试去转换类型,比如字符串的数字和数字类型,但是如果无法转换就会报错。
PUT lagou/job/1
{
"title":"python分布式web开发",
"salary_min":15000,
"city":"北京",
"company":{
"name":"百度",
"company_addr":"软件园",
"employee_count":50
},
"publish_date":"2017-4-16",
"comments":15
}
获取映射
GET lagou/_mapping
GET lagou/_mapping/job
GET _all/_mapping/job
1.1 增删改查操作
首先灌入数据
新增 (如果使用POST test001/doc不带1,系统会每次自己生产一个_id)
// 单条新增
PUT/POST test001/doc/1 # 原数据不存在则创建新的,有的话则update更新,POST创建,PUT更新,需指定id
{
"user":"zhangsan",
"age":20,
"city":"深圳"
}
// 批量新增
// 批量新增: 第一行表示操作,第二行表示数据内容,注意数据内容需要在一行,不能跨行,否则会新增不成功
POST _bulk
{"index":{"_index":"test002","_type":"doc"}}
{"user":"zhangsan", "age":30,"message":"happy birthday","city":"北京","location":{"jd":12,"wd":34}}
{"index":{"_index":"test002","_type":"doc"}}
{"user":"lisi", "age":30,"message":"happy birthday","city":"上海","location":{"jd":12,"wd":34}}
{"index":{"_index":"test002","_type":"doc"}}
{"user":"wangwu", "age":35,"message":"Happy birthday","city":"深圳","location":{"jd":12,"wd":34}}
{"index":{"_index":"test002","_type":"doc"}}
{"user":"zhaoliu", "age":40,"message":"birthday happy","city":"深圳","location":{"jd":12,"wd":34}}
更新
put test001/doc/1
{
"user":"zhangsan",
"age":18,
"city":"sz",
"location":{
"jd":12,
"wd":34
}
}
删除
// 删除单个
DELETE index_name/doc_type_name/id_index
// 删除全部
DELETE index_name
1.1.1 查询
参考
// 查询单个
GET test001/doc/1
// 查询所有
GET test001/_search
1.1.1.1 基本查询
基本语法
GET /索引库名/_search
{
"query":{
"查询类型":{
"查询条件":"查询条件值"
}
}
}
query代表一个查询对象,里面可以有不同的查询属性
- 查询类型:
- 例如:
match_all
,match
,term
,range
等等
- 例如:
- 查询条件:查询条件会根据类型的不同,写法也有差异,后面详细讲解
1.1.1.1.1 查询所有 match-all
- took:查询花费时间,单位是毫秒
- time_out:是否超时
- _shards:分片信息
- hits:搜索结果总览对象
- total:搜索到的总条数
- max_score:所有结果中文档得分的最高分
- hits:搜索结果的文档对象数组,每个元素是一条搜索到的文档信息
- _index:索引库
- _type:文档类型
- _id:文档id
- _score:文档得分
- _source:文档的源数据
示例
GET /hema/_search
{
"query":{
"match_all": {}
}
}
// query:代表查询对象
// match_all:代表查询所有
// phrase search(短语搜索)
GET /ecommerce/product/_search
{
"query" : {
"match_phrase" : {
"producer" : "yagao producer"
}
}
}
1.1.1.1.2 匹配查询 match
全文检索
示例
GET /hema/_search
{
"query":{
"match":{
"title":"小米电视"
}
}
}
// 返回值按max_score,倒排
P.S.
- or关系
match
类型查询,会把查询条件进行分词,然后进行查询,多个词条之间是or的关系
- and关系
某些情况下,我们需要更精确查找,我们希望这个关系变成and
,可以这样做:
GET hema/goods/_search
{
"query":{
"match":{
"title":{"query":"小米电视","operator":"and"}
}
}
}
1.1.1.1.3 词条匹配-term (Filter) **重点
filter是不计算相关性的,同时可以cache。因此,filter速度要快于query。
term
查询被用于精确值 匹配,这些精确值可能是数字、时间、布尔或者那些未分词的字符串
示例
GET /hema/_search
{
"query":{
"term":{
"price":2699.00
}
}
}
// terms 查询字段含有多个关键词的文档
GET student/_search
{
"query":{
"terms":{
"address":["香港","北京"]
}
}
}
1.1.1.1.4 布尔查询-bool (Filter) **重点
bool
把各种其它查询通过must
(与)、must_not
(非)、should
(或)的方式进行组合
must和should连用时,should失效,解决方案,
1.must_not 取反
**2.must改should语句,即a&b or c/d改 a&b&c or a&b&d **
3.把should并列的条件整合到一个bool查询,然后嵌套到must之中去
示例
GET /hema/_search
{
"query":{
"bool":{
"must": { "match": { "title": "大米" }},
"must_not": { "match": { "title": "电视" }},
"should": { "match": { "title": "手机" }}
}
}
}
多重布尔
( (A&B) or C) & !D
// DSL
GET employees/_search
{
"query" : {
"bool" : {
"must": [
{
"term" : {
"gender": "male"
}
},
{
"range" : {
"age" : {
"gt" : 20
}
}
}
],
"should": [
{
"term":
{
"job" : "QA"
}
}
],
"must_not": [
{"range": {
"salary": {
"lte": 9000
}
}}
]
}
}
}
// 返回结果
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 5,
"max_score" : 1.9808292,
"hits" : [
{
"_index" : "employees",
"_type" : "employee_info",
"_id" : "2",
"_score" : 1.9808292,
"_source" : {
"name" : "Underwood",
"age" : 41,
"job" : "Dev Manager",
"gender" : "male",
"salary" : 50000
}
},
{
"_index" : "employees",
"_type" : "employee_info",
"_id" : "7",
"_score" : 1.6931472,
"_source" : {
"name" : "Byrd",
"age" : 27,
"job" : "QA",
"gender" : "male",
"salary" : 20000
}
},
{
"_index" : "employees",
"_type" : "employee_info",
"_id" : "8",
"_score" : 1.3566749,
"_source" : {
"name" : "Foster",
"age" : 27,
"job" : "Java Programmer",
"gender" : "male",
"salary" : 20000
}
},
{
"_index" : "employees",
"_type" : "employee_info",
"_id" : "9",
"_score" : 1.3566749,
"_source" : {
"name" : "Gregory",
"age" : 32,
"job" : "Java Programmer",
"gender" : "male",
"salary" : 22000
}
},
{
"_index" : "employees",
"_type" : "employee_info",
"_id" : "3",
"_score" : 1.287682,
"_source" : {
"name" : "Tran",
"age" : 25,
"job" : "Web Designer",
"gender" : "male",
"salary" : 18000
}
}
]
}
}
1.1.1.1.5 范围查询-range (Filter) **重点
range
查询找出那些落在指定区间内的数字或者时间
示例
GET /hema/_search
{
"query":{
"range": {
"price": {
"gte": 1000.0,
"lt": 2800.00
}
}
}
}
range
查询允许以下字符:
操作符 | 说明 |
---|---|
gt | 大于 |
gte | 大于等于 |
lt | 小于 |
lte | 小于等于 |
ip_range查询
1.1.1.1.6 正则匹配查询-regex
正则搜索会扫描全表,性能较差,wildcard-?会匹配任意字符0/1,*会匹配0个或多个字符。性能和prefix一样差,必须要扫描整个倒排索引,prefix的匹配一般是处理不分词的场景,prefix不会计算revelance score,只是作一个过滤的操作,和filter唯一的区别是filter会缓存结果,而prefix不会。前缀越短要处理的doc越多,性能越差
示例
GET /forum/article/_search
{
"query": {
"regexp":{
"content":"thi[n]."
}
}
}
拓展
1. . >>> 任意字符
2. * >>> 0/多次
3. ?>>> 0/1次
4. + >>> 1/多次
5. [abc] >>> 出现a/b/c一次
6. [^a] >>> 除a外
7. {m,n} >>> 至少m次,至多n次
1.1.1.2 结果过滤**
默认情况下,elasticsearch在搜索的结果中,会把文档中保存在_source
的所有字段都返回。
如果只想获取其中的部分字段,可以添加_source
的过滤
1.1.1.2.1 直接指定字段-_source
示例
GET /hema/_search
{
"_source": ["title","price"],
"query": {
"term": {
"price": 2699
}
}
}
1.1.1.2.2 指定includes和excludes(包含、排除)
示例
GET /hema/_search
{
"_source": {
"includes":["title","price"]
},
"query": {
"term": {
"price": 2699
}
}
}
// 另一种实现
GET /hema/_search
{
"_source": {
"excludes": ["images"]
},
"query": {
"term": {
"price": 2699
}
}
}
1.1.1.3 组合查询-排序-sort
sort
可以让我们按照不同的字段进行排序,并且通过order
指定排序的方式
GET /hema/_search
{
"query": {
"match": {
"title": "小米手机"
}
},
"sort": [
{"price": { "order": "desc"}
}
]
}
1.1.1.4 组合查询-分页-paginator
elasticsearch的分页与mysql数据库非常相似,都是指定两个值:
- from:开始位置 从0开始
- size:每页大小
GET /hema/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"price": {
"order": "asc"
}
}
],
"from": 3,
"size": 3
}
1.1.1.5 组合查询-高亮-highlight
高亮原理:
- 服务端搜索数据,得到搜索结果
- 把搜索结果中,搜索关键字都加上约定好的标签
- 前端页面提前写好标签的CSS样式,即可高亮
在使用match查询的同时,加上一个highlight属性:
- pre_tags:前置标签
- post_tags:后置标签
- fields:需要高亮的字段
- title:这里声明title字段需要高亮,后面可以为这个字段设置特有配置,也可以空
GET /hema/_search
{
"query": {
"match": {
"title": "手机"
}
},
"highlight": {
"pre_tags": "<em>",
"post_tags": "</em>",
"fields": {
"title": {}
}
}
}
1.1.1.6 聚合查询-aggregations **
聚合函数的使用,一定是先查出结果,然后对结果使用聚合函数做处理
聚合可以实现对数据的统计、分析。例如:
- 什么品牌的手机最受欢迎?
- 这些手机的平均价格、最高价格、最低价格?
- 这些手机每月的销售情况如何?
实现这些统计功能的比数据库的sql要方便的多,而且查询速度非常快,可以实现近实时搜索效果。
Elasticsearch中的聚合,包含多种类型,最常用的两种,一个叫桶
,一个叫度量
:
桶(bucket)- 类似sql中的group by语法
桶的作用,是按照某种方式对数据进行分组,每一组数据在ES中称为一个桶
,例如我们根据国籍对人划分,可以得到中国桶
、英国桶
,日本桶
……或者我们按照年龄段对人进行划分:010,1020,2030,3040等。
Elasticsearch中提供的划分桶的方式有很多:
- Date Histogram Aggregation:根据日期阶梯分组,例如给定阶梯为周,会自动每周分为一组
- Histogram Aggregation:根据数值阶梯分组,与日期类似,需要知道分组的间隔(interval)
- Terms Aggregation:根据词条内容分组,词条内容完全匹配的为一组
- Range Aggregation:数值和日期的范围分组,指定开始和结束,然后按段分组
- ……
综上所述,我们发现bucket aggregations 只负责对数据进行分组,并不进行计算,因此往往bucket中往往会嵌套另一种聚合:metrics aggregations即度量
度量(metrics)- 指标
分组完成以后,我们一般会对组中的数据进行聚合运算,例如求平均值、最大、最小、求和等,这些在ES中称为度量
比较常用的一些度量聚合方式:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同时返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前几
- Value Count Aggregation:求总数
- ……
注意:在ES中,需要进行聚合、排序、过滤的字段其处理方式比较特殊,因此不能被分词,必须使用keyword
或数值类型
。
单值分析,只输出一个结果
max,min,avg,sum,cardinality
多值分析,输出多个分析结果
参考聚合查询
指标聚合-metrics
Avg(平均值)**
计算从聚合文档中提取的数值的平均值。
POST /employees/_search?size=0
{
"aggs" : {
"avg_salary" : {
"avg" : {
"field" : "salary"
}
}
}
}
Max(最大值)
计算从聚合文档中提取的数值的最大值。
POST /employees/_search?size=0
{
"aggs" : {
"max_salary" : {
"max" : {
"field" : "salary"
}
}
}
}
Min(最小值)
计算从聚合文档中提取的数值的最小值。
POST /employees/_search?size=0
{
"aggs" : {
"min_salary" : {
"min" : {
"field" : "salary"
}
}
}
}
Sum(总和)
计算从聚合文档中提取的数值的总和。
POST /employees/_search?size=0
{
"aggs" : {
"sum_salary" : {
"sum" : {
"field" : "salary"
}
}
}
}
Cadinality(唯一)
计算从聚合文档中提取的数值的唯一值。
cardinality 求唯一值,即不重复的字段有多少(相当于mysql中的distinct)
POST /employees/_search?size=0
{
"aggs" : {
"type_count" : {
"cardinality" : {
"field" : "salary"
}
}
}
}
// 返回薪资不同的数量
Stats
stats 统计,请求后会直接显示多种聚合结果
POST /employees/_search?size=0
{
"aggs" : {
"salary_stats" : {
"stats" : {
"field" : "salary"
}
}
}
}
桶聚合 bucket **重点-terms,filter,range,date
参考-官方文档 V6.7.2
POST employees/_search?size=0
{
"aggs" : {
"salary_group" : {
"range" : {
"field" : "salary",
"ranges" : [
{
"from" : 8000,
"to" : 15000
},
{
"from" : 15000,
"to" : 25000
},
{
"from" : 25000,
"to" : 100000
}]
},
"aggs" :{
"salary_avg" : {
"avg" : {
"field": "salary"
}
}
}
}
}
}
统计某字段个数
使用aggs和terms,类似group by分组
GET test003/_search
{
"size": 0,
"aggs": {
"city": {
"terms": {
"field": "city.keyword",
"size": 10
}
}
}
}
terms aggs
示例1. 根据品牌分桶
GET cars/_search?size=0
{
"aggs" : {
"genres" : {
"terms" : { "field" : "brand" }
}
}
}
示例2. 分桶后显示数量前3的桶
GET cars/_search?size=0
{
"aggs" : {
"cars" : {
"terms" : {
"field" : "brand",
"size" : 3
}
}
}
}
示例3. 分桶后排序
GET cars/_search?size=0
{
"aggs" : {
"genres" : {
"terms" : {
"field" : "brand",
"order" : { "_count" : "asc" }
}
}
}
}
示例4.
filter aggs
示例1. 过滤获取特定品牌的桶,并求该桶的平均值
GET /cars/_search?size=0
{
"aggs" : {
"brands" : {
"filter" : { "term": { "brand": "BMW" } },
"aggs" : {
"avg_price" : { "avg" : { "field" : "price" } }
}
}
}
}
示例2.多个品牌分桶-多个特定值进行聚合
GET /cars/_search?size=0
{
"size": 0,
"aggs" : {
"cars" : {
"filters" : {
"filters" : {
"colorBucket" : { "match" : { "color" : "red" }},
"brandBucket" : { "match" : { "brand" : "Audi" }}
}
}
}
}
}
histogram aggs
指定间隔对field进行分组
示例1. 按价格区间分组
GET /cars/_search?size=0
{
"aggs" : {
"prices" : {
"histogram" : {
"field" : "price",
"interval" : 10000
// "min_doc_count" : 1 如果文档数量0,不显示该桶
}
}
}
}
range aggs
根据用户传递的范围参数作为桶,进行相应的聚合。在同一个请求中,可以传递多组范围,每组范围作为一个桶。
示例1. 根据价格区间分桶
GET /cars/_search?size=0
{
"aggs" : {
"price_ranges" : {
"range" : {
"field" : "price",
"ranges" : [
{ "to" : 50000 }, // 可以通过加入"key"指定分组名
{ "from" : 5000, "to" : 80000 },
{ "from" : 80000 }
]
}
}
}
}
date aggs
对时间格式进行分组,类似histogram
示例1.
POST /cars/_search?size=0
{
"aggs" : {
"sales_over_time" : {
"date_histogram" : {
"field" : "sellTime",
"interval" : "1M",
"format" : "yyyy-MM-dd"
}
}
}
}
示例2.指定时间区间分桶------->>>>> 基本特性和range aggs一致
POST /cars/_search?size=0
{
"aggs": {
"range": {
"date_range": {
"field": "sellTime",
"format": "MM-yyyy",
"ranges": [
{ "to": "now-10M/M" },
{ "from": "now-10M/M" }
]
}
}
}
}
批量操作数据
// 操作数据1-新增数据
POST /hema/goods/1
{
"title":"小米手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":2899.00
}
POST /hema/goods/2
{
"title":"大米手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":2699.00
}
POST /hema/goods/3
{
"title":"超米手机",
"images":"http://image.leyou.com/12479122.jpg",
"price":3699.00
}
// 操作数据2-查询操作
POST /car/orders/_bulk
{ "index": {"_id":"1"}}
{ "price" : 10000, "color" : "红", "make" : "本田", "sold" : "2014-10-28" }
{ "index": {"_id":"2"}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {"_id":"3"}}
{ "price" : 30000, "color" : "绿", "make" : "福特", "sold" : "2014-05-18" }
{ "index": {"_id":"4"}}
{ "price" : 15000, "color" : "蓝", "make" : "丰田", "sold" : "2014-07-02" }
{ "index": {"_id":"5"}}
{ "price" : 12000, "color" : "绿", "make" : "丰田", "sold" : "2014-08-19" }
{ "index": {"_id":"6"}}
{ "price" : 20000, "color" : "红", "make" : "本田", "sold" : "2014-11-05" }
{ "index": {"_id":"7"}}
{ "price" : 80000, "color" : "红", "make" : "宝马", "sold" : "2014-01-01" }
{ "index": {"_id":"8"}}
{ "price" : 25000, "color" : "蓝", "make" : "福特", "sold" : "2014-02-12" }
// 操作数据3-聚合操作
PUT /employees/employee_info/_bulk
{ "index" : { "_id" : "1" } }
{ "name" : "Emma","age":32,"job":"Product Manager","gender":"female","salary":35000 }
{ "index" : { "_id" : "2" } }
{ "name" : "Underwood","age":41,"job":"Dev Manager","gender":"male","salary": 50000}
{ "index" : { "_id" : "3" } }
{ "name" : "Tran","age":25,"job":"Web Designer","gender":"male","salary":18000 }
{ "index" : { "_id" : "4" } }
{ "name" : "Rivera","age":26,"job":"Web Designer","gender":"female","salary": 22000}
{ "index" : { "_id" : "5" } }
{ "name" : "Rose","age":25,"job":"QA","gender":"female","salary":18000 }
{ "index" : { "_id" : "6" } }
{ "name" : "Lucy","age":31,"job":"QA","gender":"female","salary": 25000}
{ "index" : { "_id" : "7" } }
{ "name" : "Byrd","age":27,"job":"QA","gender":"male","salary":20000 }
{ "index" : { "_id" : "8" } }
{ "name" : "Foster","age":27,"job":"Java Programmer","gender":"male","salary": 20000}
{ "index" : { "_id" : "9" } }
{ "name" : "Gregory","age":32,"job":"Java Programmer","gender":"male","salary":22000 }
{ "index" : { "_id" : "10" } }
{ "name" : "Bryant","age":20,"job":"Java Programmer","gender":"male","salary": 9000}
// 操作数据4-聚合操作之分组
POST _bulk
{"index":{"_index":"test003","_type":"doc"}}
{"user":"zhangsan", "age":30,"message":"happy birthday","city":"北京","location":{"lat":30,"lon":40}}
{"index":{"_index":"test003","_type":"doc"}}
{"user":"lisi", "age":30,"message":"happy birthday","city":"上海","location":{"lat":38.970718,"lon":116.325747}}
{"index":{"_index":"test003","_type":"doc"}}
{"user":"wangwu", "age":35,"message":"Happy birthday","city":"深圳","location":{"lat":37.970718,"lon":116.325747}}
{"index":{"_index":"test003","_type":"doc"}}
{"user":"zhaoliu", "age":40,"message":"birthday happy","city":"深圳","location":{"lat":36.970718,"lon":116.325747}}