> 文档中心 > BOOT 整合 ElasticSearch

BOOT 整合 ElasticSearch


整合前先理解几个概念 与关键字

  • 索引 一组相似文档的集合

一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个商品数据的索引,一个订单数据的索引,还有一个用户数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。

  • 映射 用来定义索引存储文档的结构:字段、类型等

映射是定义一个文档和它所包含的字段如何被存储和索引的过程。在默认配置下,ES可以根据插入的数据自动地创建mapping,也可以手动创建mapping。 mapping中主要包括字段名、字段类型等

  • 文档 索引中一条记录,可以被索引的最小单元

文档是索引中存储的一条条数据。一条文档是一个可被索引的最小单元。ES中的文档采用了轻量级的JSON格式数据来表示。

  • 分片

Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置 到集群中的任何节点上。

  • 复制

Index的分片中一份或多份副本。


 关键字对比

ElasticSearch

对比

must

相当于MySQL的and

should

相当于MySQL的or

must_not

相当于MySQL的not不等于

gt        大于
git         大于等于
lt 小于
lte 小于等于

match

分词查询

term

完全匹配

fuzzy 误拼写查询
range         范围查询
wildcard 模糊查询
exists 排除null
match_phrase 短语匹配
match_phrase和match区别 match是分词匹配(将输入的词进行分词),match_phrase是短语匹配(输入文本进行查找)

ES存储类型:

文本类型 - text

在Elasticsearch 5.4 版本开始, text取代了需要分词的string, 当一个字段需要用于全文搜索(会被分词), 比如产品名称、产品描述信息, 就应该使用text类型.

text的内容会被分词, 可以设置是否需要存储: “index”: “true|false” 。text类型的字段不能用于排序, 也很少用于聚合.

PUT website{"mappings": { "blog": {     "properties": { "summary": {"type": "text", "index": "true"}     } }    }}

 关键字类型 - keyword

在Elasticsearch 5.4 版本开始, keyword取代了不需要分词的string.—— 当一个字段需要按照精确值进行过滤、排序、聚合等操作时, 就应该使用keyword类型.

keyword的内容不会被分词, 可以设置是否需要存储: “index”: “true|false”.

PUT website{"mappings": { "blog": {     "properties": { "tags": {"type": "keyword", "index": "true"}     } }    }}

数字类型 - 8种

类型

说明

byte

有符号的8位整数, 范围: [-128 ~ 127]

short

有符号的16位整数, 范围: [-32768 ~ 32767]

integer

有符号的32位整数, 范围: [−231 ~ 231-1]

long

有符号的64位整数, 范围: [−263 ~ 263-1]

float

32位单精度浮点数

double

64位双精度浮点数

half_float

16位半精度IEEE 754浮点类型

scaled_float

缩放类型的的浮点数, 比如price字段只需精确到分, 57.34缩放因子为100, 存储结果为5734

使用注意事项:

尽可能选择范围小的数据类型, 字段的长度越短, 索引和搜索的效率越高;优先考虑使用带缩放因子的浮点类型.

PUT shop{    "mappings": { "book": {     "properties": {  "name": {"type": "text"},  "quantity": {"type": "integer"},  // integer类型  "price": {      "type": "scaled_float",// scaled_float类型      "scaling_factor": 100  }     } }    }}

 日期类型 - date

JSON没有日期数据类型, 所以在ES中, 日期可以是:包含格式化日期的字符串, “2018-10-01”, 或"2018/10/01 12:10:30"

代表时间毫秒数的长整型数字。代表时间秒数的整数.

如果时区未指定, 日期将被转换为UTC格式, 但存储的却是长整型的毫秒值.

可以自定义日期格式, 若未指定, 则使用默认格式: strict_date_optional_time||epoch_millis

 // 添加映射PUT website{    "mappings": { "blog": {     "properties": {  "pub_date": {"type": "date"}   // 日期类型     } }    }}// 添加数据PUT website/blog/11{ "pub_date": "2018-10-10" }PUT website/blog/12{ "pub_date": "2018-10-10T12:00:00Z" }// Solr中默认使用的日期格式PUT website/blog/13{ "pub_date": "1589584930103" }// 时间的毫秒值

(2) 多种日期格式:

多个格式使用双竖线||分隔, 每个格式都会被依次尝试, 直到找到匹配的.第一个格式用于将时间毫秒值转换为对应格式的字符串.

/ 添加映射PUT website{    "mappings": { "blog": {     "properties": {  "date": {      "type": "date",  // 可以接受如下类型的格式      "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"  }     } }    }}

布尔类型 - boolean

可以接受表示真、假的字符串或数字:

真值: true, “true”, “on”, “yes”, “1”…

假值: false, “false”, “off”, “no”, “0”, “”(空字符串), 0.0, 0

二进制型 - binary

二进制类型是Base64编码字符串的二进制值, 不以默认的方式存储, 且不能被搜索. 有2个设置项:

(1) doc_values: 该字段是否需要存储到磁盘上, 方便以后用来排序、聚合或脚本查询. 接受true和false(默认);

(2) store: 该字段的值是否要和_source分开存储、检索, 意思是除了_source中, 是否要单独再存储一份. 接受true或false(默认).

// 添加映射PUT website{    "mappings": { "blog": {     "properties": {  "blob": {"type": "binary"}   // 二进制     } }    }}// 添加数据PUT website/blog/1{    "title": "Some binary blog",    "blob": "hED903KSrA084fRiD5JLgY=="}

注意: Base64编码的二进制值不能嵌入换行符\n, 逗号(0x2c)等符号.

 范围类型 - range

range过滤允许我们按照制定范围查找一批数据

复杂数据类型Array

ES中没有专门的数组类型, 直接使用[]定义即可;

数组中所有的值必须是同一种数据类型, 不支持混合数据类型的数组:

① 字符串数组: [“one”, “two”]; ② 整数数组: [1, 2]; ③ 由数组组成的数组: [1, [2, 3]], 等价于[1, 2, 3]; ④ 对象数组: [{“name”: “Tom”, “age”: 20}, {“name”: “Jerry”, “age”: 18}].

注意:

  • 动态添加数据时, 数组中第一个值的类型决定整个数组的类型;
  • 不支持混合数组类型, 比如[1, “abc”];
  • 数组可以包含null值, 空数组[]会被当做missing field —— 没有值的字段.

复杂数据类型Object

JSON文档是分层的: 文档可以包含内部对象, 内部对象也可以包含内部对象.

复杂数据类型嵌套类型 - nested

嵌套类型是对象数据类型的一个特例, 可以让array类型的对象被独立索引和搜索.

专门数据类型IP类型

IP类型的字段用于存储IPv4或IPv6的地址, 本质上是一个长整型字段.

添加映射

PUT employee{    "mappings": { "customer": {     "properties": {  "ip_addr": { "type": "ip" }     } }    }}

添加数据

PUT employee/customer/1{ "ip_addr": "192.168.1.1" }

计数数据类型 - token_counttoken_count类型用于统计字符串中的单词数量.

添加映射

PUT employee{    "mappings": { "customer": {     "properties": {  "name": {"type": "text",      "fields": {   "length": {"type": "token_count", "analyzer": "standard"   }      }  }     } }    }}

添加数据:

PUT employee/customer/1{ "name": "John Snow" }PUT employee/customer/2{ "name": "Tyrion Lannister" }

 查询数据

GET employee/customer/_search{    "query": { "term": { "name.length": 2 }    }}

整合SpringBoot 

整合前熟悉几个重要的对象与构造器

SearchRequest 查询请求对象
SearchResponse 查询响应对象
SearchSourceBuilder 查询源构建器
MatchQueryBuilder 匹配查询构建器

BoolQueryBuilder

布尔查询构建器

TermQueryBuilder

词条查想构建器

QueryBuilders

查询构建器工厂

AggregationBuilders

聚合构建器工厂

TremsAggregationBuilder

词条聚合构造器

Aggregations

分组结果封装

Terms.Bucket

Spring boot中主要有Java REST Clientspring-data-elasticsearch两种方式可以整合ES 本文使用spring-data-elasticsearch

    org.springframework.boot    spring-boot-starter-data-elasticsearch

配置文件

@Configurationpublic class RestClientConfig extends AbstractElasticsearchConfiguration {    @Override    @Bean    public RestHighLevelClient elasticsearchClient() { final ClientConfiguration clientConfiguration = ClientConfiguration.builder()  .connectedTo("localhost:9200")  .build(); return RestClients.create(clientConfiguration).rest();    }}

 创建索引 并 映射 & 删除索引 

put /products   #products索引名称{    "mappings": { "properties": {     "title": {  // 字段名称  "type": "keyword" // 类型     },     "price": { // 字段名称  "type": "double" // 类型     },     "created_time": { // 字段名称  "type": "date" // 类型     },     "description": { // 字段名称  "type": "text", // 类型  "analyzer": "ik_max_word" // 开启分词器     } }    }}#删除索引DELETE /products

 Boot

@Resourceprivate RestHighLevelClient restHighLevelClient;/** *  创建索引 创建映射 */@Testvoid indexAndMapping() throws IOException {    CreateIndexRequest createIndexRequest = new CreateIndexRequest("products");    createIndexRequest.mapping("{\n" +     "    \"properties\": {\n" +     "      \"title\":{\n" +     " \"type\": \"keyword\"\n" +     "      },\n" +     "      \"price\":{\n" +     " \"type\": \"double\"\n" +     "      },\n" +     "      \"created_time\":{\n" +     " \"type\": \"date\"\n" +     "      },\n" +     "      \"description\":{\n" +     " \"type\": \"text\",\n" +     " \"analyzer\":\"ik_max_word\"\n" +     "      }\n" +     "    }\n" +     "  }", XContentType.JSON);//指定映射参数1:指定映射json结构  参数2:指定数据类型    CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);    System.out.println("创建状态: "+createIndexResponse.isAcknowledged());    restHighLevelClient.close();//关闭资源}/** * 删除索引 */@Testpublic void testDeleteIndex() throws IOException {    //参数 1: 删除索引对象  参数 2:请求配置对象    AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(new DeleteIndexRequest("products"), RequestOptions.DEFAULT);    System.out.println(acknowledgedResponse.isAcknowledged());}

 添加 & 修改 数据

/** * @author 小影 * @create 2022-05-09 9:51 * @describe:商品 */@Datapublic class Product {    private Integer id;    // 标题    private String title;    // 价格    private Double price;    // 创建时间    private LocalDateTime createTime;    // 描述    private String description;}
/** * 添加数据(文档) * @throws IOException */@Testvoid testCreate() throws IOException {    // param:索引名称    IndexRequest indexRequest = new IndexRequest("products");    // 要存储数据    Product product = new Product();    product.setId(1);    product.setTitle("蜜汁小汉堡");    product.setCreateTime(LocalDateTime.now());    product.setPrice(3.0);    product.setDescription("老八秘制小汉堡,好吃又管饱");    indexRequest.id(product.getId().toString());// 存储ID,不设置默认随机字符串    indexRequest.source(JSONUtil.toJsonStr(product), XContentType.JSON);//指定文档数据    //参数 1: 索引请求对象  参数 2:请求配置对象    IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);    System.out.println(indexResponse.status());// CREATED:成功}

修改和添加是一样的,传入的参数中的ID在es中存在即为修改,不同的是修改成功indexResponse.status()返回的 是OK,如果是添加返回的是CREATE

 删除数据

/** * 删除数据(文档) */@Testpublic void testDelete() throws IOException {    //参数 1: 删除请求对象 参数 2: 请求配置对象    DeleteRequest deleteRequest = new DeleteRequest("products", "5W25poAB9vN9HF984Etg");    DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);    System.out.println(deleteResponse.status());// 成功返回OK}

根据ID查询数据

/** * 基于id 查询数据(文档) */@Testpublic void testQueryById() throws IOException {    GetRequest getRequest = new GetRequest("products", "1");    //参数 1: 查询请求对象 参数 2:请求配置对象  返回值: 查询响应对象    GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);    System.out.println("id: "+getResponse.getId());    System.out.println("source: "+getResponse.getSourceAsString());}

 查询所有数据

/** * 查询所有数据(文档) * 查询所有matchAllQuery */@Testpublic void testMatchAll() throws IOException {    SearchRequest searchRequest = new SearchRequest("products"); //指定搜索索引    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象    sourceBuilder.query(QueryBuilders.matchAllQuery());//查询所有    searchRequest.source(sourceBuilder);//指定查询条件    SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象    System.out.println("总条数: " + searchResponse.getHits().getTotalHits().value);    System.out.println("最大得分: " + searchResponse.getHits().getMaxScore());    //获取结果    SearchHit[] hits = searchResponse.getHits().getHits();    for (SearchHit hit : hits) { String id = hit.getId(); System.out.println("id: "+ id +" source: "+hit.getSourceAsString());    }}

 

 不同条件查询(关键字、范围、前缀、通配符、多个ID、多个字段)

注意:es只能匹配单字符 不能搜索词语的问题 ES默认分词规则不能支持中文,通过安装IK Analysis for Elasticsearch支持中文分词。

分词器下载地址,切记要与es版本一致:Releases · medcl/elasticsearch-analysis-ik · GitHub

/** * 不同条件查询 term(关键词查询) */@Testpublic void testQuery() throws IOException {    //1.term 关键词  只能匹配单字符 不能搜索词语的问题 ES默认分词规则不能支持中文,通过安装IK Analysis for Elasticsearch支持中文分词。    //query(QueryBuilders.termQuery("description","小汉堡"));    //2.range 范围 价格在0-6之间的数据    //query(QueryBuilders.rangeQuery("price").gt(0).lte(6));    //3.prefix 前缀    //query(QueryBuilders.prefixQuery("title","s蜜汁"));    //4.wildcard 通配符查询 ? 一个字符  * 任意多个字符    //query(QueryBuilders.wildcardQuery("title","*小汉堡*"));    //5.ids 多个指定 id 查询    //query(QueryBuilders.idsQuery().addIds("1").addIds("2"));    //6.multi_match 多字段查询    query(QueryBuilders.multiMatchQuery("豆腐","title","description"));}public void query(QueryBuilder queryBuilder) throws IOException {    SearchRequest searchRequest = new SearchRequest("products");//指定搜索索引    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象    sourceBuilder.query(queryBuilder);//指定查询条件    searchRequest.source(sourceBuilder);    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象    System.out.println("符合条件总数: "+searchResponse.getHits().getTotalHits().value);    System.out.println("获取文档最大分数: "+searchResponse.getHits().getMaxScore());    SearchHit[] hits = searchResponse.getHits().getHits();    for (SearchHit hit : hits) { System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString());    }}

 分页、排序、指定返回字段、高亮结果

/** * 查询所有 * 分页查询 form 起始位置默认0开始   size 每页展示记录数 * 排序 sort * 返回指定的字段 fetchSource  用来指定查询文档返回那些字段 * 高亮结果  highlighter */@Testpublic void testSearch() throws IOException {    SearchRequest searchRequest = new SearchRequest("products");    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();    //创建高亮器    HighlightBuilder highlightBuilder = new HighlightBuilder();    highlightBuilder.requireFieldMatch(false).field("description").field("title").preTags("").postTags("");    sourceBuilder.query(QueryBuilders.termQuery("description","简简单单"))     .from(0) //起始位置 start = (page-1)*size     .size(10)//每页显示条数 默认返回 10条     .sort("price", SortOrder.ASC)//指定排序字段 参数 1: 根据哪个字段排序  参数 2:排序方式 [asc,desc]     .fetchSource(new String[]{},new String[]{"create_time"})//参数 1: 包含字段数组  参数 2:排除字段数组     .highlighter(highlightBuilder);//高亮搜索结果    searchRequest.source(sourceBuilder);    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);    System.out.println("符合条件总数: "+searchResponse.getHits().getTotalHits().value);    System.out.println("获取文档最大分数: "+searchResponse.getHits().getMaxScore());    SearchHit[] hits = searchResponse.getHits().getHits();    for (SearchHit hit : hits) { System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString()); //获取高亮字段 Map highlightFields = hit.getHighlightFields(); if (highlightFields.containsKey("description")){     System.out.println("description高亮结果: "+highlightFields.get("description").fragments()[0]); } if (highlightFields.containsKey("title")){     System.out.println("title高亮结果: "+highlightFields.get("title").fragments()[0]); }    }}

 过滤查询

过滤查询,其实准确来说,ES中的查询操作分为2种: 查询(query)和过滤(filter)。查询即是之前提到的query查询,它 (查询)默认会计算每个返回文档的得分,然后根据得分排序。而过滤(filter)只会筛选出符合的文档,并不计算 得分,而且它可以缓存文档 。所以,单从性能考虑,过滤比查询更快。 换句话说过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时, 应先使用过滤操作过滤数据, 然后使用查询匹配数据。

/** * query     : 查询精确查询  查询计算文档得分 并根据文档得分进行返回 * filter query     : 过滤查询  用来在大量数据中筛选出本地查询相关数据  不会计算文档得分 经常使用 filter query 结果进行缓存 * 注意: 一旦使用 query 和 filterQuery  es 优先执行 filter Query 然后再执行 query 所以使用filter query 查询会比较快 */@Testpublic void testFilterQuery() throws IOException {    SearchRequest searchRequest = new SearchRequest("products");    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();    sourceBuilder     .query(QueryBuilders.termQuery("description","老八"))     .postFilter(QueryBuilders.idsQuery().addIds("1").addIds("2").addIds("3"));//用来指定过滤条件    searchRequest.source(sourceBuilder);    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);    System.out.println("符合条件总数: "+searchResponse.getHits().getTotalHits().value);    System.out.println("获取文档最大分数: "+searchResponse.getHits().getMaxScore());    SearchHit[] hits = searchResponse.getHits().getHits();    for (SearchHit hit : hits) { System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString());    }}

 聚合查询 max(ParsedMax)、 min(ParsedMin) 、sum(ParsedSum)、 avg(ParsedAvg)

es语法:

先根据某个字段分组 此案例中根据价格分组

GET /products/_search{  "query": {    "term": {      "title": { "value": "老八米饭"      }    }  },  "aggs": {    "price_gooup": {      "terms": { "field": "price"      }    }  }}

之后就可以操作

求最大值

GET /products/_search{  "aggs": {    "price_max": {      "max": { "field": "price"      }    }  }}

 求最小值

GET /products/_search{  "aggs": {    "price_min": {      "min": { "field": "price"      }    }  }}

求平均值

GET /products/_search{  "aggs": {    "price_avg": {      "avg": { "field": "price"      }    }  }}

....

/** * max(ParsedMax) min(ParsedMin) sum(ParsedSum) avg(ParsedAvg) 聚合函数  桶中只有一个返回值 */@Testpublic void testAggsFunction() throws IOException {    SearchRequest searchRequest = new SearchRequest("products");    SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();    sourceBuilder     .query(QueryBuilders.matchAllQuery()) //查询条件     //.aggregation(AggregationBuilders.sum("price_sum").field("price"))//用来设置聚合处理 sum     //.aggregation(AggregationBuilders.avg("price_avg").field("price")) //用来设置聚合处理 avg     .aggregation(AggregationBuilders.max("price_min").field("price")) //max     .size(0);//默认查询所有进行聚合    searchRequest.source(sourceBuilder);    SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);    Aggregations aggregations = searchResponse.getAggregations();    ParsedMax parsedMax = aggregations.get("price_min");    System.out.println(parsedMax.getValue());}

nested嵌套对象 关联查询

需求:通过传递一系列评论来存储博客文章及其所有评论。数据结构如下:

{  "title": "牛人集结地",  "body": "随便评论",  "comments": [    {      "name": "张三",      "age": 33,      "rating": 6,      "comment": "找我牛逼",      "commented_on": "22 Nov 2022"    },    {      "name": "老八",      "age": 33,      "rating": 9,      "comment": "吃俩柠檬整点蒜,简简单单一顿饭,美食界里我老八,今天给你们吃沙拉",      "commented_on": "25 Nov 2022"    },    {      "name": "吕小布",      "age": 35,      "rating": 6,      "comment": "踏遍青楼人未老,请用汇仁肾宝",      "commented_on": "21 Nov 2022"    }  ]}
#创建索引PUT /blog_new{  "mappings": {      "properties":{ "title":{   "type":"text" }, "body":{   "type":"text" }, "comments":{   "type":"nested",   "properties": {     "name": {"type": "text"     },     "comment": {"type": "text"     },     "age": {"type": "short"     },     "rating": {"type": "short"     },     "commented_on": {"type": "text"     }   } }      }  }}
/** * 添加关联信息 */@Testpublic void addParent() throws IOException {    IndexRequest indexRequest = new IndexRequest("blog_new");    String id = UUID.randomUUID().toString();    Blog blog = new Blog();    blog.setBody("随便评论");    blog.setTitle("牛人集结地");    List list = new ArrayList();    Comments comments1 = new Comments();    comments1.setName("张三");    comments1.setAge(33);    comments1.setRating(6);    comments1.setComment("找我牛逼");    comments1.setCommented_on("22 Nov 2022");    Comments comments2 = new Comments();    comments2.setName("老八");    comments2.setAge(33);    comments2.setRating(9);    comments2.setComment("吃俩柠檬整点蒜,简简单单一顿饭,美食界里我老八,今天给你们吃沙拉");    comments2.setCommented_on("25 Nov 2022");    Comments comments3 = new Comments();    comments3.setName("吕小布");    comments3.setAge(35);    comments3.setRating(6);    comments3.setComment("踏遍青楼人未老,请用汇仁肾宝");    comments3.setCommented_on("21 Nov 2022");    list.add(comments1);    list.add(comments2);    list.add(comments3);    blog.setComments(list);    String source = JSON.toJSONString(blog);    System.out.println("source = " + source);    indexRequest.id("W_DKq4ABHdN8KsLXQ2ZR");    indexRequest.source(source, XContentType.JSON);     IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);    System.out.println(indexResponse.status());}

 

 查询:

@Testpublic void testGet() throws IOException {    SearchRequest request = new SearchRequest("blog_new"); // 初始化需要查询的索引    //查询student 字段下的hobby字段下的name字段的值 等于篮球的学生,这里特别注意第一层必须是你查询的字段的前一层字段    // 查询条件1 comment存在老八    QueryBuilder one = QueryBuilders.nestedQuery("comments", QueryBuilders.boolQuery()     .must(QueryBuilders.matchQuery("comments.comment", "老八")), ScoreMode.Total);    // 并且年龄在33    QueryBuilder two = QueryBuilders.nestedQuery("comments", QueryBuilders.boolQuery()     .must(QueryBuilders.matchQuery("comments.age", "33")), ScoreMode.Total); BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); // 初始化 最后组合成的条件    queryBuilder.must(one);    queryBuilder.must(two);    //初始化条件builer    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();    //加入组合成的条件到builder    searchSourceBuilder.query(queryBuilder);    //设置查询的分页 第一个从0开始    searchSourceBuilder.from(0);    //每页的大小为2    searchSourceBuilder.size(2);    //初始化索引中加入 builder    request.source(searchSourceBuilder);    try { SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT); SearchHit[] hits = searchResponse.getHits().getHits(); //这里把数据转成实体 List res = new ArrayList(); for (SearchHit hit : hits) {     res.add(JSON.parseObject(hit.getSourceAsString(), Blog.class)); } System.out.println(JSON.toJSONString(res));    } catch (Exception e) { e.printStackTrace();    }}

缺点:更新某个内容只能把这条数据,都替换,删除某个子对象也是


自己根据案例改的工具类

/** * @author 小影 * @create 2022-05-09 14:54 * @describe:ElasticSearch工具类 */@Componentpublic class EsUtil {    private static final Logger logger = LoggerFactory.getLogger(EsUtil.class);    @Resource    private RestHighLevelClient restHighLevelClient;    /**     * 判断一个索引是否存在     *     * @param indexName     * @return     */    public boolean isExistsIndex(String indexName) { GetIndexRequest request = new GetIndexRequest(); try {     boolean exists = restHighLevelClient.indices().exists(request.indices(indexName), RequestOptions.DEFAULT);     return exists; } catch (IOException e) {     e.printStackTrace(); } return false;    }    /**     * 删除索引     *     * @param indexName 索引名称     * @return     * @throws IOException     */    public boolean DeleteIndex(String indexName) { //参数 1: 删除索引对象  参数 2:请求配置对象 AcknowledgedResponse acknowledgedResponse = null; try {     acknowledgedResponse = restHighLevelClient.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT); } catch (IOException e) {     e.printStackTrace(); } return acknowledgedResponse.isAcknowledged() == true ? true : false;    }    /**     * 添加数据 & 修改数据     *     * @param indexName 索引名称     * @param id 数据id     * @param dataJson  要存储的数据JSON格式     * @return ID在es库中不存在即为添加, 存在修改     */    public boolean createData(String indexName, String id, String dataJson) { try {     // param:索引名称     IndexRequest indexRequest = new IndexRequest(indexName);     indexRequest.id(id);// 存储ID,不设置默认随机字符串     indexRequest.source(dataJson, XContentType.JSON);//指定文档数据     //参数 1: 索引请求对象  参数 2:请求配置对象     IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);     System.err.println(indexResponse);     System.out.println(indexResponse.status());     if (indexResponse.status() == RestStatus.CREATED || indexResponse.status() == RestStatus.OK) {  return true;     } } catch (IOException e) {     e.printStackTrace(); } return false;    }    /**     * 根据ID删除数据     *     * @param indexName 索引名称     * @param id ID     * @return     */    public boolean deleteDataById(String indexName, String id) { DeleteResponse deleteResponse = null; try {     DeleteRequest deleteRequest = new DeleteRequest(indexName, id);     deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT); } catch (IOException e) {     e.printStackTrace(); } return deleteResponse.status() == RestStatus.OK;    }    /**     * 根据ID查询数据(文档)     *     * @param indexName     * @param id     * @return     */    public String queryById(String indexName, String id) { try {     GetRequest getRequest = new GetRequest(indexName, id);     //参数 1: 查询请求对象 参数 2:请求配置对象  返回值: 查询响应对象     GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);     return getResponse.getSourceAsString(); } catch (IOException e) {     e.printStackTrace(); } return null;    }    /**     * 查询所有:查询、分页     *     * @param indexName 索引名称     * @param pageNo    页码     * @param pageSize  页大小     * @return     */    public SearchHit[] queryAll(String indexName, Integer pageNo, Integer pageSize) { SearchResponse searchResponse = null;//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象 try {     SearchRequest searchRequest = new SearchRequest(indexName); //指定搜索索引     SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象     sourceBuilder.query(QueryBuilders.matchAllQuery())//查询所有      .from((pageNo - 1) * pageSize)      .size(pageSize);     searchRequest.source(sourceBuilder);//指定查询条件     searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT); } catch (IOException e) {     e.printStackTrace(); } return searchResponse.getHits().getHits();    }    /**     * 调用通用查询借鉴     *     * @throws IOException     */    public void testQuery() throws IOException { //1.term 关键词  只能匹配单字符 不能搜索词语的问题 ES默认分词规则不能支持中文,通过安装IK Analysis for Elasticsearch支持中文分词。 //query(QueryBuilders.termQuery("description","小汉堡")); //2.range 范围 价格在0-6之间的数据 //query(QueryBuilders.rangeQuery("price").gt(0).lte(6)); //3.prefix 前缀 //query(QueryBuilders.prefixQuery("title","s蜜汁")); //4.wildcard 通配符查询 ? 一个字符  * 任意多个字符 //query(QueryBuilders.wildcardQuery("title","*小汉堡*")); //5.ids 多个指定 id 查询 //query(QueryBuilders.idsQuery().addIds("1").addIds("2")); //6.multi_match 多字段查询 query(QueryBuilders.multiMatchQuery("豆腐", "title", "description"));    }    /**     * 通用查询     *     * @param queryBuilder     * @throws IOException     */    public SearchHit[] query(QueryBuilder queryBuilder) throws IOException { SearchRequest searchRequest = new SearchRequest("products");//指定搜索索引 SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象 sourceBuilder.query(queryBuilder);//指定查询条件 searchRequest.source(sourceBuilder); SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象 SearchHit[] hits = searchResponse.getHits().getHits(); return hits;    }}

这是小编在开发学习使用和总结,  这中间或许也存在着不足,希望可以得到大家的理解和建议。如有侵权联系小编!