(二)Elasticsearch的基本数据结构,检索类型和聚合类型

1,054 阅读10分钟

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 字符串类型

  1. string:在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,改为由text和keyword类型替代。
  2. text:当一个字段被全文搜索时,如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
  3. 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 浮点类型

类型取值范围
doule64位双精度IEEE 754浮点类型
float32位单精度IEEE 754浮点类型
half_float16位半精度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类型

日期类型格式:

  1. 日期格式的字符串,比如 “2018-01-13” 或 “2018-01-13 12:10:30”
  2. long类型的毫秒数( milliseconds-since-the-epoch,epoch就是指UNIX诞生的UTC时间1970年1月1日0时0分0秒)
  3. 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” ],常用的数组类型是:

  1. 字符数组: [ “one”, “two” ]
  2. 整数数组: productid:[ 1, 2 ]
  3. 对象(文档)数组: “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 结构化检索

针对字段类型: 日期、时间、数字类型,以及精确的文本匹配。 结构化检索特点:

  1. 结构化查询,我们得到的结果总是非A即B,要么存于集合之中,要么存在集合之外。
  2. 结构化查询不关心文件的相关度或评分,它简单的对文档包括或排除处理

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 tolte: <= 小于或等于(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

  1. 单值分析,只输出一个分析结果
  • min,max,avg,sum
  • cardinality
  1. 多值分析,输出多个分析结果
  • 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值得累计和