共计 26322 个字符,预计需要花费 66 分钟才能阅读完成。
part 1
前言
emmmmm…. 我一个前端学习 es 我也很为难啊。elasticsearch 涉及的内容非常多,分布式相关的章节我都是跳过的。因为我真的看不懂????
参考文章以及相关资料
Elastic Stack and Product DocumentationElasticsearch: 权威指南 Elastic Stack 从入门到实践
安装, 启动, 汉化
首先你的电脑需要拥有 Java8 的环境
// 使用 Homebrew 一键安装
brew update
brew install elasticsearch
// kibana 是一个可视化工具
brew install kibana
????️kibana 的汉化, 可以使用 GitHub 上的提供的包(需要安装 python 环境)
RESTful API
可以使用 RESTful API 通过 9200 端口与 Elasticsearch 进行交互
基本概念
Index
索引类似于数据库中表,是用来存储文档的地方。将数据存储到 es 的行为也可以叫做索引
type
???? Mapping types will be completely removed in Elasticsearch 7.0.0. (type 的概念将在 7.0 中被删除)
???? The first alternative is to have an index per document type (更好的做法是, 每一种 type 将会有单独的 Index, 不同的 type 存储到不同的索引中)
Document
Index(索引)中的每一条记录被称为文档, 许多的 Document 组成了 Index
搜索
空搜索
返回所有索引下的所有文档
GET /_search
# 等同于,匹配所有的文档
GET /_search
{
“query”: {
“match_all”: {}
}
}
简单搜索
通过查询字符串 query-string 搜索, 通过 URL 参数的形式, 传递查询参数
# 查询 test_index 索引中, name 字段包含 lihang 的文档
GET test_index/_search?q=name:lihang
# 查询 test_index 索引中, name 字段包含 zhangyue 或者 liyubo
GET test_index/_search?q=name:(zhangyue OR liyubo)
QueryDSL
Query DSL 指定了一个 JSON 进行检索, 它支持构建更加复杂和健壮的查询
# match 查询, 会将查询语句进行分词处理, 查询 name 字段中包含 lihang 的文档
GET test_index/_search
{
“query”: {
“match”: {
“name”: “lihang”
}
}
}
全文搜索
使用 match 查询全文字段,可以对查询语句进行分词操作,返回所有的相关的文档,查询结果按照相关度算分排序。如果查询的是数字,日期,Boolean,match 则会进行精确匹配
# 查询 job 字段中和 engineer 相关的文档
GET test_index/_search
{
“query”: {
“match”: {
“job”: “engineer”
}
}
}
短语搜索
使用 match_phrase 可以查询 job 字段中包含查询短语的文档
GET test_index/_search
{
“query”: {
“match_phrase”: {
“job”: “node engineer”
}
}
}
# 通过设置 slop 相似度, 可以允许查询文档与差异语句有一些差异
# 设置 slop 为 2, java engineer 和 java web engineer 可以匹配
GET test_index/_search
{
“query”: {
“match_phrase”: {
“job”: {
“query”: “java engineer”,
“slop”: 2
}
}
}
}
高亮搜索
会对检索的匹配的结果中,匹配的部分做出高亮的展示, 使用标签 em 包裹
GET test_index/_search
{
“query”: {
“match”: {
“job”: “engineer”
}
},
“highlight”: {
“fields”: {
“job”: {}
}
}
}
// 返回的部分结果
{
“_index”: “test_index”,
“_type”: “doc”,
“_id”: “3”,
“_score”: 0.2876821,
“_source”: {
“name”: “houjiaying”,
“job”: “java engineer”,
“age”: 22
},
“highlight”: {
“job”: [
“java <em>engineer</em>”
]
}
}
多索引搜索
GET /test_index, test2_index/_search
???? 针对多个索引进行搜索操作, 文档中也是类似的语法。但是我不太清楚为什么返回错误给我,如果有好心人请告诉我
???? Multi-Index search (autocomplete)(折中的解决方案)
# 使用_msearch 方法多索引搜索
GET _msearch
{“index”:”test_index”}
{“query”:{“term”:{“age”:{“value”:23}}}}
{“index”:”test2_index”}
{“query”:{“term”:{“_id”:{“value”:1}}}}
分页
使用 form 指定查询的起始位置, size 指定查询的数量
GET _all/_search?size=5&from=1
GET _all/_search
{
“from”: 0,
“size”: 1
}
聚合
# 统计索引中全部文档的 age,并会返回聚合的结果
GET /test_index/doc/_search
{
“aggs”: {
// 返回聚合结果到 all_age 字段中, 聚合 terms 匹配的 age 字段
“all_age”: {
“terms”: {“field”: “age”}
}
}
}
// 返回的聚合结果
“aggregations”: {
// all_age 是自己定义的
“all_age”: {
“doc_count_error_upper_bound”: 0,
“sum_other_doc_count”: 0,
“buckets”: [
{
“key”: 24,
“doc_count”: 2
},
{
“key”: 22,
“doc_count”: 1
},
{
“key”: 23,
“doc_count”: 1
},
{
“key”: 30,
“doc_count”: 1
}
]
}
}
聚合分级汇总
# 添加测试用数据
DELETE test_index
PUT test_index
POST test_index/doc/_bulk
{“index”:{“_id”:1}}
{“name”:”zhangyue”,”age”:23,”job”:”web”}
{“index”:{“_id”:2}}
{“name”:”lihange”,”age”:23,”job”:”web”}
{“index”:{“_id”:3}}
{“name”:”liyubo”,”age”:22,”job”:”web”}
{“index”:{“_id”:4}}
{“name”:”houjiaying”,”age”:22,”job”:”java”}
{“index”:{“_id”:5}}
{“name”:”xiaodong”,”age”:26,”job”:”java”}
# 聚合查询分级汇总
# 汇总所有的职业并计算职业的评价年龄
GET test_index/doc/_search
{
“aggs”: {
“job_info”: {
“terms”: {
“field”: “job”
},
“aggs”: {
“avg_age”: {
“avg”: {
“field”: “age”
}
}
}
}
}
}
???? 解决方案
???? Fielddata is disabled on text fields by default. Set fielddata=true
???? 在 text 字段中聚合是聚合是禁用的,需要设置索引的 mapping, 设置字段的 fielddata 为 true
# 设置 job 字段的 fielddata 为 true
PUT test_index/_mapping/doc
{
“doc”: {
“properties”: {
“job”: {
“type”: “text”,
“fielddata”: true
}
}
}
}
Document
元数据
_index 文档存放的索引
_type 文档的类型
_id 文档的唯一标识
创建文档
可以分为指定 ID 创建, 和不指定 ID 创建(Elasticsearch 会自动创建一个唯一标示)。指定 ID 使用 PUT 请求, 不指定 ID 使用 POST 请求
查询指定 ID 文档
GET /you_index/you_type/you_document_id
指定返回的字段
# 只返回文档的 name 字段
GET test_index/doc/1?_source=name
# 只返回文档的 age 字段
GET test_index/doc/_search
{
“query”: {
“range”: {
“age”: {
“gt”: 23
}
}
},
“_source”: {
// 返回的字段
“includes”: [“age”],
// 不返回的字段
“excludes”: []
}
}
检查文档是否存在
使用 HEAD 方法,HEAD 不返回响应体
# 检查 ID 为 1 的文档是否存在
HEAD test_index/doc/1
更新文档
POST test_index/doc/1/_update
{
“doc”: {
“name”: “zhang yue”
}
}
???? 更新文档后的返回结果,可以看的_version 变为了 2,_version 字段的含义是文档更新了几次,_version 可以实现乐观锁的功能
{
“_index”: “test_index”,
“_type”: “doc”,
“_id”: “1”,
“_version”: 2,
“found”: true,
“_source”: {
“name”: “zhangyueHI”,
“age”: 23,
“job”: “web”
}
}
删除文档
# 删除 ID 为 2 的文档
DELETE test_index/doc/2
乐观锁
更新的时候,指定_version 的大小,当更新文档的时候_version 必须等于我们指定的大小,更新操作才会成功
# version 为 2 时更新操作才会成功
POST test_index/doc/1/_update?version=2
{
“doc”: {
“name”: “zhangyue”
}
}
批量检索
# 检索同一个索引下的多个的文档
GET /test_index/doc/_mget
{
“docs”: [
{
“_id”: 1
},
{
“_id”: 3
}
]
}
# 直接通过不同的 id, 检索同一个索引下的多个的文档
GET /test_index/doc/_mget
{
“ids”: [1, 3, 4]
}
# 检索不同索引下的多个文档
GET /_mget
{
“docs”: [
{
“_index”: “test_index”,
“_type”: “doc”,
“_id”: 1
},
{
“_index”: “test2_index”,
“_type”: “doc”,
“_id”: 1
}
]
}
批量创建
批量创建的 JSON 中, 不能包含未转义的换行符,因为他们将会对解析造成干扰。必须使用 Kibana 自动格式化 JSON,才可以正确的执行_bulk 操作
批量操作关键字, index(创建, id 重复时覆盖), delete(删除), create(创建, id 重复时报错), update(更新)
# 批量操作
POST /test_index/doc/_bulk
{“index”:{“_index”:”test_index”,”_type”:”doc”,”_id”:2}}
{“name”:”songmin”,”age”:20}
{“delete”:{“_index”:”test_index”,”_type”:”doc”,”_id”:2}}
{“create”:{“_index”:”test_index”,”_type”:”doc”,”_id”:2}}
{“name”:”songmin”,”age”:18}
{“update”:{“_index”:”test_index”,”_type”:”doc”,”_id”:2}}
{“doc”:{“age”:22}}
Mapping 与分析
精确值与全文
精确值,通常指的是日期, 用户 ID, 邮箱地址。对于精确值, java 与 java web 是不同的。精确值,要么匹配查询,要么不匹配。
全文, 通常指的是文本数据, 匹配时是查询的匹配的程度
倒排索引
Elasticsearch 中使用倒排索引的方式,实现快速的全文搜索
倒排索引是由文档中, 是所有可以被分词的字段的复词列表组成的。将所有文档的复词列表,用来创建一个没有重复分词的复词列表。并且记录了,每一个分词出现在那一个文档中。
例如搜索短语 ”dog over”, 在倒排索引中在 Doc_1, Doc_2 都会匹配, 但 Doc_1 文档的匹配度会更高
Term Doc_1 Doc_2
————————-
Quick | | X
The | X |
brown | X | X
dog | X |
dogs | | X
fox | X |
foxes | | X
in | | X
jumped | X |
lazy | X | X
leap | | X
over | X | X
quick | X |
summer | | X
the | X |
————————
分析器
分析器由以下三种功能组成:
字符过滤器, 例如过滤文本中 HTML 标签
分词器, 将文本拆分成单独的词条
Token 过滤器, 过滤分词, 例如转化大小写, 删除语气组词
Elasticsearch 内置了多种的分析器, 例如空格分析器, 可以按照空格分词。语言分析器, 可以删除多余的语气助词
如果希望指定索引中字段的分析器,则需要我手动的指定索引的 Mapping
analyze API
# 测试 standard 分析器
GET /_analyze
{
“analyzer”: “standard”,
“text”: “Text to analyze”
}
Mapping
Mapping 的定义可以类比为 Mongo 中 Schema, 在 Mapping 中定义了索引里每个字段的类型以及分析器等
# 查看索引的 Mapping
GET test_index/doc/_mapping
自定义 Mapping
???? Elasticsearch 中, 已经不存在 string, object 类型, 使用 text 类型。
???? 字段的 type 被设置为 ”text” 类型, 字段通常会被全文检索。如果设置为 ”keyword” 类型, 字段会则需要精确查找。
???? 当字段的 type 被设置为 ”text” 类型, 可以另外设置 index(是否可以被搜索)和 analyzer(分析器)
# 自定义索引的 Mapping
PUT m2y_index
{
“mappings”: {
“doc”: {
“properties”: {
“info”: {
type: “text”
}
}
}
}
}
更新 Mapping
不能直接更新索引的 Mapping, 只能在创建索引的时候指定 Mapping, 以及添加新的字段的 Mapping
复杂类型
数组类型, 数组的内容必须类型一致, 检索时, 所有包含在数组中的内容都可以被检索
null, 空值不会被索引
Object, 可以通过以下的方式定义 Object 类型的 Mapping(通过嵌套 properties)
# 定义 Object 类型的 Mapping
POST test3_index/doc
{
“mappings”: {
“doc”: {
“properties”: {
“info”: {
“properties”: {
“name”: {
“type”: “text”
},
“age”: {
“type”: “text”
},
“hobbies”: {
“properties”: {
“one”: {
“type”: “text”
},
“two”: {
“type”: “text”
}
}
}
}
}
}
}
}
}
查询
match 查询
match 查询时,如果查询是全文字段,会对查询内容分词后进行全文搜索。如果指定的是数字, 日期, 布尔字段, match 查询则会进行精准匹配
multi_match 查询
对多个字段(fields 数组中的字段), 执行相同的 match 查询
GET test_index/doc/_search
{
“query”: {
“multi_match”: {
“query”: “web”,
“fields”: [“job”, “name”]
}
}
}
range 查询
进行范围查询操作, 关键字 gt(大于), gte(大于等于), lt(小于), lte(小于等于)
GET test_index/doc/_search
{
“query”: {
“range”: {
“age”: {
“gte”: 22,
“lte”: 30
}
}
}
}
# 日期字段进行 range 查询
# 添加测试数据
PUT range_index
POST range_index/doc
{
“mappings”: {
“doc”: {
“date”: {
“type”: “date”
}
}
}
}
POST range_index/doc/_bulk
{“index”:{“_id”:1}}
{“name”:”zhangyue”,”date”:”1994-06-07″}
{“index”:{“_id”:2}}
{“name”:”lihang”,”date”:”1994-10-22″}
{“index”:{“_id”:3}}
{“name”:”zhaochen”,”date”:”1994-07-01″}
# 查询日期范围, 日期小于 1994 年 7 月
GET range_index/doc/_search
{
“query”: {
“range”: {
“date”: {
“lt”: “1994-07”
}
}
}
}
term 查询
term 查询用于精准匹配, 对查询的内容, 不进行分词处理
terms 查询
与 term 查询一致,区别在于 terms 提供了一个数组, 可以允许多值匹配
组合多查询(bool)
bool 支持多种条件的查询和过滤
bool 支持多种参数, must(必须包含的条件), must_no(必须不包含的条件), should(满足 should 中的内容可以增加匹配得分),filter(必须匹配, 但是不影响匹配得分)
# bool 查询中, 文档必须满足 must 中的全部条件,并进行相关性算分。filter 会过滤不符合条件的文档, 但不进行相关性算分。
GET test_index/_search
{
“query”: {
“bool”: {
“must”: [
{
“match”: {
“name”: “lihang”
}
}
],
“filter”: {
“range”: {
“age”: {
“gt”: 20
}
}
}
}
}
}
验证查询
验证查询语句是否合法
GET zc_index/doc/_validate/query
{
“query”: {
“range”: {
“age”: {
“gt1e”: 2
}
}
}
}
// 当查询非法的时候,valid 会返回 false
{
“valid”: false
}
排序
desc 降序
asc 升序
// 查询 job 中包含 web 的,并不进行相关性算分, 按照 age 的升序排序
GET zc_index/doc/_search
{
“query”: {
“bool”: {
“filter”: {
“match”: {
“job”: “web”
}
}
}
},
“sort”: [
{
“age”: {
“order”: “asc”
}
}
]
}
多值字段 (不是多字段) 排序。当字段内拥有多个值的时候,可以使用 mode,对这些值进行 min,max,sum,avg 等操作。然后,对计算后的结果进行排序
“sort”: {
“dates”: {
“order”: “asc”,
“mode”: “min”
}
}
字符串排序
有时候,我们希望对字符串进行排序(例如: 通过首字母的方式排序字符串), 但是对于 text 类型, elasticsearch 会进行分词处理。如果需要对字符串进行排序,我们不能进行分词处理。但是如果需要全文检索,我们又需要对字符串进行分词处理。
我们可以通过 fields 对相同的字段,以不同的目的,设置不同的 type 类型
// job 字段设置为 text 类型,进行全文检索
// job.row 设置为 keyword 类型, 可以进行排序,精确匹配
PUT zy_index
{
“mappings”: {
“doc”: {
“properties”: {
“job”: {
“type”: “text”,
“fields”: {
“raw”: {
“type”: “keyword”
}
}
}
}
}
}
}
// 添加一些测试数据
POST zy_index/doc/_bulk
{“index”:{“_id”:1}}
{“name”:”zhangyue”,”job”:”web node”}
{“index”:{“_id”:2}}
{“name”:”lihang”,”job”:”node java”}
{“index”:{“_id”:3}}
{“name”:”liyubo”,”job”:”web”}
{“index”:{“_id”:4}}
{“name”:”houjiaying”,”job”:”java”}
// 全文检索 job 含有 node
// 通过 job.row 进行首字母排序
GET zy_index/doc/_search
{
“query”: {
“match”: {
“job”: “node”
}
},
“sort”: [
{
“job.raw”: {
“order”: “asc”
}
}
]
}
文档的相关性
_score 表示文档的相关性,_score 越高文档与查询的条件匹配程度越高。
_score 的评分标准:
检索词频率,检索词在字段出现的频率越高,文档的相关性越高
反向文档频率,检索词在索引中出现的越高,文档的相关性越低(因为大多数文档都出现了该检索词)
字段长度准则, 检索词出现的字段越长, 相关性越低
索引
# 创建索引
PUT test_index
# 查看索引
GET test_index
# 删除索引
DELETE test_index
创建索引
通过索引一篇文档可以自动创建索引, 索引采用默认的配置。字段会通过动态映射添加类型,当然我们可以指定索引的 mapping
删除索引
// 删除全部的索引
DELETE /_all
DELETE /*
动态映射 dynamic mapping
Elasticsearch 会将 mapping 中没有定义的字段,通过 dynamic mapping 确定字段的数据类型,这是默认的行为。如果对没有出现的未定义字段作出其他的操作,可以通过定义 mapping 的 dynamic 配置项目
dynamic: true 会自动为新增的字段添加类型
dynamic: false 忽略新增的字段
dynamic: strict 会抛出异常
PUT hello_index
{
“mappings”: {
“doc”: {
“dynamic”: true
}
}
}
重新索引数据
在 es 中 mapping 定义后是无法修改的,如果想要修改 mapping 只能创建新的索引。更多内容 Reindex API
POST _reindex
{
“source”: {
“index”: “zc_index”
},
“dest”: {
“index”: “zc2_index”
}
}
part2
<!–more–>
导入
使用 elasticsearch-dump 导入文件数据到 Es 中
// dump 的使用方法
elasticdump \
// JSON 文件路径
–input=/Users/zhangyue/Documents/swmgame-activity-2018.06/swmgame-activity-2018.06.mapping.json \
// 导入的路径
–output=http://127.0.0.1:9200/my_index_type \
–type=mapping
elasticdump \
–input=/Users/zhangyue/Documents/swmgame-activity-2018.06/swmgame-activity-2018.06.data.json \
–output=http://127.0.0.1:9200/my_index_type \
–type=data
搜索
精确值搜索
term 查询数字
term 查询会查找指定的精确值, 如果不想计算相关度算分, 可以使用 filter 可以更快的执行操作
// 检索 passStatus 为 2 的文档
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“must”: [
{
“term”: {
“passStatus”: {
“value”: 2
}
}
}
]
}
}
}
// 检索 passStatus 等于 2 并且 gameId 等于 G09, 并且查询不计算相关度算分
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
// 不计算相关度算分
“filter”: {
“bool”: {
“must”: [
{
“term”: {
“passStatus”: {
“value”: 2
}
}
},
{
“term”: {
“gameId.keyword”: {
“value”: “G09”
}
}
}
]
}
}
}
}
}
term 查询文本
???? 使用 term 检索文本的时候, 检索的结果可能与我们预期的结果并不一致, 这是什么原因导致的呢?
???? 我们通过 analyzeAPI 可以看到, 文档中的 text 类型的字段, 已经被分词。如果使用 term 精确查找全文, 是不会匹配到数据的。因为这些全文并没有被存储在倒排索引中。
???? 有两种解决的办法, 解决办法 1, 在 es 中 es 默认会为字段创建名为 keyword 的 keyword 类型的 fields 字段, 于字段一致但是类型并不一致。可以使用 keyword 的 fields 字段进行 term 查询。解决办法 2, 创建新的索引使用新的 mapping, 指定字段为 keyword 类型
// 解决办法 1
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“must”: [
{
“term”: {
“gameId.keyword”: {
“value”: “G09”
}
}
}
]
}
}
}
// 解决办法 2
DELETE test5_index
PUT test5_index
{
“mappings”: {
“doc”: {
“properties”: {
“gameId”: {
“type”: “keyword”
}
}
}
}
}
term 查询范围类型
DELETE test_index
PUT test_index
{
“mappings”: {
“doc”: {
“properties”: {
// 定义 age 字段为范围类型
“age”: {
“type”: “integer_range”
}
}
}
}
}
// 添加测试数据
POST test_index/doc/_bulk
{“index”:{“_id”:1}}
{“age”:{“gte”:10,”lte”:20}}
{“index”:{“_id”:2}}
{“age”:{“gte”:21,”lte”:30}}
// 查询文档
GET test_index/doc/_search
{
“query”: {
“term”: {
“age”: {
“value”: 11
}
}
}
}
组合过滤器
组件过滤器的组成:
must 必须满足的一组条件
must_not 必须不满足的一组条件
should 可以满足的一组的条件, 如果满足可以增加相关性算分(如果组合过滤器中只有 should, 则文档必须满足 should 中的一项条件)
// 嵌套的 bool 查询
// 检索 gameId 等于 G09, 或者 passStatus 等于 2 并且 source 等于 ios
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“should”: [
{
“term”: {
“gameId.keyword”: {
“value”: “G09”
}
}
},
{
“bool”: {
“must”: [
{
“term”: {
“passStatus”: {
“value”: “2”
}
}
},
{
“term”: {
“source.keyword”: {
“value”: “ios”
}
}
}
]
}
}
]
}
}
}
terms
terms 查询与 term 查询类似,terms 匹配的是一组精确值, 是一个精确值数组
// terms 匹配的是一组数组
// 检索 passStatus 为 1 或者 2 的文档
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“filter”: {
“terms”: {
“passStatus”: [
1,2
]
}
}
}
}
}
range
gt 大于
gte 大于等于
lt 小于
lte 小于等于
数字范围查询 ????
// passStatus 大于等于 1 并且小于 2 的文档
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“filter”: {
“range”: {
“passStatus”: {
“gte”: 1,
“lt”: 2
}
}
}
}
}
}
日期范围查询 ????
在使用日期范围查询的时候,需要预先将日期的字段的 mapping 的 type 类型设置为 date
DELETE test_index
PUT test_index
{
“mappings”: {
“doc”: {
“properties”: {
// 设置为 time 类型
“time”: {
“type”: “date”
}
}
}
}
}
// 添加测试数据
POST test_index/doc/_bulk
{“index”:{“_id”:1}}
{“time”:”1994-06-07″}
{“index”:{“_id”:2}}
{“time”:”1994-10-22″}
{“index”:{“_id”:3}}
{“time”:”1993-07-01″}
{“index”:{“_id”:4}}
{“time”:”2005-01-01″}
// 查询文档的 time 字段的日期范围是大于 1994-10-22, 小于等于 2005-01-01
GET test6_index/doc/_search
{
“query”: {
“bool”: {
“filter”: {
“range”: {
“time”: {
“gt”: “1994-10-22”,
“lte”: “2005-01-01”
}
}
}
}
}
}
字符串范围查询 ????
字符串范围使用的是字典顺序 (lexicographically) 的排序方式。
???? 什么是字典序?
比如说有一个随机变量 X 包含 1, 2, 3 三个数值。
其字典排序就是 123 132 213 231 312 321
DELETE test_index
// 添加测试数据
POST test_index/doc/_bulk
{“index”:{“_id”:1}}
{“name”:”abce fg”}
{“index”:{“_id”:2}}
{“name”:”cba twa”}
{“index”:{“_id”:3}}
{“name”:”a cb”}
{“index”:{“_id”:4}}
{“name”:”gfw wfw”}
{“index”:{“_id”:5}}
{“name”:”bgfw wfw”}
GET test_index/doc/_search
{
“query”: {
“bool”: {
“filter”: {
“range”: {
“name”: {
“gte”: “a”,
“lt”: “c”
}
}
}
}
}
}
处理 Null 值
存在查询
检索文档中至少包含一个非空的文档。(???? 全部为空或者没有该字段的除外)
// 查找 game 字段至少包含一个非空文档的文档
GET test_index/doc/_search
{
“query”: {
“exists”: {
“field”: “game”
}
}
}
缺失查询
查询文档中为 null, 或者没有该字段的文档
???? 缺失查询已经在 ElasticSearch2.2.0 中废弃。可以使用以下的方法代替
GET test_index/doc/_search
{
“query”: {
“bool”: {
“must_not”: {
“exists”: {
“field”: “game”
}
}
}
}
}
全文搜索
基本概念
基于词项
如 term 查询不会经过分析阶段,只会在倒排索引中查找精确的词项,并用 TF/DF 算法为文档进行相关性评分_score。term 查询只对倒排索引的词项精确匹配,而不会对查询词进行多样性处理。
基于全文
如 match 查询和 query_string 查询。对于日期和整数会将查询字符串当作整数对待。如果查询的是 keyword 类型的字段,会将查询字符串当作单个单词对待。如果是全文字段,会将查询字符串使用合适的分析器,作出处理,生成一个查询词项的列表,查询会对词项列表中的每一项进行查询,再将结果进行合并。
匹配查询
match 查询即可以进行精确检索也可以进行全文检索,以下是 match 查询的具体的过程:
检查字段类型, 如果检索的字段是 text 类型, 那么查询字符串也应当被分析
分析查询字符串, 将查询字符串经过默认的分析器的处理, match 执行的是当个底层的 term 查询
term 查询会在倒排索引中对分析后的查询字符串进行检索
为匹配的文档评分(检索词频率, 反向文档频率, 字段长度准则)
为什么在这里测试只创建一个分片, 后面会有介绍
// 只创建一个主分片
PUT /swmgame-activity-2018.06
{“settings”: { “number_of_shards”: 1}}
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“filter”: {
“match”: {
“ipInfo.city”: “ 蚌埠 ”
}
}
}
}
}
多词查询
match 查询如果查询的是 text 类型的全文字段, 那么也会对查询字符串进行分析
// match 查询会分别查询蚌埠和龙子湖区两个字段
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“filter”: {
“match”: {
“ipInfo.city”: “ 蚌埠 龙子湖区 ”
}
}
}
}
}
如果希望查询的文档, 同时包含所有的关键词, 则可以使用 operator 使用 and 操作符
// 查询包含蚌埠并且包含龙子湖区两个字段
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“filter”: {
“match”: {
“ipInfo.city”: “ 蚌埠 龙子湖区 ”,
“operator”: “and”
}
}
}
}
}
最小匹配数
match 查询可以设置 minimum_should_match 参数,即最小匹配数,如果 match 查询有四个词项,如果 minimum_should_match 设置为 50%,那么最少匹配 2 个词项
组合查询
组合查询接受 must, must_not, should。如果当 DSL 中只包含 should 的时候, should 至少有一个必须匹配。同时可以指定 should 的 minimum_should_match 参数, 控制查询的精度。控制必须匹配的数量。
多词查询中, 使用 or 操作符号, 和使用 should 查询是等价的
// 两条查询是等价的
GET test_index/doc/_search
{
“query”: {
“match”: {
“title”: “fox dog”
}
}
}
GET test_index/doc/_search
{
“query”: {
“bool”: {
“should”: [
{
“term”: {
“title”: {
“value”: “fox”
}
}
},
{
“term”: {
“title”: {
“value”: “dog”
}
}
}
]
}
}
}
多词查询中, 使用 and 操作符, 和使用 must 查询是等价的
// 两条查询是等价的
GET test_index/doc/_search
{
“query”: {
“match”: {
“title”: {
“query”: “fox dog”,
“operator”: “and”
}
}
}
}
GET test_index/doc/_search
{
“query”: {
“bool”: {
“must”: [
{
“term”: {
“title”: {
“value”: “fox”
}
}
},
{
“term”: {
“title”: {
“value”: “dog”
}
}
}
]
}
}
}
权重
设置 boost 可以设置查询语句的权重, 大于 1 权重增大, 0 到 1 之间权重逐渐降低。匹配到权重越高的查询语句, 相关性算分越高
// 安徽的查询语句的权重为 3, 匹配到的文档的得分更高
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“bool”: {
“should”: [
{
“term”: {
“passStatus”: {
“value”: 2,
“boost”: 2
}
}
},
{
“match”: {
“ipInfo.province.keyword”: {
“query”: “ 安徽 ”,
“boost”: 3
}
}
}
]
}
}
}
默认的分析器
索引时的分析器:
首先使用字段映射的分析器
其次是 index 默认的分析器
最后是默认为 standard 分析器
搜索时的分析器:
优先查找定义的分析器
其次是字段映射的分析器
最后是索引默认的 default 的分析器, 默认为 standard 分析器
相关度被破坏
Q: 为什么在之前的测试索引时,需要指定只为这个索引创建一个主分片?
A: 为了避免相关度被破坏。在 elasticsearch 中计算相关度使用 TF/IDF 算法,词频 / 逆向文档词频的算法。如果有 5 个包含”fox”的文档位于分片 1,一个包含“fox”的文档位于分片 2。就会导致一个问题, fox 在分片 1 的相关度会降低, 而在分片 2 的相关度就会非常高。但是在实际的场景中这并不是一个问题,因为在真实的环境中,数据量通常是非常大的,分片之间的差异会被迅速均化,以上的问题只是因为数据量太少了。
最佳字段
什么是最佳字段,我们需要首先了解什么是竞争关系。
下面是两条文档, 假设我们需要搜索 ”Brown fox” 两个关键词并且需要同时检索 ”title” 和 ”body” 两个字段的时候。文档 2 的 body 字段虽然同时匹配了两个关键词, 但是文档 2 的 title 没有匹配到任何的关键词。所以相关性算分, 文档 2 是低于文档 1 的。
但是如果取最佳字段的评分,文档 2 的 body 字段为最佳字段, 文档 2 的评分是高于文档 1 的评分的
{
“title”: “Quick brown rabbits”,
“body”: “Brown rabbits are commonly seen.”
}
{
“title”: “Keeping pets healthy”,
“body”: “My quick brown fox eats rabbits on a regular basis.”
}
dis_max
// 在最后计算文档的相关性算分的时候, 只会取 queries 中的相关性的最大值
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“dis_max”: {
“queries”: [
{
“match”: {
“FIELD1”: “TEXT1”
}
},
{
“match”: {
“FIELD2”: “TEXT2”
}
}
]
}
}
}
tie_breaker
使用 dis_max 最佳字段可能存在另外的问题就是。如果同时查询多个字段, 如果一个文档同时匹配了多个字段,但是由于文档的相关性算分取的是最佳字段, 可能导致该文档的相关性算分并不是最高的。
使用 tie_breaker 参数,可以将其他匹配语句的评分也计算在内。将其他匹配语句的评分结果与 tie_breaker 相乘, 最后与最佳字段的评分求和得出文档的算分。
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“dis_max”: {
“tie_breaker”: 0.3,
“queries”: [
{
“match”: {
“FIELD1”: “TEXT1”
}
},
{
“match”: {
“FIELD2”: “TEXT2”
}
}
]
}
}
}
multi_match
多字段的 match 查询, 为多个字段执行相同的 match 查询。multi_match 查询默认取最佳字段的相关性算分
// 下面两种查询是等同的
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“dis_max”: {
“queries”: [
{
“match”: {
“FIELD1”: “TEXT1”
}
},
{
“match”: {
“FIELD2”: “TEXT1”
}
}
]
}
}
}
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“multi_match”: {
“query”: “TEXT1”,
“type”: “best_fields”, // 取最佳字段的相关性算分
“fields”: [
“FIELD1”,
“FIELD2”,
“FIELD3^2” // 可以指定的查询字段的权重, 如果匹配 FIELD3 字段得分将会更高
]
}
}
}
fields
fields 是对同一个字段索引多次, 每一个字段不同的 fields 可以定义不同的 type 以及分析器,用做不同的用途
// title 为 text 类型
// title.keyword 为 keyword 类型
// title.english 使用 english 的分析器
PUT test3_index
{
“mappings”: {
“doc”: {
“properties”: {
“title”: {
“type”: “text”,
“fields”: {
“keyword”: {
“type”: “keyword”
},
“english”: {
“type”: “text”,
“analyzer”: “english”
}
}
}
}
}
}
}
跨字段实体搜索
比如一个人的信息,包含姓名,电话号码,家庭住址等多个字段。如果想要搜索人的信息,可能需要检索多个字段。
// 姓名, 手机号, 家庭住址字段都包含了一个人的信息
{
“name”: “Frank”,
“mobi”: “13053127868”,
“hours”: “bengbu”
}
最直接的检索的方式是通过 multi_match 或者 bool 的 should 实现对多个字段的检索
GET test_index/doc/_search
{
“query”: {
“bool”: {
“should”: [
{
“match”: {
“name”: “Frank 1305312786”
}
},
{
“mathch”: {
“mobi”: “Frank 13053127868”
}
},
{
“mathch”: {
“hours”: “Frank 13053127868”
}
}
]
}
}
}
{
“query”: {
“multi_match”: {
“query”: “Frank 1305312786”,
“type”: “most_fields”, // 合并匹配字段的评分, 不使用最佳字段的评分
“fields”: [“name”, “mobi”, “hours”]
}
}
}
跨字段检索的问题
在同一个字段匹配到多个关键词的相关性算分,要小于多个字段匹配同一个关键词的算分。
使用 and 操作符, 会导致所有查询变为所有关键词都必须要在同一个字段匹配。
逆向文档频率的问题, 当一个查询具有较低的逆向文档频率 (相关度较高)。可能会削弱较高的逆向文档频率(相关度较低) 的作用。
_all
使用 copy_to 的功能,将多个字段内容拷贝到文档的一个字段中。检索时使用合并的字段进行检索。
PUT test3_index
{
“mappings”: {
“doc”: {
“properties”: {
“name”: {
“type”: “text”,
“copy_to”: “info”
},
“mobi”: {
“type”: “text”,
“copy_to”: “info”
},
“hours”: {
“type”: “text”,
“copy_to”: “info”
},
“info”: {
“type”: “text”
}
}
}
}
}
// 查询时可以直接通过 info 字段查询
GET test3_index/doc/_search
{
“query”: {
“match”: {
“info”: “Frank 1305312786”
}
}
}
cross-fields
当 multi_match 的 type 等于 cross-fields 时, 它将所有 fields 中的字段当成一个大字段,并在这个大字段中进行检索。
同时也会解决逆向文档频率的问题, 因为 cross-fields 会取不同字段中最高的逆向文档频率 (相关度最低的) 的值。
cross-fields 时,所有字段必须为同一种分析器。如果包括了不同分析器的字段,它们会以 best_fields 的相同方式被加入到查询结果中
// abc efg 必须同时出现在 FIELD1 或者 FIELD2 文档中
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“multi_match”: {
“query”: “abc efg”,
“fields”: [“FIELD1”, “FIELD2”],
“type”: “most_fields”,
“operator”: “and”
}
}
}
// abc efg 必须出现但是可以在 FIELD1 或者 FIELD2 不同的字段中
GET swmgame-activity-2018.06/my_index/_search
{
“query”: {
“multi_match”: {
“query”: “abc efg”,
“fields”: [“FIELD1^2”, “FIELD2”], // 可以提高字段的权重
“type”: “cross_fields”,
“operator”: “and”
}
}
}
part3
<!–more–>
聚合
基本概念
桶
桶指的是满足特定条件的文档的集合, (比如李航属于陕西桶), 聚合开始执行, 符合条件的文档会放入对应的桶。
指标
对桶内的文档进行统计计算, 例如:最小值, 平均值, 汇总等
指标与桶
桶是可以被嵌套的, 可以实现非常复杂的组合。
国家桶 -> 性别桶 -> 年龄段桶 -> 不同国家不同性别不同年龄段的平均工资
初步聚合
简单的聚合
// 对每一个省份的数据进行聚合
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“province_info”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 10
}
}
}
}
简单的指标
// 聚合每一个省份的文档, 并对每一个省份的得分求平均值
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“province_info”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 100
},
“aggs”: {
“avg_score”: {
“avg”: {
“field”: “score”
}
}
}
}
}
}
嵌套桶
对聚合的结果进一步聚合。每一层的聚合都可以添加单独的指标, 每一层聚合的指标之间相互独立。
// 首先对每一个国家进行聚合, 并计算每一个国家的平均得分
// 其次对每一个省份进行聚合, 并计算每一个省份的平均得分
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“country_info”: {
“terms”: {
“field”: “ipInfo.country.keyword”,
“size”: 10
},
“aggs”: {
“avg_score”: {
“avg”: {
“field”: “score”
}
},
“province_info”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 100
},
“aggs”: {
“avg_score”: {
“avg”: {
“field”: “score”
}
}
}
}
}
}
}
}
直方图
什么是直方图
直方图就是柱状图, 创建直方图需要指定一个区间, elasticsearch 可以对每一个区间进行聚合操作
// 根据 durationCallAll 字段, 从最小值开始, 每 10000 为 1 个区间进行聚合
GET crm_statistics-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“day_duration_call_all”: {
“histogram”: {
“field”: “durationCallAll”,
“interval”: 10000,
“min_doc_count”: 0
}
}
}
}
// 返回的部分结果
// ……
{
“key”: 20000,
“doc_count”: 3
},
{
“key”: 30000,
“doc_count”: 3
},
{
“key”: 40000,
“doc_count”: 9
}
// ……
按时间统计
根据时间统计, 可以根据时间类型的字段进行聚合, 区间可以是每一日, 每一周, 每一月, 每一季度等等
GET crm_statistics-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“at”: {
“date_histogram”: {
“field”: “at”, // 根据时间类型的字段 at 进行聚合
“interval”: “day”, // 区间是每一天
“min_doc_count”: 0, // 可以强制返回空的桶
“extended_bounds”: {// 时间区间
“min”: “2018-05-30”,
“max”: “2018-06-22”
}
}
}
}
}
返回空的桶
min_doc_count, 可以强制返回长度为空的桶
extended_boundss, 可以设置返回的时间区间(因为 elasticsearch 默认只返回最小值到最大值之间的桶)
扩展例子
???? 按时间统计的直方图上进行聚合操作, 并计算度量指标的例子。
下面的 DSL, 按时间进行统计直方图, 以每一天作为区间, 并且计算区间内, 每一个省份的平均分数, 以及每一个省份下每一个城市的平均得分
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“at_date_histogram”: {
“date_histogram”: {
“field”: “at”,
“interval”: “day”,
“min_doc_count”: 0,
“extended_bounds”: {
“min”: “2018-05-01”,
“max”: “2018-06-22”
}
},
“aggs”: {
“province_info”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 50
},
“aggs”: {
“avg_score”: {
“avg”: {
“field”: “score”
}
},
“city_info”: {
“terms”: {
“field”: “ipInfo.city.keyword”,
“size”: 100
},
“aggs”: {
“avg_score”: {
“avg”: {
“field”: “score”
}
}
}
}
}
}
}
}
}
}
限定范围聚合
没有指定 query 的情况下, 聚合操作针对的是整个索引
// 限定安徽和福建两个省份进行聚合操作
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“query”: {
“bool”: {
“should”: [
{
“term”: {
“ipInfo.province.keyword”: {
“value”: “ 安徽 ”
}
}
},
{
“term”: {
“ipInfo.province.keyword”: {
“value”: “ 福建 ”
}
}
}
]
}
},
“aggs”: {
“city_info”: {
“terms”: {
“field”: “ipInfo.city.keyword”,
“size”: 100
},
“aggs”: {
“avg_socre”: {
“avg”: {
“field”: “score”
}
}
}
}
}
}
全局桶
使用全局桶 (global) 可以在查询范围内聚合, 也可以全局文档上聚合
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“query”: {
“bool”: {
“filter”: {
“term”: {
“ipInfo.province.keyword”: “ 安徽 ”
}
}
}
},
“aggs”: {
// 查询限定的范围内聚合
“city_info”: {
“terms”: {
“field”: “ipInfo.city.keyword”,
“size”: 100
}
},
“all”: {
“global”: {},
// 在全局范围内聚合
“aggs”: {
“city”: {
“terms”: {
“field”: “ipInfo.city.keyword”,
“size”: 1000
},
“aggs”: {
“all_avg_score”: {
“avg”: {
“field”: “score”
}
}
}
}
}
}
}
}
过滤和聚合
过滤
参考限定范围的聚合 ????️ ✈️
过滤桶
对聚合的结果进行过滤
为什么使用过滤桶?
我们可能遇到这种情况, 我们想把查询条件 a 查询到数据显示到前端页面上, 但同时又想把查询条件 a + b 的聚合结果显示到页面上。所以在过滤的时候,我们并不能直接使用过滤条件 a +b。而聚合桶就可以对聚合的结果进行过滤
// 查询显示的结果的过滤条件是 “ 安徽 ”
// 聚合显示的结果的过滤条件是 “ 安徽 ” + “ 蚌埠 ”
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 20,
“query”: {
“bool”: {
“filter”: {
“term”: {
“ipInfo.province.keyword”: “ 安徽 ”
}
}
}
},
“aggs”: {
“city_info”: {
“filter”: {
“term”: {
“ipInfo.city.keyword”: “ 阜阳 ”
}
},
“aggs”: {
“city_info”: {
“terms”: {
“field”: “ipInfo.city.keyword”,
“size”: 20
}
}
}
}
}
}
后过滤器
对查询的结果进行过滤
为什么需要后过滤器?
我们可能遇到这种情况, 我们想在查询 a 条件的情况下对结果作出聚合, 但是又想在查询 a + b 条件下下显示结果。这种情况下可以使用后过滤器, 对查询的结果进行过滤。
// 查询的条件是 福建 + 厦门
// 聚合的过滤条件是厦门
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 5,
“query”: {
“bool”: {
“filter”: {
“term”: {
“ipInfo.province.keyword”: “ 福建 ”
}
}
}
},
“post_filter”: {
“term”: {
“ipInfo.city.keyword”: “ 厦门 ”
}
},
“aggs”: {
“city_info”: {
“terms”: {
“field”: “ipInfo.city.keyword”,
“size”: 20
}
}
}
}
聚合排序
内置排序
_count 按照文档的数量排序
_term 按词项的字符串值的字母顺序排序
_key 按每个桶的键值数值排序。只在 histogram 和 date_histogram 内使用
desc 降序
asc 升序
// 文档的内容升序进行排序
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“province”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 50,
“order”: {
“_count”: “asc”
}
}
}
}
}
// 按照字符串的顺序进行排序, 只能用在 term
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“game_id_info”: {
“terms”: {
“field”: “gameId.keyword”,
“size”: 50,
“order”: {
“_term”: “asc”
}
}
}
}
}
// 按照 date_histogram 或者 histogram 的 key 排序
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“date”: {
“date_histogram”: {
“field”: “at”,
“interval”: “day”,
“min_doc_count”: 0,
// 时间区间按照降序排序
“order”: {
“_key”: “desc”
}
}
}
}
}
度量排序
直接使用度量的 key 作为排序的字段
单度量值排序
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“province_info”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 50,
“order”: {
“avg_score”: “asc”
}
},
“aggs”: {
“avg_score”: {
“avg”: {
“field”: “score”
}
}
}
}
}
}
多度量值排序
使用点操作符号, 选择单个度量值进行排序
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“province_info”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 50,
“order”: {
“info.avg”: “asc”
}
},
“aggs”: {
“info”: {
“extended_stats”: {
“field”: “score”
}
}
}
}
}
}
近似聚合
对于一些复杂的操作,需要在精准性和实时性上作出权衡
近似聚合 —— 去重
cardinality 可以实现去重操作, 但是数据量巨大的情况下精确性上可能存在误差, 但是可以设置更大的内存提供去重的精确性
// 查看有多少个城市
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“city_count”: {
“cardinality”: {
“field”: “ipInfo.city.keyword”
}
}
}
}
// 统计每一个省份的城市数量
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“country_info”: {
“terms”: {
“field”: “ipInfo.country.keyword”,
“size”: 10
},
“aggs”: {
“province_info”: {
“terms”: {
“field”: “ipInfo.province.keyword”,
“size”: 50
},
“aggs”: {
“city”: {
“terms”: {
“field”: “ipInfo.city.keyword”,
“size”: 50
}
},
“city_count”: {
“cardinality”: {
“field”: “ipInfo.city.keyword”
}
}
}
}
}
}
}
}
precision_threshold
cardinality 精确性与内存的使用量有关,通过配置 precision_threshold 参数,设置去重需要的固定内存使用量。内存使用量只与你配置的精确度相关。
precision_threshold 设置为 100 的时候,去重的需要的内存是 100 * 8 的字节。precision_threshold 接受的范围在 0–40,000 之间
// 统计每一个国家的数量
GET swmgame-activity-2018.06/my_index/_search
{
“size”: 0,
“aggs”: {
“country_count”: {
“cardinality”: {
“field”: “ipInfo.country.keyword”
}
}
}
}
近似聚合 —— 百分位
什么是百分位
以下是百度百科的内容
统计学术语,如果将一组数据从小到大排序,并计算相应的累计百分位,则某一百分位所对应数据的值就称为这一百分位的百分位数。可表示为:一组 n 个观测值按数值大小排列。如,处于 p% 位置的值称第 p 百分位数。
百分位通常用第几百分位来表示,如第五百分位,它表示在所有测量数据中,测量值的累计频次达 5%。以身高为例,身高分布的第五百分位表示有 5% 的人的身高小于此测量值,95% 的身高大于此测量值。
高等院校的入学考试成绩经常以百分位数的形式报告。比如,假设某个考生在入学考试中的语文部分的原始分数为 54 分。相对于参加同一考试的其他学生来说,他的成绩如何并不容易知道。但是如果原始分数 54 分恰好对应的是第 70 百分位数,我们就能知道大约 70% 的学生的考分比他低,而约 30% 的学生考分比他高。
百分位计算
// 添加测试数据, 网络延迟数据
PUT test9_index
POST /test9_index/doc/_bulk
{“index”: {}}
{“latency” : 100, “zone” : “US”, “timestamp” : “2014-10-28”}
{“index”: {}}
{“latency” : 80, “zone” : “US”, “timestamp” : “2014-10-29”}
{“index”: {}}
{“latency” : 99, “zone” : “US”, “timestamp” : “2014-10-29”}
{“index”: {}}
{“latency” : 102, “zone” : “US”, “timestamp” : “2014-10-28”}
{“index”: {}}
{“latency” : 75, “zone” : “US”, “timestamp” : “2014-10-28”}
{“index”: {}}
{“latency” : 82, “zone” : “US”, “timestamp” : “2014-10-29”}
{“index”: {}}
{“latency” : 100, “zone” : “EU”, “timestamp” : “2014-10-28”}
{“index”: {}}
{“latency” : 280, “zone” : “EU”, “timestamp” : “2014-10-29”}
{“index”: {}}
{“latency” : 155, “zone” : “EU”, “timestamp” : “2014-10-29”}
{“index”: {}}
{“latency” : 623, “zone” : “EU”, “timestamp” : “2014-10-28”}
{“index”: {}}
{“latency” : 380, “zone” : “EU”, “timestamp” : “2014-10-28”}
{“index”: {}}
{“latency” : 319, “zone” : “EU”, “timestamp” : “2014-10-29”}
计算网络延迟的百分位和网络延迟的平均数
GET test9_index/doc/_search
{
“size”: 0,
“aggs”: {
// 计算网络延迟的平均数
“avg_ping”: {
“avg”: {
“field”: “latency”
}
},
// 计算网络延迟的百分位
“percentiles_ping”: {
“percentiles”: {
“field”: “latency”
}
}
}
}
下面是返回的聚合结果,可以看到平均数有时并不能体现异常的数据。约有 25% 的用户的网络延迟是大于 289 的
“aggregations”: {
“avg_ping”: {
“value”: 199.58333333333334
},
“percentiles_ping”: {
“values”: {
“1.0”: 75.55,
“5.0”: 77.75,
“25.0”: 94.75,
“50.0”: 101,
“75.0”: 289.75,
“95.0”: 489.34999999999985,
“99.0”: 596.2700000000002
}
}
}
查看网络的延迟是否和地区有关,在聚合的基础上进行百分位计算。根据结果可以得知, 欧洲用户 (EU) 的网络延迟更高
GET test9_index/doc/_search
{
“size”: 0,
“aggs”: {
“zone_info”: {
“terms”: {
“field”: “zone.keyword”,
“size”: 10
},
“aggs”: {
“zone_ping”: {
“percentiles”: {
“field”: “latency”
}
}
}
}
}
}
百分位等级
得知具体的数值所占的百分位。以下是查看 ping 为 75, 623 的用户所占的百分位。
GET test9_index/doc/_search
{
“size”: 0,
“aggs”: {
“ping”: {
“percentile_ranks”: {
“field”: “latency”,
“values”: [
75,
623
]
}
}
}
}
可以看到如下的结果, ping 小于等于 75 的用户占到了 4.16%, 有 22.5% 的用户 ping 是大于 623 的
“aggregations”: {
“ping”: {
“values”: {
“75.0”: 4.166666666666666,
“623.0”: 87.5
}
}
}
Elasticsearch 会暂停更新, 未来有时间了还会继续更新, 目前为止我个人工作所需的知识已经足够了 ….