一 Elasticsearch简介
Elasticsearch 是一个开源的搜索引擎,Elasticsearch 使用 Java 编写的,它的内部使用 Lucene 做索引与搜索,但是它的目的是使全文检索变得简单, 通过隐藏 Lucene 的复杂性,取而代之的提供一套简单一致的 RESTful API。
- 一个分布式的实时文档存储,每个字段 可以被索引与搜索
- 一个分布式实时分析搜索引擎
- 能胜任上百个服务节点的扩展,并支持 PB 级别的结构化或者非结构化数据
二 安装并运行
已经在其他文档中详细介绍,此次仅做简单步骤介绍
# 安装:
$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.13.3-linux-x86_64.tar.gz
$ tar -xzf elasticsearch-7.13.3-linux-x86_64.tar.gz
$ cd elasticsearch-7.13.3/
# 运行
sh bin/elasticsearch
# 访问
$ curl http://192.168.3.14:9200/
{
"name" : "87DNZWU",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "e3A3l85MSZuZlRhxj6IB2w",
"version" : {
"number" : "6.7.0",
"build_flavor" : "default",
"build_type" : "zip",
"build_hash" : "8453f77",
"build_date" : "2019-03-21T15:32:29.844721Z",
"build_snapshot" : false,
"lucene_version" : "7.7.0",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}
三 Query DSL 基本结构
查询表达式(Query DSL)是一种非常灵活又富有表现力的查询语言, Elasticsearch 使用它可以以简单的 JSON 接口来展现 Lucene 功能的绝大部分
// 查询
GET /_search // 查找整个ES中所有索引的内容
{
"query": {}, //具体的查询语句对象
"from": 0, //从第几条数据开始返回
"size": 100, //返回的条数 默认ES最多返回10000条
"highlight": { //高亮
"pre_tags": {}, //高亮内容的前面标签 一般都是html比如<b> <p>这种
"post_tags": {},//高亮内容的后面标签 一般都是html比如</b> </p>这种
"fields": { //需要高亮的字段
}
},
"sort": [{ //排序
"FIELD": { //排序的字段(需要填上具体的字段名)
"order": "desc"
}
}],
"_source": "{field}" //指定返回的字段
}
// 结果
{
"took": 350, // 整个搜索请求消耗了多少毫秒
"timed_out": false, // 表示本次查询是否超时,如果为true也会返回结果,只是数据可能不完整
"_shards": { // 显示查询中参与的分片信息,成功多少分片失败多少分片等
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 5, // 匹配到的文档总数
"max_score": 1, // 为文档中所有_score的最大值
"hits": [
{
"_index": "mysql-shop_trades-order_item_label_binds",
"_type": "doc",
"_id": "591935",
"_score": 1,
"_source": {
"id": 591935,
"updated_at": "2021-05-20T06:26:09.000Z",
"@version": "1",
"bind_item_label_id": 729,
"label_type": "brand",
"created_at": "2021-05-20T06:26:09.000Z",
"@timestamp": "2021-07-07T07:31:36.262Z",
"is_deleted": 0,
"table_name": "order_item_label_binds",
"bar_code": "6907925004486"
}
}
]
}
}
四 指定索引搜索
上述查询会搜索ES中的所有索引,但通常只需要去固定一个或几个索引中搜索,搜索全部无疑会造成资源的浪费,在ES中可以通过以下几种方法来指定索引
- 指定一个固定的索引,
ops-coffee-nginx-2019.05.15
为索引名字
GET /mysql-shop_trades-order_statics/_search
以上表示在mysql-shop_trades-order_statics
索引下查找数据
- 指定多个固定索引,多个索引名字用逗号分割
GET /mysql-shop_trades-order_statics,mysql-shop_trades-order_item_labels/_search
- 用*号匹配,在匹配到的所有索引下查找数据
GET /mysql-shop_trades-*/_search
这里也可以用逗号分割多个匹配索引
五 DSL查询
1、筛选字段
// 筛选_source的数据,单个字段
GET /_search
{
"_source": "bar_code",
"query": {}
}
// 筛选_source的数据,多个字段
{
"_source": {
"includes": ["store_id", "sku_id"]
},
"query": {}
}
// 对字段进行转换
{
"docvalue_fields": [
{
"field": "updated_at",
"format": "yyyy-MM-dd HH:mm:ss"
},
{
"field": "num",
"format": "long" // 没有作用,懵逼...
}
],
"query": {}
}
2、多条件查询 (where)
- constant_score:装另一个查询的查询,固定分数查询,支持filter查询,不支持match查询:
{ "constant_score": { "filter": { "match": { "name": "小米" } }, "boost": 10 } }
- bool:主要与其他关键字组合使用,多条件的查询必须要用bool包在外层,然后再根据具体的业务来拼接。
{
"query": {
"bool": {
"should": [{}], //满足其中一个对象查询条件就行 像sql里的or
"must": [{}], //必须满足所有对象的查询条件 就像sql里的and
"must_not": [{}] //必须不满足所有对象的查询条件 就像sql里的and !=
}
}
}
- must: 类似于SQL中的AND,必须包含
- must_not: 类似于SQL中的NOT,必须不包含
- should: 满足这些条件中的任何条件都会增加评分
_score
,不满足也不影响,should
只会影响查询结果的_score
值,并不会影响结果的内容 - filter: 与must相似,但不会对结果进行相关性评分
_score
,大多数情况下我们对于日志的需求都无相关性的要求,所以建议查询的过程中多用filter
3、group by:
ES本身没有group关键词搜索,但支持聚合查询,,需要使用关键字aggs
// 单个字段 group by
{
"query":{},//这里省略你的查询条件
"aggs": {
"age_group": {//这个是指你要返回字段名
"terms": { //这里还可以用其它关键词 这里terms才能实现group by效果
"field": "age",//groupby的字段
"size":1 //返回的条数 相当于group by limit
}
}
}
}
// 多字段group by (如 group by sku_id,store_id)
// 方法一:script
{
"query":{},
"aggs": {
"age_group": {
"terms": {
"script":{
"source": """ 's' + doc['store_id'] + '_s' + doc['sku_id'] """,
"lang": "painless"
},
"size": 10
}
}
}
}
// 方法二:copy to
1. 设置mapping中的多个字段,copy_to 为同一个字段(skuId_storeId)
2. 搜索新字段
{
"query":{},
"aggs": {
"list": {
"terms": {
"field": "skuId_storeId
"size":1
}
}
}
}
// 方法三:multi_terms (使用高版本,目前6.7不支持)
{
"aggs": {
"genres_and_products": {
"multi_terms": {
"terms": [{
"field": "genre"
}, {
"field": "product"
}]
}
}
}
}
4、order by
order by:注意日期格式和数值格式才支持排序;文本不支持,如果要排序, 需把字段设置为not analysis
// 单排序
{
"query": {
"sort": {
"id": "desc"
}
}
}
// avg按照平均值排序
{
"query": {
"sort": [
{
"id": "desc"
},
{
"price": {
"order": "asc",
"mode": "avg"
}
}
]
}
}
5、count(distinct)
{
"query":{},
"aggs": {
"total_sku_id": {
"cardinality":{ "field": "sku_id"}
},
"total_entity_store_id": { // 非数字类型,无法使用field排序,可以对field增加fieldData = true,或者对field.keyword排序,建议使用后者,高效内存消耗低
"cardinality":{ "field": "entity_store_id.keyword"}
}
}
}
6、SUM
{
"query":{},
"aggs": {
"total_pay_num": {
"sum": {"field": "num"}
},
"total_cost_fee": {
"sum": {"field": "cost_fee"}
}
}
}
7、distinct :
select distinct(id) from table
{
"query":{},
"collapse": {
"field": "id" //你需要distinct的字段
},
}
8、limit
1. 分页:
1. form:从第几个开始查询,最开始是0
2. size:即limit
3. 使用size,size最大可获取数量是xx个
2. 获取所有数据的三种方式
1. scroll深度滚动需要根据scroll_id和循环取,取完后,需删除scroll,减小内存开销(深度滚动高效,用于处理大量数据,不适合实时获取)
2.调整索引index.max_result_window的大小,默认10000 (大小与堆内存成正比,这个限制内存)
3.search_after:请求需增加前一个结果的排序,(实时游标,可根据索引更新和删除而改变 )
4。 如果是group by查询获取所有数据, 获取需要使用到cardinality查询预估总数,再使用partition、num_partitions分区依次获取数据
9、搜索关键字
- match:自定字段,根据字段关键字进行搜索,会分割关键词,匹配到含有一个多多个词的匹配
- query_string:全文搜索
- match_phrase:不分割关键词 {"match_phrase": {"name":"婴幼儿奶粉"}}
- term: 类似SQL where field = x,主要用于数字匹配;如果要匹配文本,会自动分词,不能精准查询,需把字段设置成not analysis
{ "query": { "term": {"bind_item_label_id": 729} } }
- terms: 类似SQL where field in (x,x),主要用于数字匹配,
{ "query": { "terms": {"bind_item_label_id": [703,729]} } }
- range:: 查询价格在1000-2000的商品
{
"query": {
"range": {
"price": {
"gte": 1000,
"lte": 2000
}
}
}
}
- filter:判断文档是否满足条件
{
"query": {
"bool": {
"filter": {
"term": {
"price": 1999
}
}
}
}
}
Elasticsearch:Aggregation
- metric:度量聚合,主要针对number类型的数据,需要es做较多的计算工作(类似SQL的SUM、MAX、AVG、MIN、Cardinality、stats<属于多值分析>等)
- bucket:桶聚合,划分不同步的桶,将数据分配到不同的桶,(类似SQL中的group by)
- Pipeline Aggregation:管道分析类型,对其他聚合结果进行二次聚合
- Matrix Aggregation:矩阵分析类型,支持对多个字段的操作并提供一个结果矩阵
term aggregation
- size 可以通过size返回top size的文档,该术语聚合针对顶层术语(不包含嵌套词根),其搜索过程是将请求向所有分点发送请求,每个分片节点返回size条数据,然后聚合所有分片的结果(会对各分片返回的同样词根的数数值进行相加),最终从中挑选size条记录返回给客户端。从这个过程也可以看出,其结果并不是准确的,而是一个近似值。
- Shard Size 为了提高该聚合的精确度,可以通过shard_size参数设置协调节点向各个分片请求的词根个数,然后在协调节点进行聚合,最后只返回size个词根给到客户端,shard_size >= size,如果shard_size设置小于size,ES会自动将其设置为size,默认情况下shard_size建议设置为(1.5 * size + 10)。
// 单个字段 group by
{
"query":{},//这里省略你的查询条件
"aggs": {
"age_group": {//这个是指你要返回字段名
"terms": { //这里还可以用其它关键词 这里terms才能实现group by效果
"field": "age",//groupby的字段
"size":1 //返回的条数 相当于group by limit
}
}
}
}
// 返回结果格式
{
...
"aggregations" : {
"list" : {
"doc_count_error_upper_bound" : 0, // 该值表示未进入最终术语列表的术语的最大潜在文档计数
"sum_other_doc_count" : 90 // 该值表示未进入最终术语列表的术语的最大潜在文档计数
"buckets" : [ // 返回doc_count排名最前的10个,受size参数的影响
{
"key" : "1",
"doc_count" : 24,
"total_refund_fee" : {
"value" : 0.0
},
"total_cost_fee" : {
"value" : -14976.0
},
}
]
}
}
}
}
match_phrase :查询分析文本,创建词组查询
举例子
GET mysql-shop_trades-order_item_label_binds/_search/?scroll=1m
{
"docvalue_fields": [
{
"field": "updated_at",
"format": "yyyy-MM-dd HH:mm:ss"
}
],
"size": 1000,
"sort": {"id":"desc"},
"query": {
"bool": {
"must": [
{"match": {"is_deleted": 0}},
{"match": {"label_type": "brand"}},
{
"constant_score": {
"filter": {
"terms": {
"bind_item_label_id": [703, 2, 729]
}
}
}
}
]
}
}
}
GET mysql-shop_trades-order_item_label_binds/_search/?scroll=1m
{
"_source": "bar_code",
"query": {
"bool": {
"filter": [
{"match": {"is_deleted": 0}},
{"match_phrase": {"label_type": "brand"}},
{"terms": {"bind_item_label_id": [703, 2, 729]}}
]
}
},
"aggs": {
"bar_code_group": {
"terms": {
"field": "bar_code.keyword",
"size": 10
}
}
}
}
GET mysql-shop_trades-order_item_label_binds,mysql-shop_trades-order_statics/_search
{
"query": {
"bool": {
"filter": [
{"match_phrase": {"sys_name": "yiqigou"}},
{"range": {"num": {"lte": 2000}}},
{"range": {"return_num": {"gte": -1000}}},
{"range": {"total_price": {"lte": 1000000}}},
{"match": {"id": 60}},
{"term": {"order_type": 0}},
{"term": {"item_type": 0}},
{"range": {"date": {
"gte": "2020-01-21 00:00:00",
"lte": "2021-07-22 00:00:00",
"format": "yyyy-MM-dd HH:mm:ss",
"time_zone": "+08:00"
}}}
],
"must_not": [
{"terms": {"store_id": [165]}}
]
}
}
}
设置fieldData
// 第一步,创建索引 (如果已经有索引,直接看第二步)
PUT mysql-shop_trades-order_statics2
{
"mappings": {
"_doc": {
"properties": {
"entity_store_id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
}
// 第二步 设置fieldData为true
PUT mysql-shop_trades-order_statics/_mapping/_doc
{
"properties": {
"entity_store_id": {
"type": "text",
"fielddata": true
}
}
}
// 第三步 可以查看该索引的Mapping结构,fieldData是否加上去
{
"mapping": {
"doc": {
"properties": {
"entity_store_id": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
},
"fielddata": true
}
}
}
}
}
延伸
设置 max_result_window
PUT /mysql-shop_trades-order_statics/_settings
{
"index": {
"max_result_window": 100000
}
}