ElasticSearch 文档检索、查询建议、数据聚合


 

查询都是写在"query"字段中的,可以使用get请求,如果携带的查询参数较多,也常常使用post请求,将请求参数放在请求体中。

 

结构化搜索

结构化搜索(structured search) 是指查询具有内在结构的数据,常用于number, date, keyword等有固定格式的数据类型的查询。

结构化查询只是简单的对文档包括或排除处理,是精确匹配,不关心相关度|评分,得到的结果总是“非是即否”,要么存在集合中,要么存在集合外。对于结构化文本来说,一个值要么相等,要么不等,没有“相似”这种概念。

 

term 单词匹配(单个值)

term不分词,是精确匹配

  • 匹配文本字段(keyword或text)时,如果搜索关键字是单个字、字母,直接返回空文档
  • 如果匹配的是keyword字段,完全等价才算匹配,eg.“苹果"只能匹配"苹果”,不能匹配"红富士苹果"、“冰糖心苹果”
  • 如果匹配的是text字段,包含搜索的关键字就算匹配,不用完全等价,eg. “苹果"可以匹配"苹果”、“红富士苹果”、“冰糖心苹果”
  • 如果匹配的是float之类的数值型字段,以数值的实际大小进行比较,eg. 15.00、15认为是匹配的。
GET /mall/_search
{
  "query": {
      "term": {
          "goods_name": "苹果"
          #"goods_price": 15
      }
  }
}

term搜索关键字的值可以直接写,“goods_price”: “苹果”;也可以写成对象形式 “goods_name”: {“value”=“苹果”}。

 

terms 单词匹配(多个值)

terms的用法、效果和term差不多,只是值要写成数组形式

GET /mall/_search
{
"query":{
    "terms":{
        "goods_name":["梨子","桃子"]  #值写成数组形式,只要匹配数组中的任意一个元素,就认为该文档匹配
    }
 }
}

term、terms都只能检索一个字段,不能同时检索多个字段。

 

返回数据示例

{
    "took": 2,  #take的过去式,搜索花费的时间,ms
    "timed_out": false,  #本次搜索是否超时
    "_shards": {  #分片信息
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": {  #查询结果
        "total": {  #结果统计
            "value": 2,  #匹配的文档数
            "relation": "eq"  #匹配标准
        },
        "max_score": 1.0,  #结果中最大的得分。es会根据相关性对匹配的文档打分,数值越大匹配度越高
        "hits": [
            {
                "_index": "mall",  #所属index
                "_type": "_doc",  #所属type
                "_id": "2",  #文档id
                "_score": 1.0,  #得分
                "_source": {  #文档中存储中的数据
                    "goods_name": "桃子",
                    "goods_price": 3.0,
                    "goods_description": "新鲜桃子,3元一斤"
                }
            },
            {
                "_index": "mall",
                "_type": "_doc",
                "_id": "3",
                "_score": 1.0,
                "_source": {
                    "goods_name": "梨子",
                    "goods_price": 5.0,
                    "goods_description": "新鲜桃子,5元一斤"
                }
            }
        ]
    }
}

 

range 范围查询
#查询价格在[10,100]上的商品
GET /mall/_search
{
    "query": {
        "range": {
            "goods_price": {
                "gte": 10,
                "lte": 100
            }
        }
    }
}

 
时间范围

GET /blog/_search
{
    "query": {
        "range": {
            "birthDay": {
                # "gte": "2020-01-01",  #可以只写一个限制条件
                "lte": "2020-12-31"
            }
        }
    }
}


#可以使用预定义的值
"gt" : "now-1h"   #过去一小时
"gt" : "2020-01-01 00:00:00||+1M"   #如果是具体时间日期的基础上增减,要放在双管符号||后面

 

exists 存在查询、miss 缺失查询

添加文档时没有传递某个字段,会使该字段没有值,有时查询时可能要筛选指定字段是否为空

GET /mall/_search
{
    "query": {
        "exists": {
            "field": "goods_description"  #指定字段有值(存在)即可
        }
    }
}

missing的使用方式和exists相同,但查询的是指定字段没有值(不存在)的文档。

 

ids id匹配
GET mall/_search
{
    "query": {
        "ids": {
            "values": [1,2,3]  #返回指定id对应的文档,id写成数组形式
        }
    }
}

 

prefix 前缀匹配
GET mall/_search
{
    "query": {
        "prefix": {
            "goods_name": "梨"  #指定字符串
        }
    }
}
  • 如果匹配的是keyword字段,以指定字符串开头才算匹配,常用于查询a-z开头的书名、人名等
  • 如果匹配的是text字段,包含指定字符串就算匹配

 

wildcard 通配符匹配
GET mall/_search
{
    "query": {
        "wildcard": {
            "goods_name": "*果"  #*表示任意字符串,?表示任意一个字符
        }
    }
}

 

regexp 正则匹配
GET mall/_search
{
  "query":{
    "regexp":{
      "goods_name":".*瓜"
    }
  }
}

正则表达式的语法与平时使用的不太一样,比如不能使用\w之类的规则,具体可参考
https://www.elastic.co/guide/en/elasticsearch/reference/current/regexp-syntax.html

 

说明

如果wildcard、regex查询以通配符开头,且查询字符串的长度很长,可能导致es服务器在进行处理时消耗大量的性能,cpu使用率、内存使用飙升,es服务器挂掉、出现查询超时。

解决方案:wildcard、regex查询避免使用通配符开头,如果要使用通配符开头,应该限制查询字符串(用户输入)的长度,超出长度时像百度一样提示“查询限制在xx字以内,xxxxx后面的内容将被忽略”。

 

全文搜索

全文搜索(full-text search) :计算相关性,返回相关(相似)的文档。

相关性(Relevance):用于衡量搜索关键字、文档之间的匹配程度,数值越大越匹配。

结构化搜索是精确匹配,要包含搜索关键字;全文搜索注重相关性,只要相关(相似)就认为匹配。

 

match_all 获取全部文档
GET /mall/_search
{
    "query": {
        "match_all": {}  #返回所有文档,match_all里面不能写字段
    }
}

 

match 全文检索
GET /mall/_search
{
    "query": {
        "match": {
            "goods_name": "梨子"
        }
    }
}
  • 匹配文本字段(keyword或text)时,如果搜索关键字是单个字、字母,直接返回空文档
  • 如果匹配的是keyword字段,完全等价才算匹配
  • 如果匹配的是text字段,会先使用分词器进行分词,包含分词后得到的任意一个词就算匹配
  • 如果匹配的是float之类的数值型字段,以数值的实际大小进行比较

 

multi_match 在多个字段中检索
GET /mall/_search
{
    "query": {
        "multi_match": {
            "query": "苹果",  #指定查询关键字
            "fields": ["goods_name","goods_description"]  #要检索的字段
        }
    }
}

效果和match相同,区别是会在指定的多个字段中检索,只要其中一个字段匹配,就认为文档匹配。

 

match_phrase 短语搜索

使用方式和match一样,区别是match_phrase把搜索关键字作为一个短语处理,不分词。

 

match_phrase_prefix 文本短语搜索

使用方式、效果和match_phrase基本相同,区别是match_phrase_prefix只能对文本字段使用。

 

term、match的比较

term、match是最常用的2个查询,term主要用于匹配keyword类型的字段,match主要用于匹配text类型的字段。
 

共同点

  • 匹配文本字段(keyword或text)时,如果搜索关键字是单个字、字母,直接返回空文档
  • 匹配keyword字段时都是完全匹配(都不分词),要与搜索关键字完全相同才算匹配
  • 匹配text字段时都是包含匹配,只要包含就匹配
  • 匹配数值型字段时以数值的实际大小进行比较,数值相等就算匹配

后3条是由字段类型的性质决定的,和使用的查询方法无关。

 

不同点

  • 匹配text类型时:term不分词,要包整个搜索关键字才算匹配;match要分词,包含搜索关键字分词得到的任意一个词项就算匹配

 

bool 布尔查询

布尔查询又叫做组合查询,可以组合多个查询条件

  • must:必须满足其中的条件(都要满足)
  • must_not:不能满足其中任意条件(都不要满足)
  • should:可选条件(满不满足均可)
  • filter:过滤器,常用于对查询结果进行筛选过滤,或者跳过评分阶段、加快查询速度
GET /mall/_search
{
    "query": {
        "bool": {
            "must": [  #如果只有一个条件,可以缺省外围的数组符号[]
                {"match": {"goods_name": "苹果"}}
            ],
            "should": [
                {"match": {"goods_decription": "冰糖心"}},
                {"match": {"goods_decription": "红富士"}}
            ]
        }
    }
}


#过滤查询结果(依然会打分)
GET /mall/_search
{
    "query": {
        "bool": {  #放在bool中
            "must": {
                "match": {"goods_description": "苹果"}
            },
            "filter": {  #对前面得到的查询结果进行过滤
                "range": {
                    "goods_price": {"lt": 30}
                }
            }
        }
    }
}


#跳过评分阶段,常用于match等不关注评分的查询。返回的评分均为默认值0.0
GET /mall/_search
{
    "query": {
        "bool": {  #放在bool中
            "filter": [ #将查询放在filter中,如果只有一个查询条件,可以缺省外层的数组符号[] 
                {"match": {"goods_description": "苹果"}}
            ]
        }
    }
}

 

query_string 查询字符串

使用lucene的查询语法构造⼀个查询字符串进行查询,es接到请求后,通过解析器获取查询字符串,⽣成对应的查询。

GET /mall/_search
{
    "query": {
        "query_string": {
            "default_field": "goods_description",  #default_field只能指定单个字段,多个字段用fields
            #"fields": ["goods_name"],
            "query": "生抽 or 老抽 or 酱油"  #查询关键字,and、or  不区分大小写
        }
    },
    "size": 10
}


#也可以直接在原来的match查询上实现
GET /mall/_search
{
    "query": {
        "match": {
            "goods_name": {
                "query": "生抽 老抽 酱油",  #关键字
                "operator": "or"
            }
        }
    },
    "size": 10
}
  • query_string查询keyword字段时,要与query完全等价才算匹配,不分词,不会检测关键字and、or。
  • query_string查询text字段时,会检测关键字and、or,以and、or为分割符将查询关键字划分为多个子串,再对这些子串使用match进行匹配。and表示所有子串都匹配才算匹配,or表示任一子串匹配就算匹配。

 

分词器

分词器简介

es内置的6种分词器

  • standard:默认的分词器,在空格、符号、中文字符处切,中文会切割为一个一个的汉字
  • simple:在空格、符号、阿拉伯数字处切
  • stop:在空格、符号、阿拉伯数字、英文介词|冠词 处切
  • whitespace:只在空格处切
  • language:根据语言的语法规则来切,常见的有 english、chinese,这2种的效果都一样:在空格、符号、英文介词|冠词 处切,中文切割为一个一个的汉字。
  • pattern:根据正则表达式来切,默认使用的正则表达式是\W+,在匹配\W+的地方切
     

es内置的分词器对中文分词支持较差,常使用第三方的中文分词器

  • smartcn:SmartCN,支持中文、中英文混合文本的分词
  • ik_max_word:ik分词器,比SmartCN更智能,推荐

此外还有拼音分词器、ip分词器等

 

分词器的安装

插件下载地址:https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis.html

linux、win都是下载zip,插件版本尽量与es版本保持一致。

可以本地解压后上传到es的plugins目录下,也可以上传到es的plugins目录后再解压

#如果没有unzip命令,可以自己安装 yum install unzip

#-d指定解压目录,未指定时默认解压到当前目录下
unzip elasticsearch-analysis-ik-7.10.0.zip -d elasticsearch-analysis-ik-7.10.0

#需删除原来的压缩包
rm -I elasticsearch-analysis-ik-7.10.0.zip

安装插件:放在plugins目录下,卸载插件:从plugins目录中移除。

重启es生效

 

分词器的使用

在定义mapping时给文本字段指定分词器,该字段查询要分词时会自动使用指定的分词器分词

PUT /mall
{
    "mappings": {
        "properties": {
            "goods_name": {
                "type": "keyword"
            },
            "goods_price": {
                "type": "float"
            },
            "goods_description": {
                "type": "text",
                "analyzer": "ik_max_word"  #在mapping中指定分词器时,只能给text类型的字段指定分词器,不能给keyword指定分词器
            }
        }
    }
}

ik分词器有2种模式

  • ik_max_word:分词粒度细,检索结果中往往包含了关联性不大的条目。eg.小米笔记本 => 小米、笔记本、笔记、本
  • ik_smart:分词力度粗,检索结果的相关性大,用得较多。eg. 小米笔记本 => 小米、笔记本

 

查询时可以指定分词器

GET /mall/_search
{
    "query": {
        "match": {
            "goods_name": {
                "query": "苹果汁",
                "analyzer": "ik_max_word"  #指定要使用的分词器,查询时可以对keyword、text类型指定分词器
            }
        }
    }
}

 

查看分词结果

GET /_analyze
{
 "analyzer": "ik_max_word",  #指定要使用的分词器
 "text": "小米笔记本"
}

 
分词器优先级:查询语句中指定的分词器 > mapping中指定的分词器 > 未指定时默认的standard分词器

 

排序、分页

#查询结果排序
GET /mall/_search
{
    "query": {
        "match": {"goods_name": "苹果"}
    },
    "sort": [  #只有一个排序字段时,可以省略数组符号[]
        {"goods_price": {"order": "asc"}}  #asc、desc
    ]
}


#查询结果分页
GET /mall/_search
{
    "query": {
        "match": {
            "goods_name": "苹果"
        }
    },
    "from": 10,  #指定开始位置的index,缺省时默认0
    "size": 5  #指定条数。此处返回的是第10、11、12、13、14一共5条
}

 

highlight 高亮显示关键字

GET /mall/_search
{
    "query": {
        "match": {
            "goods_name": "苹果"
        }
    },
    "highlight": {  #高亮显示
        "fields": {
            "goods_name": {  #指定字段,一般是查询的字段
                "pre_tags": "<span style='color:red'>",  #开始标签
                "post_tags": "</span>"  #结束标签
            }
        }
    }
}

高亮显示只是给结果中的关键字加上指定标签,代替原来的内容。开始、结束标签缺省时默认为<em></em>。

对于match等要使用分词器的查询,是把分词后得到的词作为关键字加标签,eg. “mac pro”分词为“mac”、“pro”,匹配到的“mac pro 16英寸”会变成“<em>mac</em> <em>pro</em> 16英寸”。一般加行内标签,不加块级标签。

 

suggest 查询建议

es有3个建议器(suggester)

  • term suggester: 词条建议器,对输入的搜索关键字分词,对每个词(term)进行模糊查询提供对应的词项建议
  • phrase suggester:短语建议器,在term的基础上,会考量多个term之间的关系,⽐如是否同时出现在索引的原⽂⾥,相邻程度,以及词频等
  • completion suggester:自动补全建议器

前2个可以实现自动纠错,第三个可以实现补全提示

GET /mall/_search
{
    "suggest": {
        "goods_name_suggest": {  #自定义的suggest名称
            "text": "苹果",  #要搜索的关键字
            "term": {"field": "goods_name"}  #要使用的建议器、要检索的字段
        }
    }
}

 

aggs 聚合

聚合简介

数据聚合即数据的统计分析。常见的聚合方式如下

  • 桶聚合:对数据进行分组,相当于sql的group by
  • 指标聚合:对数据集使用max() 、min()、avg()、sum()之类的聚合函数,统计最大值、最小值、平均值、总量等指标
     

主要概念

  • 桶(bucket):满足特定条件的文档集合,实质是基于条件来划分文档
  • 指标(metric):数据集的一些特征值,比如最大值、最小值、平均值
     

支持嵌套,桶聚合可以嵌套桶聚合、指标聚合,指标聚合也可以嵌套桶聚合、指标聚合。

对于嵌套的聚合,es只需遍历一次数据,不用多次循环遍历数据,速度快、性能高,但聚合、排序都很耗内存。

聚合可以对index中的所有文档使用,也可以对查询结果使用。
 

聚合文本类型时要注意:只能对keyword字段使用聚合,不能对text字段使用聚合,如果对text使用聚合,会报错

Text fields are not optimised for operations that require per-document field data like aggregations and sorting, so these operations are disabled by default

text存储的是文章内容、评论之类的长文本,没必要聚合,定义mapping时要严格区分keyword、text字段。

 

桶聚合
#对全样数据进行聚合,eg. 统计每个球队的球员(人数)
GET /nba/_search
{
    "aggs": {
        "team_buckets": {  #自定义的聚合名称
            "terms": {  #按关键字划分。不能换成term、match
                "field": "team_name",  #指定要聚合的字段。按队名划分桶,一个队一个桶,桶内是该队所有球员的文档,字段的值即桶名
              # "include": ["湖人队","公牛队","勇士队"],  #指定参与聚合的值,只聚合这些值
			  # "exclude": ["湖人队"],  #指定不参与聚合的值,除了这些值其它都参与聚合
                "size": 10,  #返回的桶数,默认返回所有的桶
		      #	"order": {  #桶的排序方式
		      #     "_count": "desc"  #按桶中的文档数降序排列,默认也是按桶中的文档数降序排列
		      #  }
            }
        }
    },
    "size": 0  #返回的文档数,默认返回每个桶中的所有文档,设置为0即不返回文档,可提高查询速度。如果不需要获取具体文档,可设置为0
}

#include、exclude可以使用通配符,点号表示任意一个字符,*表示任意多个字符



#按范围划分桶之自定义区间,eg.统计指定年龄区间上的用户数量
GET /user/_search
{
    "aggs": {
        "age_range": {  #自定义的聚合名称
            "range": {  #按范围划分
                "field": "age",  #指定字段
                "ranges": [  #划分区间
                    {
                        "to": 12,  # [from,to),只要有一个即可
                        "key": "12岁以下"  #区间别名,类似于sql查询结果集的字段别名,非必需
                    },
                    {
                        "from": 12,
                        "to": 18,
                        "key": "12~18岁"
                    },
                    {
                        "from": 18,
                        "key": "18岁以上"
                    }
                ]
            }
        }
    },
    "size": 0
}



#按时间日期划分桶,eg.统计每月的订单数
GET /order/_search
{
    "aggs": {
        "order_count": {
            "date_histogram": {
                "field": "create_date",
                "calendar_interval": "month",  
                "format": "yyyy-MM-dd"
            }
        }
    },
    "size": 0
}

 

指标聚合

常用的聚合函数

  • value_count 该字段有值(不为空)的文档数
  • max
  • min
  • avg
  • sum
  • stats 同时统计以上5个指标
  • extended_stats 统计更多的信息,比如平⽅和、⽅差、标准差
  • cardinality 去重计数
#对全样数据进行聚合,eg. 统计nba球员的平均年龄
GET /nba/_search
{
    "aggs": {
        "avg_age": {  #自定义的聚合名称
            "avg": {  #要使用的聚合函数
                "field": "age"  #要统计的字段
            }
        }
    },
    "size": 0
}


#对查询结果进行聚合,eg. 统计⽕箭队球员的平均年龄
GET /nba/_search
{
    "query": {
        "term": {
            "team_name": "火箭队"
        }
    },
    "aggs": {
        "avgAge": {
            "avg": {
                "field": "age"
            }
        }
    },
    "size": 0
}


#去重计数,eg. 统计某个活动的中奖人数
POST /raffle/_search
{
    "query": {
        "term": {
            "activity_id": 123
        }
    },
    "aggs": {
        "coun_user": {
            "cardinality": {
                "field": "user_id"
            }
        }
    },
    "size": 0
}

 

嵌套聚合
#桶聚合嵌套指标聚合。先桶聚合按球队划分球员,再指标聚合得到每只球队球员的平均年龄,再按平均年龄对桶进行排序
GET /nba/_search
{
    "aggs": {
        "team_avg_age": {
            "terms": {
                "field": "team_name",
                "order": {
                    "avg_age": "asc"  #avg_age是年龄聚合的名称
                }
            },
            "aggs": {
                "avg_age": {
                    "avg": {
                        "field": "age"
                    }
                }
            }
        }
    },
    "size": 0
}



#桶嵌套桶。先按省份划分用户,再在每个桶中按城市划分
GET /user/_search
{
    "aggs": {
        "count_province": {
            "terms": {
                "field": "province"
            },
            "aggs": {
                "count_province_city": {
                    "terms": {
                        "field": "city"
                    }
                }
            }
        }
    },
    "size": 0
}

 

百分比
#查看学生成绩的分布情况
POST /student/_search
{
    "aggs": {
        "percent_score": {
            "percentiles": {
                "field": "score",
                "percents": [25, 50, 75]  #指定百分比的分数,缺省时默认为 [1.0, 5.0, 25.0, 50.0, 75.0, 95.0, 99.0]
            }
        }
    },
    "size": 0
}

 
返回值示例

"25.0" : 68,  # <=68分的占25.0%
"50.0" : 80.5,  # <=80.5分的占50.0%
"75.0" : 92  # <=92分的占75.0%

 

图表

聚合能把统计结果转换成易于显示为图表的数据,方便前端展示

#eg. 统计学生成绩的分布情况。也属于桶聚合
GET /student/_search
{
   "aggs":{
      "socre_info":{
         "histogram":{
            "field": "score",
            "interval": 20  #指定间距。[0,20),[20,40)...一个区间一个桶,区间上有文档时才划分为桶
         }
      }
   },
   "size" : 0
}


#按成绩划分区间(桶),统计每个区间上的文档,再计算每个桶内的平均分。桶聚合+指标聚合
GET /student/_search
{
    "aggs": {
        "socre_info": {
            "histogram": {
                "field": "score",
                "interval": 20
            },
            "aggs": {
                "avg_score": {
                    "avg": {
                        "field": "score"
                    }
                }
            }
        }
    },
    "size": 0
}

 
返回值示例

"aggregations" : {
    "socre_info" : {
      "buckets" : [
        {
          "key" : 40.0,  # [40, 60) 有4人,4人平均分50.5
          "doc_count" : 4,
          "avg_score" : {
            "value" : 50.5
          }
        },
        {
          "key" : 60.0,  # [60, 80) 有20人,21人平均分72
          "doc_count" : 21,
          "avg_score" : {
            "value" : 72.0
          }
        },
        {
          "key" : 80.0,  # [80, 100) 有9人,9人平均分89
          "doc_count" : 9,
          "avg_score" : {
            "value" : 89.0
          }
        }
      ]  #没有[100, 120) 这个桶,说明无人考满分
    }
  }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值