初步了解ES

一、ES基础查询

1、es基础查询

1.1 准备数据

# 准备数据
PUT test_index/_doc/1
{
  "name":"顾老二",
  "age":30,
  "from": "gu",
  "desc": "皮肤黑、武器长、性格直",
  "tags": ["黑", "长", "直"]
}

PUT test_index/_doc/2
{
  "name":"大娘子",
  "age":18,
  "from":"sheng",
  "desc":"肤白貌美,娇憨可爱",
  "tags":["白", "富","美"]
}

PUT test_index/_doc/3
{
  "name":"龙套偏房",
  "age":22,
  "from":"gu",
  "desc":"mmp,没怎么看,不知道怎么形容",
  "tags":["造数据", "真","难"]
}

PUT test_index/_doc/4
{
  "name":"石头",
  "age":29,
  "from":"gu",
  "desc":"粗中有细,狐假虎威",
  "tags":["粗", "大","猛"]
}

PUT test_index/_doc/5
{
  "name":"魏行首",
  "age":25,
  "from":"广云台",
  "desc":"仿佛兮若轻云之蔽月,飘飘兮若流风之回雪,mmp,最后竟然没有嫁给顾老二!",
  "tags":["闭月","羞花"]
}

1.2 match和term

match 和 term 中必须要加条件,但是我们有时候需要查询所有,不带条件,需要用到 match_all
match:会将查询条件分词,然后进行查询。会先对搜索词进行分词,比如“白雪公主和苹果”,会分成“白雪”“公主”“苹果”。含有相关内容的字段,都会被检索出来。
term:会将查询条件完整的匹配相关字段。在ES中保存的文档,都会对单词进行分词处理,然后建立倒排索引,比如我们存了一个HelloWorld,但是很有可能会被分词成hello和world两个单词,这个时候如果你用term查询,通过HelloWorld是查不到的,因为term是不做分词处理的。这个时候可以通过match或者字段.keyword来代替。
通过term这种查询方式在blogs这个索引中查找title为Quick disjunction的文档,是找不到的,因为Quick disjunction已经做了分词处理
POST blogs/_search
{
  "query": {
    "term": {
      "title": {  # 查找的字段
        "value": "Quick disjunction"  # 查找的数据
      }
    }
  }
}


将上面的语句稍作改动即可
POST blogs/_search
{
  "query": {
    "term": {
      "title.keyword": { # 每个字段都有一个keyword这个属性,是没有分词之前元数据
        "value": "Quick disjunction"
      }
    }
  }
}

做查询,返回的结果会给每一个文档做一个相关性算分,用_score来表示,如果一个查询匹配到多条数据,那么_score最高的会排在最前面,表示匹配度最高。这个算分的过程其实是比较消耗性能的,如果我们不关注这个属性的话,可以通过Filter的方式绕过算分这个环节,避免一些开销,并且Filter还可以利用缓存,提升查询效率。

POST blogs/_search
{
  "query": {
    "constant_score": {  # constant:常数,表示跳过算分,返回的_score是一个常数
      "filter": {
        "term": {
          "title.keyword": {
            "value": "Quick disjunction"
          }
        }
      }
    }
  }
}

但是可能大多数情况下,我们还是比较关注score这个分数的,甚至有可能希望为某一次特殊的查询去调整这个分数,比如百度竞价排名的广告。。那么这个时候可以通过另一个参数boost (提高,使增长)这个参数去实现。他是一个浮点类型的数字,默认是1.0,如果我们想去降低分数,只需要把他控制在0.0-1.0之间,越小分数越低,如果我们想提高分数,只需要把他从1.0开始往大调整,越大算出来的分数越高。

POST blogs/_search
{
  "query": {
    "term": {
      "title.keyword": {
        "value": "Quick disjunction",
        "boost": 2
      }
    }
  }
}

默认情况下,Elasticsearch 将字词查询限制为最多65536个字词。 这包括使用术语查找获取的术语。 你可以使用 index.max_terms_count 设置更改此限制。

1.3 match_all

# 查询所有
GET test_index/_search
{
  "query": {
    "match_all": {}
  }
}

1.4 前缀查询match_phrase_prefix

# 查英文   beautiful   --->be开头的---》能查到

GET test_index/_search
{
  "query": {
    "match_phrase_prefix": {
      "name": "顾"
    }
  }
}

1.5 match_phrase。短语模糊查询.

即它会将给定的短语(phrase)当成一个完整的查询条件。match_phrase与slop一起用,能保证分词间的邻近关系,slop参数告诉match_phrase查询词条能够相隔多远时仍然将文档视为匹配,默认是0。为0时必须相邻才能被检索出来。

# 会分词,分词完成后,如果写了slop,会按分词之间间隔是slop数字去抽

GET t1/doc/_search
{
  "query": {
    "match_phrase": {
      "title": {
        "query": "中国世界",
        "slop": 2
      }
    }
  }
}

1.6 多条件查询,或的关系

# 只要name或者desc中带龙套就查出来

GET test_index/_search  
{
  "query": {
    "multi_match": {
      "query": "龙套",
      "fields": ["name", "desc"]
    }
  }
}

1.7 指定查询字段

GET test_index/_search 
{
  "query": { "match_all": {} },
  "_source": ["id", "createTime"]
}

1.8 wildcard通配符查询。

match_phrase与slop一起用,能保证分词间的邻近关系,slop参数告诉match_phrase查询词条能够相隔多远时仍然将文档视为匹配,默认是0。为0时 必须相邻才能被检索出来。与mysql中的 Like 是一个套路,可以在查询时,在字符串中指定通配符*和占位符?Wildcard 性能会比较慢。如果非必要,尽量避免在开头加通配符 ? 或者 *,这样会明显降低查询性能。

wildcard查询支持下面几个参数:

  • value:查询条件,就是我们指定的通配符,目前支持两种:表示可以匹配0到多个字符,?表示匹配任何单个字符。注意在正则表达式中,不能用或者?开头,因为这样可能匹配到大量的数据,导致性能下降严重。
  • boost:用来减少或增加查询相关性算分的参数。
  • case_insensitive:默认值false,如果设置为true,表示不区分大小写。
  • rewrite:可以重写查询方法,目前还没实践到,关于更多说明可以参考:rewrite parameter
 {
    "wildcard": {
        "form_name.keyword": "*very*"
    }
}

# 查询的内容非空
{
    "wildcard": {
        "form_name": "*"
    }
}
 
GET test_index/_search
{
  "query": {
    "wildcard": {
      "user.id": {
        "value": "ki*y",
        "boost": 1.0,
        "rewrite": "constant_score"
      }
    }
  }
}

1.9 查看分词结果

# fieldName可以字段名,也可以是字段名.
http://localhost:9200/{index}/{type}/{id}/_termvectors?fields={fieldName} 
# 具体示例
http://localhost:9200/a_index_text/_doc/n1A6ioUBFQ9q5qTUK1mH/_termvectors?fields=phone.short_char

利用该字段的分词器查看分词结果。查看user_addresses索引上address_name字段的分词效果

GET user_addresses/_analyze
{
  "field": "address_name",
  "text": "山东省青岛市黄岛区"
}

查看指定分词器及filter(GET或者POST都行)

GET _analyze
{
  "tokenizer": "standard",
  "filter": ["lowercase"],
  "text":"Hello WORLD"
}
# 结果
{
  "tokens" : [
    {
      "token" : "hello",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "world",
      "start_offset" : 6,
      "end_offset" : 11,
      "type" : "<ALPHANUM>",
      "position" : 1
    }
  ]
}

1.10查看指定索引总数据量

GET rest_logs-*/_count

# 结果
{
  "count" : 12371823,
  "_shards" : {
    "total" : 631,
    "successful" : 631,
    "skipped" : 0,
    "failed" : 0
  }
}

1.11 统计总存储空间占用

GET /_cat/shards?v

# 结果
index                                     shard prirep state       docs   store ip             node
rest_logs-2022.08.03                      0     r      STARTED  8357996   2.6gb ***    default-es-default-0
rest_logs-2022.08.03                      0     p      STARTED  8357996   2.6gb ***  default-es-default-3
rest_logs-2022.10.08                      0     r      STARTED   721722 290.6mb ***  default-es-default-4

1.12 查看正在数据迁移的索引及进度

GET _cat/recovery
rt_yonghu_serv_hsicrm_wo_userauthenticationbase       0 2.2m  peer        done  172.16.81.124 es-cn-7pp29cxc0000dlvzw-8e9e6447-0008 172.16.81.119 es-cn-7pp29cxc0000dlvzw-8e9e6447-0006 n/a n/a 159 159 100.0% 159 2658832139  2658832139  100.0% 2658832139  0       0       100.0%
rt_yonghu_serv_hsicrm_wo_userauthenticationbase       0 2.5m  peer        done  172.16.81.120 es-cn-7pp29cxc0000dlvzw-8e9e6447-0005 172.16.81.121 es-cn-7pp29cxc0000dlvzw-8e9e6447-0002 n/a n/a 150 150 100.0% 150 2659359161  2659359161  100.0% 2659359161  0       0       100.0%

1.13 ids查询。

ids是相对来说比较简单的一种dsl,类似于mysql的where id in ()的语义,他支持value属性,可以传入一个数组,里面填上你想要查询的ID的文档即可

GET rest_logs-*/_search
{
  "query": {
    "ids" : {
      "values" : ["1", "4", "100"]  # 返回属性_id为14100的文档,如果存在的话
    }
  }
}

1.14 exists(Exists Query)

exists是用来匹配文档的mapping中是否包含某一个字段,如果是就返回。比如我有下面两个文档:ID为4的文档有两个字段:title和body,ID为5的文档还有一个字段content。那么如果我们想要查询包含content字段的文档,就可以用exists去做。

POST test_index/_search
{
  "query": {
    "exists": {"field": "content"}
  }
}

还有一点需要注意的是,如果content的value值为null或者[],那么是不会被匹配到的。

1.15 prefix (Prefix Query)

prefix查询很好理解,就是将查询关键字作为一个前缀进行匹配,比如下面的例子,如果title是以Ela作为前缀的都会被匹配到,这里是区分大小写的,如果要设置不区分大小写,可以将case_insensitive设置为true。

POST test_index/_search
{
  "query": {
    "prefix": { "title.keyword":"Ela"}
  }
}

1.16 fuzzy(Fuzzy Query)

上面提到的term查询是一种精确查询,必须要求你输入的查询条件和文档中的数据完全匹配才可以。但是有时候可能用户忘了一个单词怎么写,只记得前3个字母,或者拼写错了,那么如果用term是查不到的。这个时候我们就需要根据用户的输入做一个猜测,进行一个模糊匹配。那么fuzzy正好是为了解决这个问题而出现的。

为了理解fuzzy的用法,这里我们需要先了解一个概念:编辑距离,他是一个单词要变成另一个单词,需要经过几次转换的这个次数。比如java这个单词要变成jbva,只需要将b替换成a,因此这个编辑距离就是1。那么这个转换总共有四种手段

改变其中一个字母,比如box -> fox
删除其中一个字符,比如black -> lack
插入一个新的字母,比如sic -> sick
调整两个相邻字母的位置,比如cat -> act
fuzzy实现模糊查询就是基于这个编辑距离去做的,他会将用户输入的搜索条件,结合一个编辑距离,然后基于我们上面提到的四种手段去做一个扩展和变形,得到一个集合,这个过程我们可以叫做扩展模糊选项,然后将扩展后的模糊选项作为查询条件展开精确匹配,最终将所有的结果进行返回。那么这个编辑距离就需要我们去指定,在es中是通过fuzziness去指定的,他的含义就是最大编辑距离。

POST test_index/_search
{
  "query": {
    "fuzzy": {
      "title.keyword": {
        //es中存储的是ElasticSearch,编辑距离是2,那么只要你的输入经过两次转换都变成ElasticSearch,就会匹配到
        "value": "ElasticSearh",   
        "fuzziness": 2
      }
    }
  }
}

fuzzy除了可以指定最大编辑距离之外,还有其他几个参数,这里在做一个总结:

fuzziness:最大编辑距离,值可以是数字类型,也可以是AUTO:[low],[high]这种格式,表示如果查询的单词长度在 [0,low) 这个范围内,编辑距离为0,如果在 [low,high) 这个范围之内,编辑距离为1,如果大于high,则编辑距离为2;除了这两种格式外,还支持AUTO这种写法,等同于AUTO:3,6

max_expansions:最大扩展数量,前面我们提到了扩展模糊选项,假如一个查询扩展了3到5个扩展选项,那么是是很有意义的,如果扩展了1000个模糊选项,其实也就意义不大了,会让我们又迷失在海量的数据中。因此有了max_expansions这个参数,限制最大扩展数量,默认值是50。切记这个值不可以太大,否则会导致性能问题

prefix_length:指定开始多少个字符不可以被模糊

transpositions:boolean值,默认true,表示扩展模糊选项的时候,是否允许两个相邻字符的位置互换。实践过程设置了false,理论上我存储ElasticSearch,检索ElasticSaerch应该搜不到,但是却搜索到了。有点不太理解。

1.17 range(Range Query)

range查询,更好理解了,就是范围查询嘛,他的语法可以参考下面的代码。这里呢出现了gte、lte是什么意思呢,其实就是ES中对于范围的描述,这里给大家总结一下:

gt:大于
gte:大于等于
lt:小于
lte:小于等于

GET test_index/_search
{
  "query": {
    "range": {
      "age": {
        "gte": 10,
        "lte": 20,
        "boost": 2.0
      }
    }
  }
}

除了上面说到的表示范围的参数之外,range查询还支持下面的一些参数:

format:string类型,支持对查询的时间进行格式化,如果我们不提供会用默认的格式:“yyyy-MM-dd”
time_zone:用来指定时区,可以是UTC时区的偏移量,比如+01:00,或者IANA时区,比如America/Los_Angeles
relation:用来表示我们指定的范围和文档中数据的关系。一共有三种枚举值:

  • INTERSECTS (Default):匹配的文档中的范围字段的值和我们的指定的查询范围是一个交集的关系
  • CONTAINS:匹配的文档中的范围字段的值完全包含了我们指定的查询范围
  • WITHIN:匹配的文档中的范围字段的值在我们的查询范围之中

range查询也可以用在时间上,并且还可以做时间的计算,在es中,用y表示年,M表示月,d表示天,h表示小时。。。因此可以用+1d、-2h这种方法来进行时间的计算,可以通过/d表示四舍五入到最近的一天。也可以支持now取当前时间。关于时间计算的更多资料可以参考:Date Math

GET test_index/_search
{
  "query": {
    "range": {
      "timestamp": {
        "time_zone": "+01:00", 
        "gte": "2020-01-01T00:00:00", 
        "lte": "now"
      }
    }
  }
}

1.18 regexp(Regexp Query)

通配符查询,功能还是太有限了,如果能支持正则表达式岂不是堪称完美?说曹操曹操到,正则表达式查询,ES也为我们提供了。例如下面的例子,会匹配到user.id是以k开头,y结尾的单词。中间的.*表示匹配任意长度的任意字符像ky、kay、kimchy等都可以被匹配到。

在regexp中,有这么几个参数,需要关注一下,除过value,其他都是可选参数:

  • value:查询条件,指定我们输入的正则表达式,关于更多的正则表达式语句可以参考:Regular expression syntax
  • flags:可以通过这个参数为正则表达式提供一些更丰富的选项
    • ALL:默认值,允许所有的操作符
    • COMPLEMENT:允许操作符,用来实现一个求反的效果,例如:abc可以匹配到adc,aec,但是不会匹配到abc。
    • INTERVAL:允许操作符<>,用来匹配数字范围,例如:foo<01-100>可以匹配到foo01一直到foo100
    • INTERSECTION:允许操作符&,用来and的意思,就是说&符号两遍的表达式都满足才能被匹配到,例如aaa.+&.+bbb可以匹配到aaabbb
    • ANYSTRING:允许操作符@,用来匹配任何一个完整的字符串。例如@&~(abc.+)匹配任何字符串,除过以abc开头的
  • case_insensitive:默认值false,如果设置为true,表示正则表达式不区分大小写。
  • max_determinized_states:据官网介绍,es底层对正则表达式的解析是通过lucene来实现的,那么在lucene解析正则表达式的过程中,会将每个正则表达式转换为包含多个确定状态的状态机,默认值是10000,我们也可以通过这个参数去修改。而且据官网说这个生成状态机的过程是一个很耗时的过程,为了防止资源耗尽,我们应该控制这个参数。如果一个正则表达式特别复杂的话,也可以适当的去把这个参数往大调整。
  • rewrite:可以重写查询方法,目前还没实践到,关于更多说明可以参考:rewrite parameter
GET test_index/_search
{
  "query": {
    "regexp": {
      "user.id": {
        "value": "k.*y",
        "flags": "ALL",
        "case_insensitive": true,
        "max_determinized_states": 10000,
        "rewrite": "constant_score"
      }
    }
  }
}

2、Elasticsearch之排序查询

# 结构化查询 
GET test_index/_search
{
  "query": {
    "match": {
      "name": "zz"
    }
  }
}


# 排序查询---》可以按多个排序条件-->sort的列表中继续加
GET test_index/_search
{
  "query": {
    "match": {
      "from": "gu"
    }
  },
  "sort": [
    {
      "age": {
        "order": "asc"
      }
    }
  ]
}


# 不是所有字段都可以排序--->只能是数字字段

3、Elasticsearch之分页查询

# from 和 size,from表示偏移量,从0开始,size表示每页显示的数量

GET test_index/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ], 
  "from": 3,
  "size": 2
}

4、Elasticsearch之布尔查询

#   must(and)  should(or)  must_not(not)    filter

# must条件  and条件
GET test_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            # 模糊查询
            "from": "sheng"
             #精确查询
            # "from.keyword": "sheng"
          }
        },
        {
          "match": {
            "age": 18
          }
        }
      ]
    }
  }
}


"""
# 咱们认为的and查询,但是实际不行
GET test_index/_search
{
  "query": {
    "match": {
      "from": "sheng",
      "age":18
    }
  }
}
"""


# shoud   or 条件---》搜索框,就是用它
GET test_index/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "from": "sheng"
          }
        },
        {
          "match": {
            "age": 22
          }
        }
      ]
    }
  }
}



# must_not  既不是也不是
GET test_index/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match": {
            "from": "gu"
          }
        },
        {
          "match": {
            "tags": "可爱"
          }
        },
        {
          "match": {
            "age": 18
          }
        }
      ]
    }
  }
}



# 查询 from为gu,age大于25的数据怎么查 filter  /gt> lt< lte=
GET test_index/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "from": "gu"
          }
        }
      ],
      "filter": {
        "range": {
          "age": {
            "lt": 25
          }
        }
      }
    }
  }
}

# age字段等于40且state字段不包含ID的文档
GET test_index/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "age": "40" } }
      ],
      "must_not": [
        { "match": { "state": "ID" } }
      ]
    }
  }
}

5、Elasticsearch之查询结果过滤

# 只查某几个字段

GET test_index/_search
{
  "query": {
    "match": {
      "name": "顾老二"
    }
  },
  "_source": ["name", "age"]
}

6、Elasticsearch之高亮查询

# 默认高亮
GET test_index/_search
{
  "query": {
    "match": {
      "name": "石头"
    }
  },
  "highlight": {
    "fields": {
      "name": {}
    }
  }
}


# 自定义高亮
GET test_index/_search
{
  "query": {
    "match": {
      "from": "gu"
    }
  },
  "highlight": {
    "pre_tags": "<b class='key' style='color:red'>",
    "post_tags": "</b>",
    "fields": {
      "from": {}
    }
  }
}

7、Elasticsearch之聚合函数

# avg   max   min   sum
GET iot_deviceop/_search
{
  "aggs": {
    "aggs_total": {
      "stats": {
        "field": "ts"
      }
    }
  },
  "size": 0
}
# 结果
{
  "took" : 3871,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "aggs_total" : {
      "count" : 420275990,
      "min" : 1.04328556706E12,
      "max" : 1.689223778363E12,
      "avg" : 1.6677018684532202E12,
      "sum" : 7.008950537890268E20
    }
  }
}

# avg 求平均年龄
GET test_index/_search
{
  "query": {
    "match": {
      "from": "gu"
    }
  },
  "aggs": {
    "my_avg": {
      "avg": {
        "field": "age"
      }
    }
  },
  "_source": ["name", "age"]
}


# 对搜索结果进行聚合,使用aggs来表示,类似于MySql中的group by,例如对name字段进行聚合,统计出相同name的文档数量
GET test_index/_search
{
  // 不显示命中(hits)的所有文档信息
  "size": 0,
  "aggs": {
    "group_by_name": {
      "terms": {
        "field": "name.keyword"
      }
    }
  }
}
# 结果
{
  "took" : 170,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "group_by_vin" : {
      "doc_count_error_upper_bound" : 33265,
      "sum_other_doc_count" : 1767999,
      "buckets" : [
        {
          "key" : "第1天签到",
          "doc_count" : 4123788
        },
        {
          "key" : "第2天签到",
          "doc_count" : 2237997
        },
        {
          "key" : "第3天签到",
          "doc_count" : 1842282
        },
        {
          "key" : "第4天签到",
          "doc_count" : 1615952
        },
        {
          "key" : "第5天签到",
          "doc_count" : 1466083
        },
        {
          "key" : "第6天签到",
          "doc_count" : 1365050
        },
        {
          "key" : "第7天签到",
          "doc_count" : 1275374
        },
        {
          "key" : "食材临期即时提醒",
          "doc_count" : 321093
        },
        {
          "key" : "解锁多种智能模式",
          "doc_count" : 289915
        },
        {
          "key" : "远程操控冰箱温度",
          "doc_count" : 282822
        }
      ]
    }
  }
}


# 嵌套聚合,例如对name字段进行聚合统计出相同name的文档数量,再统计出聚合之后的age的平均值
GET test_index/_search
{
  "size": 0,
  "aggs": {
    "group_by_name": {
      "terms": {
        "field": "name.keyword"
      },
      "aggs": {
        "average_age": {
          "avg": {
            "field": "age.keyword"
          }
        }
      }
    }
  }
}
# 结果
{
  "took" : 558,
  "timed_out" : false,
  "_shards" : {
    "total" : 3,
    "successful" : 3,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "group_by_vin" : {
      "doc_count_error_upper_bound" : 33265,
      "sum_other_doc_count" : 1767999,
      "buckets" : [
        {
          "key" : "第1天签到",
          "doc_count" : 4123788,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "第2天签到",
          "doc_count" : 2237997,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "第3天签到",
          "doc_count" : 1842282,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "第4天签到",
          "doc_count" : 1615952,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "第5天签到",
          "doc_count" : 1466083,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "第6天签到",
          "doc_count" : 1365050,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "第7天签到",
          "doc_count" : 1275374,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "食材临期即时提醒",
          "doc_count" : 321093,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "解锁多种智能模式",
          "doc_count" : 289915,
          "average_age" : {
            "value" : null
          }
        },
        {
          "key" : "远程操控冰箱温度",
          "doc_count" : 282822,
          "average_age" : {
            "value" : null
          }
        }
      ]
    }
  }
}



# 对聚合搜索的结果进行排序,例如对name字段进行聚合统计出相同name的文档数量,再统计出聚合之后的age的平均值,按age的平均值降序排列
GET test_index/_search
{
  "size": 0,
  "aggs": {
    "group_by_vin": {
      "terms": {
        "field": "name.keyword",
        "order": {
          "average_age": "desc"
        }
      },
      "aggs": {
        "average_age": {
          "avg": {
            "field": "age.keyword"
          }
        }
      }
    }
  }
}



# 按字段值的范围进行分段聚合,例如分段范围为age字段的[20,30] [30,40] [40,50],之后按gender统计文档个数和balance的平均值
GET test_index/_search
{
  "size": 0,
  "aggs": {
    "group_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 20,
            "to": 30
          },
          {
            "from": 30,
            "to": 40
          },
          {
            "from": 40,
            "to": 50
          }
        ]
      },
      "aggs": {
        "group_by_gender": {
          "terms": {
            "field": "gender.keyword"
          },
          "aggs": {
            "average_balance": {
              "avg": {
                "field": "balance.keyword"
              }
            }
          }
        }
      }
    }
  }
}

# 根据name进行分组,并且取前20GET test_index/_search
{
  "size": 0,
  "aggs": {
    "group_by_vin": {
      "terms": {
        "field": "name.keyword",
        # 取前20"size": 20
        }
      }
    }
  }
}

# 多个平行聚合
GET test_index/_search
{
  "size": 0,
  "aggs": {
      "my-agg-name": {
          "terms": {
            "field": "requestUrl.keyword",
            "size": 5
          }
      },
      "my-agg-name2": {
          "terms": {
            "field": "requestHeader.clientId.keyword",
            "size": 5
          }
      }
    }
}
# 结果
{
  "took" : 6180,
  "timed_out" : false,
  "num_reduce_phases" : 2,
  "_shards" : {
    "total" : 624,
    "successful" : 624,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "my-agg-name2" : {
      "doc_count_error_upper_bound" : 9661,
      "sum_other_doc_count" : 89146934,
      "buckets" : [
        {
          "key" : "callcenter",
          "doc_count" : 147486013
        },
        {
          "key" : "duomai",
          "doc_count" : 49268821
        },
        {
          "key" : "service_employee",
          "doc_count" : 42791694
        }
      ]
    },
    "my-agg-name" : {
      "doc_count_error_upper_bound" : 1819770,
      "sum_other_doc_count" : 236828042,
      "buckets" : [
        {
          "key" : "/api/ecoc/v1/kafkaSend/duoMaiExcute",
          "doc_count" : 49263156
        },
        {
          "key" : "/api/admin/oauth/token",
          "doc_count" : 48386545
        },
        {
          "key" : "/api/dss/userLable/v2/getUserLable",
          "doc_count" : 42795064
        }
      ]
    }
  }
}

# 根据requestUrl进行分组,只取前10个,然后对每个没组里的数据按照@timestamp倒序排列取一条数据(数据里包含requestTime,requestUrl,requestHeader.clientId三个字段),对10条数据分页查询,每页2条;统计所有不同requestUrl的数量
GET rest_logs-*/_search
{
    "query": {
      "match_all": {}  
    },
    "aggs": {
      	# 统计所有requestUrl不同的总数
        "count": {
            "cardinality": {
                "field": "requestUrl.keyword"
            }
        },
        "requestUrl_agg": {
            "terms": {
              	# 按照requestUrl分组
                "field": "requestUrl.keyword",
              	# 取10"size": 10,
              	# 按照分组里的数量倒序排列
          			# 如果是按照分组字段排序,则使用_key
                "order": {
                  "_count": "desc"
                }
            },
            "aggs": {
              	# 自定义名称
                "group_sort": {
                    "top_hits": {
              					# 排序
                        "sort": [
                            {
              									# 分组里按照@timestamp倒序排列
                                "@timestamp": {
                                    "order": "desc"
                                }
                            }
                        ],
												# 分组里的数据,排序后取值的字段
                        "_source": {
                          "includes": [
                            "requestTime",
                            "requestUrl",
                            "requestHeader.clientId"
                            ]
                        }, 
												# 取一条数据,不取一条会报错
												# 报的错:numHits must be > 0; please use TotalHitCountCollector if you just need the total hit count
                        "size": 1
                    }
                },
								# 分页查询
                "bucket_sort": {
                    "bucket_sort": {
                        "sort": [],
                      	# 从哪个位置开始取,offset
                        "from": 0,
                      	# 每页的数量
                        "size": 2
                    }
                }
            }  
        }
    },
		# 此处的size和from没用了
    "size": 0,
    "from": 0
}
# 结果
{
  "took" : 3719,
  "timed_out" : false,
  "num_reduce_phases" : 2,
  "_shards" : {
    "total" : 624,
    "successful" : 624,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 10000,
      "relation" : "gte"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "count" : {
      "value" : 221
    },
    "requestUrl_agg" : {
      "doc_count_error_upper_bound" : 121353,
      "sum_other_doc_count" : 57937638,
      "buckets" : [
        {
          "key" : "/api/admin/oauth/token",
          "doc_count" : 49542152,
          "group_sort" : {
            "hits" : {
              "total" : {
                "value" : 49542152,
                "relation" : "eq"
              },
              "max_score" : null,
              "hits" : [
                {
                  "_index" : "rest_logs-2023.01.05",
                  "_type" : "_doc",
                  "_id" : "kxcNgYUBn8gePaBo6-bM",
                  "_score" : null,
                  "_source" : {
                    "requestTime" : "2023-01-05 16:30:51",
                    "requestUrl" : "/api/admin/oauth/token"
                  },
                  "sort" : [
                    1672907451313
                  ]
                }
              ]
            }
          }
        },
        {
          "key" : "/api/ecoc/v1/kafkaSend/duoMaiExcute",
          "doc_count" : 49333145,
          "group_sort" : {
            "hits" : {
              "total" : {
                "value" : 49333145,
                "relation" : "eq"
              },
              "max_score" : null,
              "hits" : [
                {
                  "_index" : "rest_logs-2023.01.05",
                  "_type" : "_doc",
                  "_id" : "ghcNgYUBn8gePaBo6-bM",
                  "_score" : null,
                  "_source" : {
                    "requestTime" : "2023-01-05 16:30:51",
                    "requestUrl" : "/api/ecoc/v1/kafkaSend/duoMaiExcute",
                    "requestHeader" : {
                      "clientId" : [
                        "duomai"
                      ]
                    }
                  },
                  "sort" : [
                    1672907451132
                  ]
                }
              ]
            }
          }
        }
      ]
    }
  }
}

# top_hits查询 查询ts最大的两条信息
GET iot_deviceop/_search
{
  "size": 0, 
  "aggs": {
    "top_age_infot": {
      "top_hits": {
        "size": 2,
        "sort": [
              {
              "ts": {
              "order":"desc"
              }
            }
          ]
      }
    }
  }
}

# max_bucket查询每个桶的最大值,min_bucket查询每个桶的最小值
GET employee/_search
{
  "size": 0, 
  "aggs": {
    "job_info": {
      "terms": {
        "field": "job"
      },
      "aggs": {
        "job_avg_info": {
          "avg": {
            "field": "sal"
          }
        }
      }
    },
    "min_avg_sal_job":{
      "max_bucket": {
        //查询平均工资最高的岗位
        "buckets_path": "job_info>job_avg_info"  
      }
    }
  }
}

8、创建更新删除操作

8.1、创建索引并查看

PUT test_index
GET /_cat/indices?v

8.2、删除索引并查看

DELETE test_index
GET /_cat/indices?v

8.3、查看文档类型

GET test_index/_mapping

8.4、在索引中添加文档

PUT test_index/1
{
  "vin": "B1293049555"
}

8.5、在索引中查看文档

GET /decode/resolve/1

8.6、在索引中修改文档

POST test_index/1/_update
{
  "doc": { "vin": "A1299405555" }
}

8.7、在索引中删除文档

DELETE test_index/1

8.8、在索引中批量操作文档

POST test_index/_bulk
{"index":{"_id":"1"}}
{"name": "aaaa" }
{"index":{"_id":"2"}}
{"name": "bbbb" }

8.9、创建一个数据模型样例(包含index、type、field):

PUT test_index
{
    "settings":{
        "index":{
            "number_of_shards":3,
            "number_of_replicas":0
        }
    },
    "mappings":{
        "resolve":{
            "properties":{
                "id":{
                    "type":"keyword"
                },
                "name":{
                    "type":"keyword"
                },
                "gender":{
                    "type":"keyword"
                },
                "carBrandId":{
                    "type":"text"
                },
                "balance":{
                    "type":"int"
                },
                "age":{
                    "type":"int"
                },
                "address":{
                    "type":"keyword"
                },
                "craeteTime":{
                    "type":"date"
                },
                "statusId":{
                    "type":"keyword"
                },
                "enable":{
                    "type":"boolean"
                }
            }
        }
    }
}

8.10、Elasticsearch给type增加一项field

PUT test_index/_mapping
{
 "properties": {
   "fieldName":{
     "type":"boolean"
   }
 }
}

PUT test_index/_mappings
{
 "properties": {
   "fieldName":{
     "type":"boolean"
   }
 }
}

二、ES查询相关Java的API

1、Java三种操作ES数据库的方式

以下是这三种操作方式(ElasticsearchTemplate、ElasticsearchRepository和ElasticsearchRestTemplate)与Elasticsearch版本的大致对应关系:

  • ElasticsearchTemplate:ElasticsearchTemplate通常与早期版本的Elasticsearch(例如2.x系列和早期的5.x系列)一起使用。这种方式在早期的Spring Data Elasticsearch版本中是常见的。

  • ElasticsearchRepository:ElasticsearchRepository从Spring Data Elasticsearch 3.0版本开始引入,并且在较新的版本中也得到了支持。您可以在3.x版本和4.x版本的Spring Data Elasticsearch中使用ElasticsearchRepository。请注意,具体支持的特性和功能可能会因版本而异。

  • ElasticsearchRestTemplate:ElasticsearchRestTemplate是从Spring Data Elasticsearch 4.0版本开始引入的新特性。它基于Elasticsearch高级REST客户端,这个客户端本身也是从Elasticsearch 6.0版本开始引入的。因此,ElasticsearchRestTemplate主要用于较新版本的Elasticsearch(6.x及更高版本)。

Elasticsearch从6.x升级到7.x改动超级大,ElasticsearchTemplate不建议使用了,改为使用ElasticsearchRestTemplate,ElasticsearchRepository实现复杂查询的方法也不建议使用了。从此我们简单的数据操作可以使用ElasticsearchRepository,而复杂的数据操作只能使用ElasticsearchRestTemplate了。

因此,我们使用SpringBoot集成ES数据库的时候一般采用Elasticsearch 7.x及以上版本。

注意:这三种操作方式都需要集成Spring Data Elasticsearch依赖才能使用。

2、JavaAPI环境准备

新建Maven工程。

添加依赖:

<!-- elasticsearch 的客户端 -->
<dependency>
	<groupId>org.elasticsearch.client</groupId>
	<artifactId>elasticsearch-rest-high-level-client</artifactId>
	<version>7.8.0</version>
</dependency>

<!--整合SpringData,版本是基于SpringBoot的版本来定义的-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

编写配置文件:

spring:
  application:
    name: service-search
  # ES服务地址
  elasticsearch:
    rest:
      # 集群使用逗号分开
      uris: http://10.11.24.200:9201,http://10.11.24.200:9202,http://10.11.24.200:9203
      username: elastic
      password: elastic

3、ElasticsearchRepository操作

实体类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "person",createIndex = true)
public class Person implements Serializable {
    @Id
    @Field(type = FieldType.Auto)
    private String id;
    
    @Field(type = FieldType.Text,index = true)
    private String name;
    
    @Field(type = FieldType.Integer,index = true)
    private Integer age;
    
    @Field(type = FieldType.Keyword,index = true)
    private String sex;
    
    @Field(type = FieldType.Keyword,index = true)
    private String tel;
    
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Field(type = FieldType.Date, format = DateFormat.date_time, pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
}

接口类:

public interface PersonRepository extends ElasticsearchRepository<Person,String> {
    
}

操作类:

@SpringBootTest(classes = SpringBootApplicationMain.class)
public class ApplicationMainTest {
 
    @Resource
    private PersonRepository personRepository;
 
    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
 
    @Test
    public void test(){
        System.out.println(personRepository);
        System.out.println(elasticsearchRestTemplate);
    }
    
    @Test
    public void saveOne(){
        Person person = new Person();
        person.setId("1");
        person.setName("王天龙");
        person.setAge(30);
        person.setSex("man");
        person.setTel("1111111");
        person.setCreateTime(new Date());
        Person savePerson = personRepository.save(person);
        System.out.println(savePerson);
    }
    
    @Test
    public void saveAll(){
        List<Person> personList = new ArrayList<>(3);
        Person person2 = new Person();
        person2.setId("2");
        person2.setName("王天祥");
        person2.setAge(26);
        person2.setSex("男");
        person2.setTel("222222222222");
        person2.setCreateTime(new Date());
        personList.add(person2);
 
        Person person3 = new Person();
        person3.setId("3");
        person3.setName("王杰");
        person3.setAge(31);
        person3.setSex("女");
        person3.setTel("3333333333");
        person3.setCreateTime(new Date());
        personList.add(person3);
        personRepository.saveAll(personList);
    }
    
}

3.1、简单操作的接口

在这里插入图片描述

3.2、复杂操作

您可以创建一个BookRepository接口继承自ElasticsearchRepository,并定义一些查询方法:

接口类:

public interface PersonRepository extends ElasticsearchRepository<Person,String> {
    
    List<Person> findByName(String name);

    List<Person> findByTel(String tel);

    List<Person> findByNameAndTel(String name, String tel);
}

在上面的示例中,PersonRepository继承自ElasticsearchRepository<Person, String>,其中Person是实体类,String是实体类的主键类型。然后,您定义了几个查询方法,如findByNamefindByTelfindByNameAndTel,这些方法的命名规则会被Spring Data Elasticsearch解析为相应的查询操作。

3.2.1、Spring Data JPA写法

Spring Data Elasticsearch能够自动解析您自定义的接口方法,将其转换为相应的Elasticsearch查询。它基于方法的命名规则来生成查询,这些规则与Spring Data JPA类似。以下是一些常见的规律:

  1. 方法名关键字:方法名以findByfindAllBycountBydeleteBy等开头,后面跟着实体属性名,例如findByTitlefindAllByAuthorcountByCategory等。

  2. 属性名和操作符:在方法名中,您可以使用属性名和操作符,如findByTitleAndAuthorfindByPriceGreaterThanfindByPublishedDateBetween等。

  3. 逻辑连接词:您可以在方法名中使用逻辑连接词来组合多个条件,如findByTitleAndAuthorfindByCategoryOrPrice等。

  4. 排序和分页:您可以通过在方法名中添加OrderByPageable来指定排序和分页条件,例如findByTitleOrderByPublishedDateDescfindByAuthorOrderByPriceAsc等。

  5. 限制查询结果数量:使用findFirstfindTop等关键字来限制返回的查询结果数量,例如findTop5ByCategoryfindFirstByAuthorOrderByPublishedDateDesc等。

  6. 使用自定义查询:如果您的需求超出了自动生成的查询,您可以使用@Query注解,在方法上指定自定义的Elasticsearch查询语句。

以下是一个示例,有一个实体类Person,您可以定义一个PersonRepository接口来操作Elasticsearch,并按照命名规则定义一些查询方法:

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.List;

public interface PersonRepository extends ElasticsearchRepository<Person, String> {

    List<Person> findByFirstName(String firstName);

    List<Person> findByLastNameAndAge(String lastName, int age);

    List<Person> findByAgeGreaterThan(int age);

    List<Person> findByFirstNameAndLastNameOrAge(String firstName, String lastName, int age);

    List<Person> findTop5ByAgeOrderByFirstNameAsc(int age);

    Person findFirstByLastNameOrderByAgeDesc(String lastName);
}

在上述示例中,每个方法名都遵循了Spring Data Elasticsearch的命名规则,可以自动生成相应的查询。如果您需要更复杂的查询,您还可以使用@Query注解来定义自定义的Elasticsearch查询语句。

3.2.2、@Query的写法

当使用Spring Data Elasticsearch中的@Query注解时,您可以自定义Elasticsearch的查询语句,以满足复杂的查询需求。以下是一个使用@Query的操作示例:

假设您有一个实体类Product表示产品信息:

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;

@Document(indexName = "products")
public class Product {

    @Id
    private String id;
    private String name;
    private String category;
    private double price;

    // Getters and setters
}

然后,您可以创建一个ProductRepository接口,并使用@Query注解来定义自定义查询方法:

import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.List;

public interface ProductRepository extends ElasticsearchRepository<Product, String> {

    @Query("{\"bool\": {\"must\": [{\"match\": {\"name\": \"?0\"}}]}}")
    List<Product> findByNameCustom(String name);

    @Query("{\"bool\": {\"must\": [{\"term\": {\"category\": \"?0\"}}, {\"range\": {\"price\": {\"gte\": ?1}}}]}}")
    List<Product> findByCategoryAndPriceRange(String category, double minPrice);
}

在上述示例中,我定义了两个使用@Query注解的自定义查询方法:

  1. findByNameCustom:这个方法使用了Elasticsearch的查询语法,使用match查询来查找指定名称的产品。

  2. findByCategoryAndPriceRange:这个方法使用了更复杂的查询,结合了term查询和range查询,以查找特定类别中价格在指定范围内的产品。

您可以根据自己的需求编写更复杂的查询语句,利用Elasticsearch的强大查询能力来满足各种查询需求。使用@Query注解时,请确保查询语句的语法正确,并且根据实际情况调整查询条件和参数。

在使用Spring Data Elasticsearch的@Query注解中,?0表示方法参数中的第一个参数,?1表示第二个参数,依此类推。这样,您可以在查询语句中引用方法的参数值,以动态地构建查询条件。

4、ElasticsearchRestTemplate操作

4.1、熟悉查询条件的构造

当构建Elasticsearch查询时,QueryBuildersNativeSearchQueryBuilderNativeSearchQuery以及其他类型的Builders(如AggregationBuildersSortBuilders等)是Spring Data Elasticsearch提供的一些工具和类。它们之间的关系如下所示:

                         +----------------+
                         |                |
                         |   QueryBuilders|       
                         |                |         
                         +----------------+
                                 |
                                 | 组合和构建各种查询
                                 |
                                 v
+---------------------+       +-------------------------+
|                     |       |                         |
|NativeSearchQueryBuilder|<---|    NativeSearchQuery    |
|                     |       |                         |
+---------------------+       +-------------------------+
        |                            |
        | 构建和配置查询条件            | 将查询条件传递给
        |                            | Elasticsearch操作
        v                            v
+---------------------+       +----------------------------+
|                     |       |  elasticsearchTemplate     |
|    AggregationBuilders|     |    或者                     |
|                     |       | elasticsearchRestTemplate  |
+---------------------+       +----------------------------+
        |                            |
        | 构建聚合操作                 | 执行各种Elasticsearch操作
        |                            |
        v                            v
  +-----------------+            +-------------------+
  |                 |            |                   |
  | SortBuilders    |            |       ...         |
  |                 |            |                   |
  +-----------------+            +-------------------+
  |                 |
  |    ...          |
  |                 |
  +-----------------+

(1)、关系1

QueryBuilderQueryBuilders是Elasticsearch Java客户端库提供的两个相关但不同的概念。

  1. QueryBuilder
    QueryBuilder是Elasticsearch Java客户端库中的接口,用于构建Elasticsearch查询。它提供了一种抽象方式来构建查询条件,可以用于构建各种类型的查询,如匹配、范围、布尔逻辑、嵌套查询等。QueryBuilder是一个用于构建查询对象的接口,您可以使用它来创建复杂的查询结构,然后传递给Elasticsearch客户端执行查询。

  2. QueryBuilders
    QueryBuilders是Elasticsearch Java客户端库中的一个工具类,用于创建不同类型的查询。它提供了一组静态方法,每个方法返回一个实现了QueryBuilder接口的查询对象,从而简化了构建查询的过程。通过使用QueryBuilders工具类,您可以直接调用方法来创建各种类型的查询,而无需手动创建QueryBuilder实例。

简而言之,QueryBuilder是一个接口,用于构建Elasticsearch查询对象,而QueryBuilders是一个工具类,用于创建实现了QueryBuilder接口的具体查询对象。您可以根据需要选择使用QueryBuilder接口来手动构建查询,或者使用QueryBuilders工具类来更方便地创建查询。

(2)、关系2

在Spring Data Elasticsearch中,NativeSearchQueryBuilderNativeSearchQuery是用于构建Elasticsearch查询条件的两个关键接口/类。它们之间的关系如下:

  1. NativeSearchQueryBuilderNativeSearchQueryBuilder是一个用于构建NativeSearchQuery的构造器类。它提供了一组方法,可以通过链式调用来配置和定制查询条件,例如添加过滤条件、排序、分页等。NativeSearchQueryBuilder最终会产生一个NativeSearchQuery实例,该实例表示一个完整的Elasticsearch查询。

  2. NativeSearchQueryNativeSearchQuery是用于表示Elasticsearch查询的接口。它包含了查询的各种组成部分,例如查询语句、过滤条件、排序规则、分页信息等。NativeSearchQuery可以被传递给Spring Data Elasticsearch的各种操作,例如查询、聚合等,以执行实际的Elasticsearch操作。

简而言之,NativeSearchQueryBuilder用于构建NativeSearchQuery,而NativeSearchQuery用于表示Elasticsearch查询,并作为参数传递给各种Spring Data Elasticsearch操作。

以下是一个简单的示例,演示如何使用NativeSearchQueryBuilder构建NativeSearchQuery

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

@Service
public class SearchService {

    private final ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    public SearchService(ElasticsearchTemplate elasticsearchTemplate) {
        this.elasticsearchTemplate = elasticsearchTemplate;
    }

    public List<Product> searchProductsByCategoryAndPriceRange(String category, double minPrice) {
        NativeSearchQuery query = new NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.termQuery("category", category))
            .withFilter(QueryBuilders.rangeQuery("price").gte(minPrice))
            .build();

        return elasticsearchTemplate.queryForList(query, Product.class);
    }
}

在上述示例中,NativeSearchQueryBuilder用于构建一个查询,该查询过滤出特定类别并且价格高于指定最低价格的产品。然后,通过elasticsearchTemplate执行查询操作。

总结:NativeSearchQueryBuilder用于创建NativeSearchQuery,后者用于表示Elasticsearch查询。这两者之间的关系可以看作是构建和执行查询的关系。

注:

NativeSearchQueryBuilder可以使用的方法:

在这里插入图片描述

4.2、具体代码的执行

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = SpringBootApplicationMain.class)
public class ApplicationMainTest {
 
    @Resource
    private PersonRepository personRepository;
 
    @Resource
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
 
    @Test
    public void test(){
        System.out.println(personRepository);
        System.out.println(elasticsearchRestTemplate);
    }

(1)创建索引

@Test
public void createIndex(){
    IndexCoordinates indexCoordinates = IndexCoordinates.of("person");
    IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(indexCoordinates);
    boolean created = indexOperations.create();
    System.out.println(created);
}

(2)设置mapping

@Test
public void setIndexMappings(){
    IndexCoordinates indexCoordinates = IndexCoordinates.of("person");
    IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(indexCoordinates);
    boolean created = indexOperations.putMapping(Person.class);
    System.out.println(created);
}

(3)设置settings

@Test
public void createIndexWithSetSettings(){
    IndexCoordinates indexCoordinates = IndexCoordinates.of("person");
    IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(indexCoordinates);
    Map<String,Object> settings = new HashMap<>();

    //----------------------------------------静态设置开始----------------------------------------------
    // 静态设置:只能在索引创建时或者在状态为 closed index(闭合的索引)上设置

    //主分片数,默认为5.只能在创建索引时设置,不能修改
    settings.put("index.number_of_shards",2);

    //是否应在索引打开前检查分片是否损坏,当检查到分片损坏将禁止分片被打开
    settings.put("index.shard.check_on_startup","false");//默认值
    //        settings.put("index.shard.check_on_startup","checksum");//检查物理损坏
    //        settings.put("index.shard.check_on_startup","true");//检查物理和逻辑损坏,这将消耗大量内存和CPU
    //        settings.put("index.shard.check_on_startup","fix");//检查物理和逻辑损坏。有损坏的分片将被集群自动删除,这可能导致数据丢失

    //自定义路由值可以转发的目的分片数。默认为 1,只能在索引创建时设置。此值必须小于index.number_of_shards
    settings.put("index.routing_partition_size",1);

    //默认使用LZ4压缩方式存储数据,也可以设置为 best_compression,它使用 DEFLATE 方式以牺牲字段存储性能为代价来获得更高的压缩比例。
    settings.put("index.codec","best_compression");
    //----------------------------------------静态设置结束----------------------------------------------


    //----------------------------------------动态设置开始----------------------------------------------
    //每个主分片的副本数。默认为 1。
    settings.put("index.number_of_replicas",0);

    //基于可用节点的数量自动分配副本数量,默认为 false(即禁用此功能)
    settings.put("index.auto_expand_replicas",false);

    //执行刷新操作的频率,这使得索引的最近更改可以被搜索。默认为 1s。可以设置为 -1 以禁用刷新。
    settings.put("index.refresh_interval","1s");

    //用于索引搜索的 from+size 的最大值。默认为 10000
    settings.put("index.max_result_window",10000);

    // 在搜索此索引中 rescore 的 window_size 的最大值
    settings.put("index.max_rescore_window",10000);

    //设置为 true 使索引和索引元数据为只读,false 为允许写入和元数据更改。
    settings.put("index.blocks.read_only",false);

    // 设置为 true 可禁用对索引的读取操作
    settings.put("index.blocks.read",false);

    //设置为 true 可禁用对索引的写入操作。
    settings.put("index.blocks.write",false);

    // 设置为 true 可禁用索引元数据的读取和写入。
    settings.put("index.blocks.metadata",false);

    //索引的每个分片上可用的最大刷新侦听器数
    settings.put("index.max_refresh_listeners",1000);
    //----------------------------------------动态设置结束----------------------------------------------

    boolean created = indexOperations.create(settings);
    System.out.println(created);
}

(4)删除索引

@Test
public void deleteIndex(){
    IndexCoordinates indexCoordinates = IndexCoordinates.of("person");
    IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(indexCoordinates);
    boolean isDeleted = indexOperations.delete();
    System.out.println(isDeleted);
}

(5)更新某条数据

@Test
public void updateOne(){
    Document document = Document.create();
    document.setId("1");
    document.put("name","天龙战神");

    UpdateQuery.Builder builder = UpdateQuery.builder("1").withDocument(document).withScriptedUpsert(true);

    UpdateResponse updateResponse = elasticsearchRestTemplate.update(builder.build(), IndexCoordinates.of("person"));

    System.out.println(updateResponse.getResult());
}

(6)查询所有数据MatchAll

@Test
public void searchMatchAll() throws IOException {
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder().must(new MatchAllQueryBuilder());
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
        .withQuery(boolQueryBuilder);

    NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
    SearchHits<Person> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Person.class);

    searchHits.getSearchHits().forEach(personSearchHit -> {
        Person content = personSearchHit.getContent();
        System.out.println(content);
    });
}

(7)BoolMust多条件查询

@Test
public void searchBoolMustWhere(){
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.must(QueryBuilders.matchQuery("name","王天龙"));
    boolQueryBuilder.must(QueryBuilders.matchQuery("age",30));
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
        .withQuery(boolQueryBuilder);
    NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
    SearchHits<Person> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Person.class);

    searchHits.forEach(personSearchHit -> {
        System.out.println(personSearchHit.getContent());
    });
}

(8)BoolShould多条件查询

@Test
public void searchShouldWhere(){
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    boolQueryBuilder.should(QueryBuilders.matchQuery("name","天龙"));
    boolQueryBuilder.should(QueryBuilders.matchQuery("age",26));
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
        .withQuery(boolQueryBuilder);
    NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
    SearchHits<Person> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Person.class);
    searchHits.forEach(personSearchHit -> {
        System.out.println(personSearchHit.getContent());
    });
}

(9)Range查询

@Test
public void searchRange(){
    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("age").gte(26).lt(31);
    boolQueryBuilder.filter(rangeQueryBuilder);
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
        .withQuery(boolQueryBuilder);
    NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
    SearchHits<Person> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Person.class);
    searchHits.forEach(personSearchHit -> {
        System.out.println(personSearchHit.getContent());
    });
}

(10)聚合搜索1

/**
     * 聚合搜索
     * 聚合搜索,aggs,类似于group by,对age字段进行聚合,
     */
@Test
public void aggregations1() {
    NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
        .withAggregations(AggregationBuilders.terms("count").field("sex"))
        .build();

    SearchHits<Person> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Person.class);
    //取出聚合结果
    ElasticsearchAggregations elasticsearchAggregations = (ElasticsearchAggregations) searchHits.getAggregations();
    Aggregations aggregations = elasticsearchAggregations.aggregations();
    Terms terms = (Terms) aggregations.asMap().get("count");

    for (Terms.Bucket bucket : terms.getBuckets()) {
        String keyAsString = bucket.getKeyAsString();   // 聚合字段列的值
        long docCount = bucket.getDocCount();           // 聚合字段对应的数量
        System.out.println(keyAsString + " " + docCount);
    }
}

(11)聚合搜索2

@Test
public void aggregations2() {
    // 设置要聚合的字段
    TermsAggregationBuilder lyBucket = AggregationBuilders.terms("ly_bucket").field("firstTitleId.keyword").size(2^31-1);
    TermsAggregationBuilder tableBucket = AggregationBuilders.terms("table_bucket").field("tableId.keyword").size(2^31-1);
    // 指定聚合器中返回的文档数量,默认返回前3个
    tableBucket.subAggregation(AggregationBuilders.topHits("table_result").size(2^31-1).highlighter(highlightBuilder));
    // 合并子聚合
    lyBucket.subAggregation(tableBucket);
    // 构建查询对象
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
    nativeSearchQueryBuilder.withQuery(queryBuilder);
    nativeSearchQueryBuilder.addAggregation(lyBucket);
    SearchHits<ContentEntity> search = elasticsearchRestTemplate.search(nativeSearchQueryBuilder.build(), ContentEntity.class);
    Aggregations aggregations = search.getAggregations();
        if (aggregations != null) {
            Terms lyBucket1 = aggregations.get("ly_bucket");
            List<? extends Terms.Bucket> lyBuckets = lyBucket1.getBuckets();
            for (Terms.Bucket lyBucket2 : lyBuckets) {
                Terms tableBucket1 = lyBucket2.getAggregations().get("table_bucket");
                List<? extends Terms.Bucket> tableBuckets = tableBucket1.getBuckets();
                for (Terms.Bucket table : tableBuckets) {
                    TopHits topHits = table.getAggregations().get("table_result");
                    org.elasticsearch.search.SearchHits hits = topHits.getHits();
                    System.out.println(hits);
                    }
                }
            }
        }
}

(12)分页实现

/**
     * 分页实现
     */
@Test
public void searchWithPageable(){
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder().must(new MatchAllQueryBuilder());
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
        .withQuery(boolQueryBuilder)
        //分页实现 可以传递活值在PageRequest.of(0,2)里面
        .withPageable(PageRequest.of(0,2));

    NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
    SearchHits<Person> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Person.class);

    searchHits.getSearchHits().forEach(personSearchHit -> {
        Person content = personSearchHit.getContent();
        System.out.println(content);
    });
}

(13)排序实现

/**
     * 排序实现
     */
@Test
public void searchWithSort(){
    BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder().must(new MatchAllQueryBuilder());
    NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder()
        .withQuery(boolQueryBuilder)
        //分页实现
        .withPageable(PageRequest.of(0,10))
        //排序
     .withSorts(SortBuilders.fieldSort("createTime").order(SortOrder.DESC),SortBuilders.fieldSort("age").order(SortOrder.DESC));

    NativeSearchQuery nativeSearchQuery = nativeSearchQueryBuilder.build();
    SearchHits<Person> searchHits = elasticsearchRestTemplate.search(nativeSearchQuery, Person.class);

    searchHits.getSearchHits().forEach(personSearchHit -> {
        Person content = personSearchHit.getContent();
        System.out.println(content);
    });
}
}

4.3、高亮操作

详情查看该网址:https://www.cnblogs.com/eternityz/p/17039582.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/102805.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java 中用 ReentrantReadWriteLock 解决HashMap() 线程安全问题

在并发编程中&#xff0c;当多个线程同时操作一个 变量时&#xff0c;容易出现线程安全的问题&#xff0c;我们可以使用各种锁来解决线程安全问题&#xff0c;比如&#xff1a;ConcurrentHashMap 在底层使用了synchronized 解决 HashMap()的线程安全问题, 我们这里希望使用 Ree…

SQL查询本年每月的数据

--一、以一行数据的形式&#xff0c;显示本年的12月的数据&#xff0c;本示例以2017年为例&#xff0c;根据统计日期字段判断&#xff0c;计算总和&#xff0c;查询语句如下&#xff1a;selectsum(case when datepart(month,统计日期)1 then 支付金额 else 0 end) as 1月, sum…

C# 获取Windows系统版本注意事项

首先通过微软官方文档&#xff1a;https://learn.microsoft.com/zh-cn/windows/win32/sysinfo/operating-system-version了解各个操作系统对应的版本号 下面介绍3种获取版本号的方式及弊端 1. Environment.OSVersion.Version OperatingSystem os Environment.OSVersion;// 判断…

​7.3 项目3 贪吃蛇(控制台版) (A)​

C自学精简实践教程 目录(必读) 主要考察 模块划分 / 文本文件读取 UI与业务分离 / 模块划分 控制台交互 / 数据抽象 需求 用户输入字母表示方向&#xff0c;实现贪吃蛇游戏 规则&#xff1a;碰到边缘和碰到蛇自己都算游戏结束 输入文件 data.txt data.txt 内容如下&am…

CSS学习笔记02

CSS笔记02 美化网页元素 为什么要美化网页 目的&#xff1a; 有效的传递页面信息美化网页、页面漂亮、才能吸引用户突显页面的主题提高用户的体验 span标签 span标签是短语内容的通用行内容器&#xff0c;它本身并没有任何特殊语义。 通常我们使用span标签来把我们想要重…

02-请解释一下Java的内存模型和happens-before规则?【Java面试题总结】

请解释一下Java的内存模型和happens-before规则&#xff1f; 概念&#xff1a;Java内存模型&#xff0c;简称JMM&#xff0c;是一种定义了多线程程序中内存访问行为的规范。它定义了线程如何与主内存和工作内存进行交互&#xff0c;以及如何保证多线程程序的正确性和可见性。J…

基于grpc从零开始搭建一个准生产分布式应用(6) - 02 - MapStruct数据转换

一、基础转换 1.1、基础类型 基本类型、包装类、BigDecimal转String默认使用DecimalFormat格式化&#xff0c;Mapping#numberFormat可以指定格式&#xff0c;Date转String默认使用SimpleDateFormat格式化&#xff0c;如默认格式不符要求&#xff0c;可以用&#xff0c;Mapping…

PMP项目管理主要学习内容是什么?

PMP项目管理是指根据美国项目管理学会(Project Management Institute&#xff0c;简称PMI)制定的项目管理知识体系和方法论进行项目管理的一种认证。PMP主要关注项目的规划、执行和控制等方面的知识和技能。 下面是PMP项目管理《PMBOK指南》第六版的主要学习内容&#xff1a; …

FFmpeg4.3.1+h264在windows下编译与VS2017项目集成

前言 在Android音视频开发中&#xff0c;网上知识点过于零碎&#xff0c;自学起来难度非常大&#xff0c;不过音视频大牛Jhuster提出了《Android 音视频从入门到提高 - 任务列表》&#xff0c;结合我自己的工作学习经历&#xff0c;我准备写一个音视频系列blog。本文是音视频系…

【Grasshopper基础15】“右键菜单似乎不太对劲”

距离上一篇文章已经过去了挺久的&#xff0c;很长时间没有写GH基础部分的内容了&#xff0c;原因其一是本职工作太忙了&#xff0c;进度也有些落后&#xff0c;白天工作累成马&#xff0c;回家只想躺着&#xff1b;其二则是感觉GH基础系列基本上也介绍得差不多了&#xff0c;电…

【微服务部署】08-监控与告警

文章目录 1. PrometheusOperator1.1 优势1.2 配置脚本1.3 部署脚本 2. Granfana实现监控看板2.1 Granfana核心特性2.2 部署文件 3. prometheus-net收集自定义指标3.1 组件包3.2 使用场景 目前Kubernetes中最流行的监控解决方案是使用Prometheus和AlertManager 1. PrometheusOpe…

尚硅谷SpringMVC (5-8)

五、域对象共享数据 1、使用ServletAPI向request域对象共享数据 首页&#xff1a; Controller public class TestController {RequestMapping("/")public String index(){return "index";} } <!DOCTYPE html> <html lang"en" xmln…

INDEMIND:“大+小”多机协同,实现机器人商用场景全覆盖

随着商用清洁机器人进入越来越多的场景中&#xff0c;单一的中型机器人并不能有效覆盖所有区域&#xff0c;更加细分化的产品组合正在成为新的趋势。 产品形态的“新趋势” 在商用场景中&#xff0c;目前的商用清洁机器人几乎均是中大型的产品形态&#xff0c;较大的体型意味…

性能测试(测试系列10)

目录 前言&#xff1a; 1.什么是性能测试 1.1生活中遇到的软件问题 1.2性能测试的定义 1.3性能测试和功能测试有什么区别 1.4性能的好坏的区分 1.5影响一个软件性能的因素 2.为什么要进行性能测试 3.性能测试常见的术语以及衡量指标 3.1并发 3.2用户数 3.3响应时间 …

Vulnhub: Ragnar Lothbrok: 1靶机

kali&#xff1a;192.168.111.111 靶机&#xff1a;192.168.111.226 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.226 作者提示修改hosts文件 目录爆破 gobuster dir -u http://armbjorn -w /usr/share/wordlists/dirbuster/directory-l…

HFSS 3维曲线导入

HFSS 3维曲线导入 简介环境参考代码使用结果 简介 如图一所示&#xff0c;CST中可以通过导入和到出由任意点组成的曲线&#xff0c;但是HFSS中貌似不能导入&#xff08;如图二所示&#xff09;&#xff0c;如果我们要将matlab的产生的曲线的点的数据导入特变麻烦&#xff0c;特…

英码深元“三位一体”AI场景化解决方案,助力多地化工园区快速实现智慧化转型!

我国是世界公认的化工大国&#xff0c;同时也是崛起中的化工强国。近年来多起重大爆炸事故暴露出我国化工园区安全问题突出&#xff0c;特别是在安全风险管控数字化转型、智能化升级方面存在明显短板和不足&#xff0c;尤其突出的痛点&#xff1a;化工园区的日常管理方式较为粗…

【DRONECAN】(三)WSL2 及 ubuntu20.04 CAN 驱动安装

【DRONECAN】&#xff08;三&#xff09;WSL2 及 ubuntu20.04 CAN 驱动安装 前言 这一篇文章主要介绍一下 WSL2 及 ubuntu20.04 CAN 驱动的安装&#xff0c;首先说一下介绍本文的目的。 大家肯定都接触过 ubuntu 系统&#xff0c;但是我们常用的操作系统都是 Windows&#x…

python unitest自动化框架

以下举一个最简单的unitest实例&#xff0c;包含备注&#xff0c;自己拉取代码运行一次就知道原理了 import unittest import osclass TestSample(unittest.TestCase):classmethoddef setUpClass(cls) -> None:print(整个测试类只执行一次)def setUp(self) -> None:prin…

睿趣科技:抖音开网店卖玩具怎么样

近年来&#xff0c;随着社交媒体平台的飞速发展&#xff0c;抖音作为一款短视频分享应用也迅速崭露头角。而在这个充满创业机遇的时代背景下&#xff0c;许多人开始探索在抖音平台上开设网店&#xff0c;尤其是卖玩具类商品&#xff0c;那么抖音开网店卖玩具究竟怎么样呢? 首先…