我们都知道Elasticsearch是一个搜索服务器,所以搜索是它最重要的功能,所以下面就介绍几种Elasticsearch中搜索数据的方法
我们搜索某个类型下的全部数据使用的方法为_search方法,如
GET /索引名/类型名/_search
使用上面的url进行请求,我们就可以得到如下数据
{
"took" : 103,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : 2,
"max_score" : 1.0,
"hits" : [
{
"_index" : "test",
"_type" : "test_type",
"_id" : "VyLjvXQBC0UiotsuDD-X",
"_score" : 1.0,
"_source" : {
"age" : "18"
}
},
{
"_index" : "test",
"_type" : "test_type",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"user" : "abc"
}
}
]
}
}
响应字段解释:
took:表示搜索耗时,单位为毫秒。
timed_out:表示搜索是否超时。
_shards:表示有多少个分片被搜索了,成功搜索的分片数量、跳过的分片数量以及失败的分片数量。
hits:表示搜索结果。
hits.total:表示搜索到的文档总数量。
hits.hits:表示搜索到的文档数组,默认显示搜索到的前十个文档。
如果我们想搜索某个特定商品,那么就可以使用query string search了。使用query string search很简单,只需要在我们的搜索URL后面添加q参数来指定搜索条件
比如:
搜索用户中为abc的数据,user表示文档中的字段
GET /索引名/类型名/_search?q=user:abc
我们还可以不指定字段,直接搜索相关数据,这样就会搜索全部字段
GET /索引名/类型名/_search?q=abc
我们还可以在搜索条件前加+或者-符合,+是必须包含,-是不包含
query string search一般用于简单的,临时的查询,比如curl,如果查询比较复杂,是不会使用query string search的
这是Elasticsearch特定的查询语言,query DSL将json格式的搜索条件放到http request body请求体中,然后发送请求进行搜索,这是Elasticsearch一种比较好的搜索方法,不仅方便构建查询语法,还可以构建各种复杂的语法,实用性较强。
例:
GET /索引/类型/_search
{
"query" : {
"match_all" : { }
},
"sort": [
{ "age": "desc" }
]
}
上面语句表示query表示查询的定义, match_all表示查询指定索引下的所有文档。 sort则是一个排序字段,表示根据 age字段的值降序排列。
DSL其他语法
搜索某个字段是否包含某个字段,使用以下语法
"query" : {
"match" : {
"字段名" : "搜索值"
}
}
指定返回个数使用size,在后面指定返回的个数,如果不指定的话,size默认为10。
例:
{
"query": { "match_all": {} },
"size": 1
}
分页查询
有了指定返回个数的size,那么我们只需要再指定从第几个文档开始查询,那么就可以实现分页查询了,指定从第几个文档开始查询使用from字段,如果不指定from,则默认值为0。
例:
从第10个文档开始查询,查询10个文档
{
"query": { "match_all": {} },
"size": 10,
"from": 10
}
自定义返回字段
自定义返回字段使用_source字段,默认情况下,查询结果中会返回查询文档的所有字段,如果不需要返回所有字段,则可以自定义返回字段,如下:
{
"query": { "match_all": {} },
"_source": ["user","age"]
}
指定查询条件
指定查询条件使用match,我们可以再match中指定需要搜索的字段和值
例:
搜索age为18的文档
{
"query": { "match": { "age": 18 } }
}
如果我们想搜索多个值,那么在搜索值之间加空格即可
例:
搜索name中包含mm或者yy的文档
{
"query": { "match": { "name": "mm yy"} }
}
条件查询除了match查询,还有term查询,match查询和term查询的区别在于match查询会将查询值进行分词,然后进行查询,而term查询不会对查询值进行分词
例:
{
"query": {
"term": {
"name": "mm"
}
}
}
terms还有一个方法terms,用于多个查询值的搜索,即满足terms条件中的任意一个或者多个都符合查询条件
例:
{
"query": {
"term": {
"name": [ "aa","bb"]
}
}
}
Name中包含aa或者bb或者aa bb的文档都会被输出
短语查询
有时候,一个单词的查询不能满足我们的需求,我们需要查询多个单词组成的短语,短语查询使用match_phrase
例:
{
"query": { "match_phrase": { "name": "mm yy"} }
}
注:如果我们使用math,那么表示的是name字段包含mm或者yy的文档,当使用match_phrase时,表示的是查询name包含mm yy的文档
范围搜索
将文档与具有一定范围内字词的字段进行匹配。 Lucene查询的类型取决于字段类型,对于字符串字段,TermRangeQuery,对于数字/日期字段,查询是NumericRangeQuery。
比如我们想查询年龄在18到20之间的文档
{
"query": {
"range" : {
"age" : {
"gte" : 18,
"lte" : 20
}
}
}
}
例:
范围查询接受以下参数:
gte: 大于或等于
gt: 大于
lte: 小于或等于
lt: 小于
boost: 设置查询的提升值,默认为1.0
条件查询
很多时候,我们都需要根据文档是否满足我们的条件,满足的才进行输出,这就要使用到ES的Bool查询
Bool查询包括四种子句,must,filter,should,must_not,这里我先介绍must、should、must_not
must
返回的文档必须满足must子句的条件,并且参与计算分值,可以有一个或者多个子句,多个子句相当于and,文档需要满足must中所有子句的条件才能输出
例:
如下查询表示查询name中包含“mm”和“yy”的所有文档
{
"query": {
"bool": {
"must": [
{ "match": { "name": "mm" } },
{ "match": { "name": "yy" } }
]
}
}
}
should
返回的文档满足should子句中的某个条件。在一个Bool查询中,如果没有must或者filter,有一个或者多个should子句,那么只要满足一个就可以返回。minimum_should_match参数定义了至少满足几个子句,在多个子句中,should相当于or
例:
如下查询表示查询name中包含“mm”或者“yy”的所有文档
{
"query": {
"bool": {
"should": [
{ "match": { "name": "mm" } },
{ "match": { "name": "yy" } }
]
}
}
}
must_not
返回的文档必须不满足must_not定义的条件,如果有多个子句,那么文档必须不满足全部子句的条件才能返回
例:
如下查询表示查询name中不包含“mm”和“yy”的所有文档
{
"query": {
"bool": {
"must_not": [
{ "match": { "name": "mm" } },
{ "match": { "name": "yy" } }
]
}
}
}
must、should、must_not还可以组合起来使用
例:
如下查询表示查询age为18,state不为1的所有文档
{
"query": {
"bool": {
"must": [
{ "match": { "age": "18" } }
],
"must_not": [
{ "match": { "state": "1" } }
]
}
}
}
过滤查询
过滤查询就要使用到上面bool查询中的filter子句
我们知道在_search方法返回的结果中有一个_score字段,这个字段表示返回的文档与搜索条件的匹配度,匹配度越高,则得分越多,而且_search方法的默认排序也是按照_score来进行排序的。
但有很多时候我们对这个分数是不需要的,那么这个时候使用filter查询会更好,filter查询不会去计算分值,而且filter查询可以被缓存到内存中,在重复搜索时,速度会比较快,因此效率也就更高一些。
比如,我们搜索年龄18到20的文档数据,就可以使用过滤查询
例:
{
"query": {
"bool": {
"must": { "match_all": {} },
"filter": {
"range": {
"age": {
"gte": 18,
"lte": 20
}
}
}
}
}
}
聚合查询
在MYSQL等一些聚合查询,我们都使用过GROUP BY聚合函数。而ES的聚合操作和SQL中的GROUP BY分组函数类似,都提供了分组和数理统计的能力
以下是一个最简单的聚合查询,表示根据年龄分组,并按照数量进行排序,数量越多排序越先
ES中的聚合API的调用格式如下:
"aggregations" : { // 表示聚合操作,可以使用aggs替代
"" : { // 聚合名,可以是任意的字符串。用做响应的key,便于快速取得正确的响应数据。
"" : { // 聚合类别,就是各种类型的聚合,如min、terms等
<aggregation_body> // 聚合体,不同的聚合有不同的body
}
[,"aggregations" : { [<sub_aggregation>]+ } ]? // 嵌套的子聚合,可以有0或多个
}
[,"" : { ... } ]* // 另外的聚合,可以有0或多个
}
例:
POST my_index/_search
{
"size": 1,
"aggs": {
"group_by_age": {
"terms": {
"field": "age"
}
}
}
}
aggs字段表示一个聚合操作,group_by_age是一个操作的名称,不固定,在上面例子我们按照年龄进行分组,所以取名为group_by_age,当你对其他属性进行分组,则可以改成其他名称,terms用来表示分组,在里面指定分组的字段和值
注:如果size为0,则不显示文档信息,size为显示的文档数量
返回
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 1.0,
"hits": [
{
"_index": "test",
"_type": "test_type",
"_id": "VyLjvXQBC0UiotsuDD-X",
"_score": 1.0,
"_source": {
"name": "哈哈哈",
"id": "123",
"age": "18"
}
}
]
},
"aggregations": {
"group_by_age": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "18",
"doc_count": 2
}
]
}
}
}
返回参数说明:
hits:返回的文档信息
aggregations:聚合信息
buckets中的key表示聚合值,doc_count表示文档数量
除了分组操作,ES的聚合查询还有avg、max、min、sum等,分别表示求平均、求最大值、求最小值、求和
例:查询name为哈哈哈的平均年龄
{
"query": {
"match": {
"name": "哈哈哈"
}
},
"aggs": {
"my_avg": {
"avg": {
"field": "age"
}
}
},
"_source": ["name", "age"]
}
查询年龄最大的值
{
"query": {
"match_all": {}
},
"aggs": {
"max_age": {
"max": {
"field": "age"
}
}
},
"size": 0
}
第一次执行聚合 操作查询,出现了以下错误
Fielddata is disabled on text fields by default. Set fielddata=true on [age] in order to load fielddata in memory by uninverting the inverted index. Note that this can however use significant memory. Alternatively use a keyword field instead
原因:Elasticsearch 5.x版本以后,对text类型的字段进行排序或者聚合等操作,是用单独的数据结构(fielddata)缓存到内存里的,默认是不开启的,需要单独开启,即如果我们想对text类型的字段进行排序或者聚合,需要将字段的fielddata属性设置为true
开启如下:
//在mapping方法中的properties字段开启某个字段的fielddata属性,my_index为所以名,my_type为类型名
PUT my_index/_mapping/my_type
{
"properties":{
"age":{
"type":"text",
"fielddata":true
}
}
}
返回 acknowledged:true则表示开启成功
模糊查询
Elasticsearch 模糊查询 可以使用wildcard(通配符查询)、regexp(正则查询)、prefix(前缀查询)。这里主要讲一下wildcard(通配符查询)、prefix(前缀查询)
POST 索引名/类型名(可有可无)/_search
{
"query": {
"wildcard": {
"file_name": {
"value": "*测试*"
}
}
}
}
wildcard 通配符查询是使用通配符来进行查询,? 匹配任意一个字符, * 匹配 0 或多个字符
如上面的测试可以匹配所有file_name中包含测试的文档
prefix(前缀查询)
匹配包含具有指定前缀的项(not analyzed)的字段的文档。前缀查询对应 Lucene 的 PrefixQuery 。
查询name中以哈为前缀的文档
{
"query": {
"prefix": {
"name": "哈"
}
}
}
注意:模糊查询的性能都比较差,当查询内容较多,数据量较大时建议将该字段设置成text进行分词,然后通过match进行匹配。