1. Elasticsearch的基本数据结构
1.1 字段类型概述
一级分类 | 二级分类 | 具体类型 |
---|---|---|
核心类型 | 字符串类型 | string,text,keyword |
整数类型 | integer,long,short,byte | |
浮点类型 | double,float,half_float,scaled_float | |
逻辑类型 | boolean | |
日期类型 | date | |
范围类型 | range | |
二进制类型 | binary | |
复合类型 | 数组类型 | array |
对象类型 | object | |
嵌套类型 | nested | |
地理类型 | 地理坐标类型 | geo_point |
地理地图 | geo_shape | |
特殊类型 | IP类型 | ip |
范围类型 | completion | |
令牌计数类型 | token_count | |
附件类型 | attachment | |
抽取类型 | percolator |
1.2 字符串类型
- string:在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,改为由text和keyword类型替代。
- text:当一个字段被全文搜索时,如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
- keyword:适用于索引结构化的字段,比如Email、手机号码和身份证。创建keyword字段的索引时是不进行分词的,keyword字段通常 用于过虑、排序、聚合等。keyword类型的字段只能通过精确值搜索到。
1.3 整数类型
类型 | 取值范围 |
---|---|
byte | -128~127 |
short | -32768~32767 |
integer | -231~231-1 |
long | -263~263-1 |
在满足需求的情况下,尽可能选择范围小的数据类型。字段的长度越短,索引和搜索的效率越高。
比如,某个字段的取值最大值不会超过100,那么选择byte类型即可。
迄今为止吉尼斯记录的人类的年龄的最大值为134岁,对于年龄字段,short足矣。
1.4 浮点类型
类型 | 取值范围 |
---|---|
doule | 64位双精度IEEE 754浮点类型 |
float | 32位单精度IEEE 754浮点类型 |
half_float | 16位半精度IEEE 754浮点类型 |
scaled_float | 缩放类型的的浮点数 |
比如一个价格字段,单位为元,我们将比例因子设置为100这在ES中会按分存储,映射如下:
{
"price": {
"type": "scaled_float",
"scaling_factor": 100
}
}
注意: scaling_factor属性是只针对
scaled_float
这个数据类型才有,不要在其他类型上使用此属性。
由于比例因子为100,如果我们输入的价格是23.45则ES中会将23.45乘以100存储在ES中。如果输入的价格是23.456,ES会将23.456乘以100再取一个接近原始值的数,得出2346。使用比例因子的好处是整型比浮点型更易压缩,节省磁盘空间。
1.5 date类型
日期类型格式:
- 日期格式的字符串,比如 “2018-01-13” 或 “2018-01-13 12:10:30”
- long类型的毫秒数( milliseconds-since-the-epoch,epoch就是指UNIX诞生的UTC时间1970年1月1日0时0分0秒)
- integer的秒数(seconds-since-the-epoch)
1.6 boolean类型
逻辑类型可以接受true/false/”true”/”false”值。
1.7 binary类型
二进制字段是指用base64来表示索引中存储的二进制数据,可用来存储二进制形式的数据,例如图像。
默认情况下,该类型的字段只存储不索引。
二进制类型只支持index_name属性。
1.8 array类型
在同一个数组中,数组元素的数据类型是相同的,ElasticSearch不支持元素为多个数据类型:[ 10, “some string” ],常用的数组类型是:
- 字符数组: [ “one”, “two” ]
- 整数数组: productid:[ 1, 2 ]
- 对象(文档)数组: “user”:[{ “name”: “Mary”, “age”: 12 }, { “name”: “John”, “age”: 10 }]
1.9 object类型
JSON天生具有层级关系,文档会包含嵌套的对象。
1.10 ip类型
ip类型的字段用于存储IPv4或者IPv6的地址。
PUT test
{
"mappings": {
"my":{
"properties": {
"nodeIP":{
"type": "ip"
}
}
}
}
}
2. ElasticSearch的基本检索类型
2.1 检索和过滤
测试数据
PUT website/_doc/1
{
"title": "小白学ES01",
"desc": "the first blog about es",
"level": 1,
"post_date": "2018-10-10",
"post_address": {
"country": "China",
"province": "GuangDong",
"city": "GuangZhou"
}
}
PUT website/_doc/2
{
"title": "小白学ES02",
"desc": "the second blog about es",
"level": 3,
"post_date": "2018-11-11",
"post_address": {
"country": "China",
"province": "ZheJiang",
"city": "HangZhou"
}
}
搜索条件: 搜索博客等级(level)大于等于2, 同时发布日期(post_date)是2018-11-11的博客:
#不使用filter
GET website/_doc/_search
{
"query": {
"bool": {
"must": [
{ "match": { "post_date": "2018-11-11" } },
{ "range": { "level": { "gte": 2 } } }
]
}
}
}
// 结果信息:
"hits": {
"total": 1,
"max_score": 2.0,
"hits": [
{
"_index": "website2",
"_type": "blog",
"_id": "2",
"_score": 2.0, // 评分为2.0
"_source": {
"title": "小白学ES02",
"desc": "the second blog about es",
"level": 3,
"post_date": "2018-11-11",
"post_address": {
"country": "China",
"province": "ZheJiang",
"city": "HangZhou"
}
}
}
]
}
#使用filter
GET website/_doc/_search
{
"query": {
"bool": {
"must": {
"match": { "post_date": "2018-11-11" }
},
"filter": {
"range": { "level": { "gte": 2 } }
}
}
}
}
// 结果信息:
"hits": {
"total": 1,
"max_score": 1.0,
"hits": [
{
"_index": "website2",
"_type": "blog",
"_id": "2",
"_score": 1.0, // 评分为1.0
"_source": {
"title": "小白学ES02",
"desc": "the second blog about es",
"level": 3,
"post_date": "2018-11-11",
"post_address": {
"country": "China",
"province": "ZheJiang",
"city": "HangZhou"
}
}
}
]
}
测试结果
当filter和query一起使用时, 会先执行filter。
相关度处理
filter
—— 根据搜索条件过滤出符合的文档,不计算相关度分数;
query
—— 先查询条件的文档,然后计算每个文档对于搜索条件的相关度分数,再根据评分倒序排序.
建议
- 如果对搜索结果有排序的要求,要将最匹配的文档排在最前面,就用query
- 如果只是根据一定的条件筛选出部分数据,不关注结果的排序,就用filter
性能对比
filter 性能更好,无排序 —— 不计算相关度分数,不用根据相关度分数进行排序, 同时ES内部还会缓存(cache)比较常用的filter的数据。
query 性能较差,有排序 —— 要计算相关度分数,要根据相关度分数进行排序, 并且没有cache功能。
2.2 结构化检索
针对字段类型: 日期、时间、数字类型,以及精确的文本匹配。 结构化检索特点:
- 结构化查询,我们得到的结果总是非A即B,要么存于集合之中,要么存在集合之外。
- 结构化查询不关心文件的相关度或评分,它简单的对文档包括或排除处理
2.2.1 精确匹配检索
1. 单值精确查找(term query)
term 查询会查找我们指定的精确值,它接受一个字段名以及我们希望查找的数值。
类似mysql中如下sql语句的查询操作:SELECT document FROM products WHERE price = 20;
GET /my_store/products/_search
{
"query" : {
"term" : {
"price" : 20
}
}
}
当进行精确值查找时, 我们会使用过滤器(filters)。过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存。
GET /my_store/products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"price" : 20
}
}
}
}
}
当我们不关心检索词频率TF(Term Frequency)对搜索结果排序的影响时,可以使用constant_score将查询语句query或者过滤语句filter包装起来。
2. 多值精确查找(terms query)
terms是包含的意思,包含20或者包含30。
{
"terms" : {
"price" : [20, 30]
}
}
2.2.2 范围检索(range query)
range 查询可同时提供包含(inclusive)和不包含(exclusive)这两种范围表达式,可供组合的选项如下:
gt: > 大于(greater than)
lt: < 小于(less than)
gte: >= 大于或等于(greater than or equal to)
lte: <= 小于或等于(less than or equal to)
GET /my_store/products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"range" : {
"price" : {
"gte" : 20,
"lt" : 40
}
}
}
}
}
}
2.2.3 存在与否检索(exist query)
exist查询某个字段是否存在:
GET /my_index/posts/_search
{
"query" : {
"constant_score" : {
"filter" : {
"exists" : { "field" : "tags" }
}
}
}
}
2.2.4 前缀检索( prefix query)
GET /_search
{
"query": {
"prefix" : { "user" : "ki" }
}
}
2.2.5 通配符检索( wildcard query)
匹配具有匹配通配符表达式( not analyzed )的字段的文档,支持的通配符: 1)*,它匹配任何字符序列(包括空字符序列)。 2)?,它匹配任何单个字符。 请注意,此查询可能很慢,因为它需要遍历多个术语。
GET /_search
{
"query": {
"wildcard" : { "user" : "ki*y" }
}
}
2.2.6 正则表达式检索(regexp query)
允许使用正则表达式术语查询:
GET /_search
{
"query": {
"regexp":{
"name.first": "s.*y"
}
}
}
2.2.7 模糊检索(fuzzy query)
GET /_search
{
"query": {
"fuzzy" : { "user" : "ki" }
}
}
2.2.8 类型检索(type query)
检索索引my_index中,type为xext的全部信息:
GET /my_index/_search
{
"query": {
"type" : {
"value" : "xext"
}
}
}
2.2.9 Ids检索(ids query)
返回指定id的全部信息:
GET /my_index/_search
{
"query": {
"ids" : {
"type" : "xext",
"values" : ["2", "4", "100"]
}
}
}
2.3 全文检索
高级全文查询通常用于在全文本字段(如电子邮件正文)上运行全文查询,他们了解如何对被查询的字段进行分析,并在执行前将每个字段的分析器应用于查询字符串。
match查询常用的参数:
- operator:用来控制match查询匹配词条的逻辑条件,默认值是or,如果设置为and,表示查询满足所有条件;
- minimum_should_match:当operator参数设置为or时,该参数用来控制应该匹配的分词的最少数量;
2.3.1 匹配检索(match query)
match模糊匹配,先对输入进行分词,对分词后的结果进行查询,文档只要包含match查询条件的一部分就会被返回:
GET /my_index/my_type/_search
{
"query": {
"match": {
"title": "Hello World"
}
}
}
2.3.2 匹配解析检索(match_phrase query)
从分析后的文本中构建短语查询,这意味着必须匹配短语中的所有分词,并且保证各个分词的相对位置不变:
{
"query": {
"match_phrase": {
"foo": "Hello World"
}
}
}
match_phrase 简单说就是要匹配一个短语,例如你输入的文本为:中国人,如果被分词为:中国/人,那么查找时候会在指定的字段先查找到 "中国"这个term,然后在"中国"这个 term 后面去查找 "人"这个term(有顺序要求),如果匹配到则认为匹配成功;所以更像是在匹配一个短语(连贯的句子)。
对于 matchQuery 而言,只需要查到 "中国"这个term或者"人"这个term ,则表示匹配成功,而且对顺序没有要求。
2.3.3 匹配解析前缀检索(match_phrase_prefix)
{
"query": {
"match_phrase_prefix": {
"foo": "Hello Wo"
}
}
}
2.3.4 多字段匹配检索(multi_match query)
GET /_search
{
"query": {
"multi_match" : {
"query": "this is a test",
"fields": [ "subject", "message" ]
}
}
}
提升字段权重:在查询字段后使用 ^
符号可以提高字段的权重,增加字段的分数 _score
。例如,我们想增加 subject
字段的权重:
{
"query": {
"multi_match": {
"query": "multimatch",
"fields": ["subject^3", "mess*"]
}
}
}
2.3.5 字符串检索(query_string)
query_string语法查询,同match_phase的相同点在于,输入的查询条件会被分词,但是不同之处在与文档中的数据可以不用和query_string中的查询条件有相同的顺序。
允许我们在单个查询字符串中指定AND | OR | NOT条件,同时也和 multi_match query 一样,支持多字段搜索。
# 1、检索同时包含Token【系统学、es】的文档,结果为空,等价于sql语句【where Token = “系统学”and Token = “es”】
GET /tehero_index/_doc/_search
{
"query": {
"query_string" : {
"query" : "系统学 AND es",
"fields" : ["content"]
}
}
}
2.3.6 简化字符串检索(simple_query_string)
类似于query_string ,但是会忽略错误的语法,永远不会引发异常,并且会丢弃查询的无效部分。
2.3 复合检索
2.3.1 布尔过滤器
{
"bool" : {
"must" : [],
"should" : [],
"must_not" : [],
"filter": []
}
}
- must ——所有的语句都 必须(must) 匹配,与 AND 等价。
- must_not ——所有的语句都 不能(must not) 匹配,与 NOT 等价。
- should ——至少有一个语句要匹配,与 OR 等价。
- filter——必须匹配,运行在非评分&过滤模式。
GET /my_store/products/_search
{
"query" : {
"filtered" : {
"filter" : {
"bool" : {
"should" : [
{"term" : {"price" : 20}},
{"term" : {"productID" : "XHDK-A-1293-#fJ3"}}
],
"must_not" : {
"term" : {"price" : 30}
}
}
}
}
}
}
3. Elasticsearch的聚合类型
聚合分析,英文为Aggregation,是针对es数据做统计分析的功能
- 功能丰富,提供Bucket,Metric,Pipeline等多种分析方式,可以满足大部分的分析需求
- 实时性高,所有的计算结果都是及时返回的,而Hadoop等大数据系统一般都是T+1的
Elasticsearch将聚合分析分为如下4类:
-
Metric,指标分析类型,如计算最大值,最小值,平均值等等
-
Pipeline,管道分析类型,基于上一级的聚合分析结果进行再分析
-
Bucket,分桶类型,类似SQL中的GROUP BY语法
-
Matrix,矩阵分析类型
3.1 Metrics
- 单值分析,只输出一个分析结果
- min,max,avg,sum
- cardinality
- 多值分析,输出多个分析结果
- stats,entended stats
- percentile,percentile rank
- top hits
3.1.1 min
返回数值类字段的最小值:
GET test_search_index/_search
{
"size":0,
"aggs":{
"min_age":{
"min": {
"field": "age"
}
}
}
}
3.1.2 max
返回数值类字段的最大值:
GET test_search_index/_search
{
"size":0,
"aggs":{
"max_age":{
"max": {
"field": "age"
}
}
}
}
3.1.3 avg
返回数值类字段的平均值:
GET test_search_index/_search
{
"size":0,
"aggs":{
"avg_age":{
"avg": {
"field": "age"
}
}
}
}
3.1.4 sum
返回数值类字段的总和
GET test_search_index/_search
{
"size":0,
"aggs":{
"sum_age":{
"sum": {
"field": "age"
}
}
}
}
3.1.5 cardinality
cardinality,是指不同数值的个数,类似SQL中的distinct count概念:
GET test_search_index/_search
{
"size":0,
"aggs":{
"count_of_job":{
"cardinality": {
"field": "job.keyword"
}
}
}
}
3.1.6 stats
返回一系列数值类型的统计值,包含min,max,avg,sum和count:
GET test_search_index/_search
{
"size":0,
"aggs":{
"stats_age":{
"stats": {
"field": "age"
}
}
}
}
3.1.7 extended Stats
对stats的扩展,包含了更多的统计数据,如方差,标准差等:
GET test_search_index/_search
{
"size":0,
"aggs":{
"exstats_salary":{
"extended_stats": {
"field": "salary"
}
}
}
}
3.1.8 percentile
百分位数统计:
GET test_search_index/_search
{
"size":0,
"aggs":{
"per_salary":{
"percentiles": {
"field": "salary"
}
}
}
}
GET test_search_index/_search
{
"size": 0,
"aggs": {
"per_age": {
"percentiles": {
"field": "salary",
"percents": [
95,
99,
99.9
]
}
}
}
}
3.2 bucket
Bucket 意为桶,即按照一定的规则将文档分配到不同的桶中,达到分类分析的目的。
按照Bucket的分桶策略,常见的Bucket聚合分析如下:
- Terms
- Range
- Date Range
- Histogram
- Date Histogram
3.2.1 terms
该分桶策略最简单,直接按照term来分桶;如果是text类型,则按照分词后的结果分桶:
GET test_search_index/_search
{
"size": 0,
"aggs": {
"jobs": {
"terms": {
"field": "job",
"size": 5
}
}
}
}
3.2.2 range
按照指定数值的范围来设定分桶规则:
GET test_search_index/_search
{
"size": 0,
"aggs": {
"salary_range": {
"range": {
"field": "salary",
"ranges": [
{
"key":"<10000",
"to": 10000
},
{
"from": 10000,
"to": 20000
},
{
"key":">20000",
"from": 20000
}
]
}
}
}
}
3.2.3 date range
通过指定日期的范围来设定分桶规则:
GET test_search_index/_search
{
"size": 0,
"aggs": {
"date_range": {
"range": {
"field": "birth",
"format": "yyyy",
"ranges": [
{
"from":"1980",
"to": "1990"
},
{
"from": "1990",
"to": "2000"
},
{
"from": "2000"
}
]
}
}
}
}
3.2.4 histogram
直方图,以固定间隔的策略来分割数据:
GET test_search_index/_search
{
"size":0,
"aggs":{
"salary_hist":{
"histogram": {
"field": "salary",
"interval": 5000,
"extended_bounds": {
"min": 0,
"max": 40000
}
}
}
}
}
3.2.5 date histogram
针对日期的直方图或者柱状图,是时序数据分析中常用的聚合分析类型:
GET test_search_index/_search
{
"size":0,
"aggs":{
"by_year":{
"date_histogram": {
"field": "birth",
"interval": "year",
"format":"yyyy"
}
}
}
}
3.3 pipeline
针对聚合分析的结果进行再次聚合分析,而且支持链式调用。
3.3.1 min bucket
GET test_search_index/_search
{
"size":0,
"aggs":{
"jobs":{
"terms": {
"field": "job.keyword",
"size": 10
},
"aggs":{
"avg_salary":{
"avg": {
"field": "salary"
}
}
}
},
"min_salary_by_job":{
"min_bucket": {
"buckets_path": "jobs>avg_salary"
}
}
}
}
3.3.2 derivative
计算Bucket值的导数
GET test_search_index/_search
{
"size": 0,
"aggs": {
"birth": {
"date_histogram": {
"field": "birth",
"interval": "year",
"min_doc_count": 0
},
"aggs": {
"avg_salary": {
"avg": {
"field": "salary"
}
},
"derivative_avg_salary": {
"derivative": {
"buckets_path": "avg_salary"
}
}
}
}
}
}
3.3.3 moving average
计算Bucket值的移动平均值
3.3.4 cumulative sum
计算Bucket值得累计和