初步了解ES

news2024/11/23 7:25:49

一、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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日两题 110平衡二叉树 199二叉树的右视图

110 题目 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&a…

提升用户体验:Vue与compressor.js实现高效文件压缩

前言 上传文件是一个常见的需求&#xff0c;并且文件大小往往成为限制因素之一。为了提升用户体验和节省带宽消耗&#xff0c;上传时的文件压缩便显得格外重要。本文将介绍基于 Vue 框架和 compressor.js 的上传时文件压缩实现方法&#xff0c;通过在上传过程中对文件进行压缩&…

Linux常用命令——depmod命令

在线Linux命令查询工具 depmod 分析可载入模块的相依性 补充说明 depmod命令可产生模块依赖的映射文件&#xff0c;在构建嵌入式系统时&#xff0c;需要由这个命令来生成相应的文件&#xff0c;由modprobe使用。 语法 depmod(选项)选项 -a或--all&#xff1a;分析所有可…

Redis数据结构:Set类型全面解析

Set 类型是一个无序并唯一的键值集合&#xff0c;它的存储顺序不会按照插入的先后顺序进行存储。Redis 中集合是通过哈希表实现的&#xff0c;所以添加&#xff0c;删除&#xff0c;查找的复杂度都是 O(1)。相对于列表&#xff0c;集合也有两个特点&#xff1a;无序、不可重复 …

【内网穿透】搭建我的世界Java版服务器,公网远程联机

目录 前言 1. 搭建我的世界服务器 1.1 服务器安装java环境 1.2 配置服务端 2. 测试局域网联机 3. 公网远程联机 3.1 安装cpolar内网穿透 3.1.1 windows系统 3.1.2 linux系统&#xff08;支持一键自动安装脚本&#xff09; 3.2 创建隧道映射内网端口 3.3 测试公网远程…

Spring AOP基于注解方式实现和细节

目录 一、Spring AOP底层技术 二、初步实现AOP编程 三、获取切点详细信息 四、 切点表达式语法 五、重用&#xff08;提取&#xff09;切点表达式 一、Spring AOP底层技术 SpringAop的核心在于动态代理&#xff0c;那么在SpringAop的底层的技术是依靠了什么技术呢&#x…

国产AI芯片突破,芯片或成白菜价,恐惧的美芯阻止台积电为它代工

日前消息指台积电大幅减少一家中国AI芯片企业的产能&#xff0c;原因在于国产AI芯片的性能已接近美芯&#xff0c;美国芯片企业NVIDIA与相关的资本机构贝莱德联手施压台积电所致&#xff0c;凸显出美国芯片忧虑中国AI芯片的竞争力。 这家国产AI芯片企业为壁仞科技&#xff0c;据…

C#,《小白学程序》第七课:列表(List)应用之一————编制高铁车次信息表

1 文本格式 /// <summary> /// 车站信息类 class /// </summary> public class Station { /// <summary> /// 编号 /// </summary> public int Id { get; set; } 0; /// <summary> /// 车站名 /// </summary>…

【JavaSE专栏89】Java字符串和XML数据结构的转换,高效灵活转变数据

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;3年JAVA全栈开发经验&#xff0c;专注JAVA技术、系统定制、远程指导&#xff0c;致力于企业数字化转型&#xff0c;CSDN学院、蓝桥云课认证讲师。 主打方向&#xff1a;Vue、SpringBoot、微信小程序 本文讲解了 XML 的概…

软件测试的方法有哪些?

软件测试 根据利用的被测对象信息的不同&#xff0c;可以将软件测试方法分为&#xff1a;黑盒测试、灰盒测试、白盒测试。 1、白盒测试 1&#xff09;概念&#xff1a;是依据被测软件分析程序内部构造&#xff0c;并根据内部构造分析用例&#xff0c;来对内部控制流程进行测试…

基于Dpabi的功能连接

1.预处理 这里预处理用Gretna软件进行&#xff0c;共分为以下几步&#xff1a; &#xff08;1&#xff09;DICOM转NIfTI格式 (2)去除前10个时间点(Remove first 10 times points)&#xff1a;由于机器刚启动、被试刚躺进去也还需适应环境&#xff0c;导致刚开始扫描的数据很…

macOS 安装 Homebrew 详细过程

文章目录 macOS 安装 Homebrew 详细过程Homebrew 简介Homebrew 安装过程设置环境变量安装 Homebrew安装完成后续设置(重要)设置环境变量homebrew 镜像源设置macOS 安装 Homebrew 详细过程 本文讲解了如何使用中科大源安装 Homebrew 的安装过程,文章里面的所有步骤都是必要的,需…

ExpressLRS开源之RC链路性能测试

ExpressLRS开源之RC链路性能测试 1. 源由2. 分析3. 测试方案4. 测试设计4.1 校准测试4.2 实验室测试4.3 拉距测试4.4 遮挡测试 5. 总结6. 参考资料 1. 源由 基于ExpressLRS开源基本调试验证方法&#xff0c;对RC链路性能进行简单的性能测试。 修改设计总能够满足合理的需求&a…

Streamlit 讲解专栏(十一):数据可视化-图表绘制详解(中)

文章目录 1 前言2 绘制交互式散点图3 定制图表主题4 增强数据可视化的交互性与注释步骤1步骤二 5 结语 1 前言 在上一篇博文《 Streamlit 讲解专栏&#xff08;十&#xff09;&#xff1a;数据可视化-图表绘制详解&#xff08;上&#xff09;》中&#xff0c;我们学习了一些关…

Vue脚手架中安装ElementUi

目录 ElementUi简介&#xff1a; ElementUi下载&#xff1a; npm 安装&#xff1a; 引入ElementUi: 测试是否引入成功&#xff1a; Element-ui官网&#xff1a;组件 | Element ElementUi简介&#xff1a; ElementUi&#xff0c;是由国内的饿了么团队开发并开源的一套为开…

美五代机装备激光武器可行性分析

源自&#xff1a;北京蓝德信息科技有限公司 一、SHiELD项目研究进展分析 图表&#xff1a;SHiELD项目主要情况 二、机载激光武器面临的技术挑战分析 三、五代机装备激光武器的可行性 声明:公众号转载的文章及图片出于非商业性的教育和科研目的供大家参考和探讨&#xff0c;并不…

三维模型OBJ格式轻量化压缩处理效率提高的技术方法探讨

三维模型OBJ格式轻量化压缩处理效率提高的技术方法探讨 要提高三维模型OBJ格式轻量化压缩处理的效率&#xff0c;可以采取以下方法&#xff1a; 1、优化算法选择&#xff1a;选择合适的优化算法对模型进行轻量化处理。不同的优化算法有不同的时间复杂度和效果。一些常用的优化…

软件测试用例经典方法 | 因果图法及案例

典型的黑盒测试用例设计方法包括等价类划分法、边界值分析法、决策表法、因果图法等。 如果程序的输入条件之间相互存在联系,那么就会使情况变得复杂,因为要检查输入条件的组合情况并不是一件容易的事情,即使把所有输入条件划分为等价类,它们之间的组合情况也相当多,难以分析。…

【Go 基础篇】深入探索:Go语言中的二维数组

在计算机编程中&#xff0c;数组是一种基本的数据结构&#xff0c;用于存储相同类型的元素。而二维数组作为数组的一种扩展&#xff0c;允许我们以类似表格的方式存储和处理数据。在Go语言中&#xff0c;二维数组是一个重要的概念&#xff0c;本文将深入探讨Go语言中的二维数组…