Spring Data Elasticsearch--ElasticsearchRestTemplate--使用/教程/实例

news2024/10/6 21:39:00

原文网址:Spring Data Elasticsearch--ElasticsearchRestTemplate--使用/教程/实例_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文用实例来介绍如何使用Spring Data Elasticsearch的ElasticsearchRestTemplate来操作ES。包括:索引的增删等、文档的增删改查、文档的动态查询(或者说:多条件查询、复杂查询)。

        动态查询的含义:查询条件有多个时,某个查询条件可能有也可能没有,这时就需要手动判断,如果为空则不拼装搜索条件。

        本文用博客的业务进行示例。索引名:blog,里边有标题、内容、作者、创建时间等。

官网

Elasticsearch Operations

为什么用ElasticsearchRestTemplate

        在Spring Data Elasticsearch4.0之后,ElasticsearchRepository里边大部分搜索方法都已经被标记为废弃,注释中让我们用两种方法来操作:

  1. 在方法上使用@Query自定义DSL。
  2. 使用 ElasticsearchOperations来操作。

使用@Query自定义DSL不支持动态查询,其用法见:Spring Data Elasticsearch--使用/教程/实例_IT利刃出鞘的博客-CSDN博客

ElasticsearchOperations支持动态查询,ElasticsearchRestTemplate是ElasticsearchOperations的实现类。

公共代码

依赖及配置

依赖

主要是spring-boot-starter-data-elasticsearch这个依赖:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

配置

spring:
  elasticsearch:
    rest:
      # 如果是集群,用逗号隔开
      uris: http://127.0.0.1:9200
      # username: xxx
      # password: yyy
      # connection-timeout: 1
      # read-timeout: 30

# 上边是客户端High Level REST Client的配置,推荐使用。

# 下边是reactive客户端的配置,非官方,不推荐。
#  data:
#    elasticsearch:
#      client:
#        reactive:
#          endpoints: 127.0.0.1:9200
#          username: xxx
#          password: yyy
#          connection-timeout: 1
#          socket-timeout: 30
#          use-ssl: false

整个pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo_spring-data-elasticsearch</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo_spring-data-elasticsearch</name>
    <description>demo_spring-data-elasticsearch</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

索引结构及数据

索引结构

http://localhost:9200/
PUT blog

{
    "mappings": {
        "properties": {
            "id":{
                "type":"long"
            },
            "title": {
                "type": "text"
            },
            "content": {
                "type": "text"
            },
            "author":{
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword"
                    }
                }
            },
            "category":{
                "type": "keyword"
            },
            "createTime": {
                "type": "date",
                "format":"yyyy-MM-dd HH:mm:ss.SSS||yyyy-MM-dd'T'HH:mm:ss.SSS||yyyy-MM-dd HH:mm:ss||epoch_millis"
            },
            "updateTime": {
                "type": "date",
                "format":"yyyy-MM-dd HH:mm:ss.SSS||yyyy-MM-dd'T'HH:mm:ss.SSS||yyyy-MM-dd HH:mm:ss||epoch_millis"
            },
            "status":{
                "type":"integer"
            },
            "serialNum": {
                "type": "keyword"
            }
        }
    }
}

数据

  • 每个文档必须独占一行,不能换行。
  • 此命令要放到postman中去执行,如果用head执行会失败

http://localhost:9200/
POST _bulk

{"index":{"_index":"blog","_id":1}}
{"blogId":1,"title":"Spring Data ElasticSearch学习教程1","content":"这是批量添加的文档1","author":"Iron Man","category":"ElasticSearch","status":1,"serialNum":"1","createTime":"2021-10-10 11:52:01.249","updateTime":null}
{"index":{"_index":"blog","_id":2}}
{"blogId":2,"title":"Spring Data ElasticSearch学习教程2","content":"这是批量添加的文档2","author":"Iron Man","category":"ElasticSearch","status":1,"serialNum":"2","createTime":"2021-10-10 11:52:02.249","updateTime":null}
{"index":{"_index":"blog","_id":3}}
{"blogId":3,"title":"Spring Data ElasticSearch学习教程3","content":"这是批量添加的文档3","author":"Captain America","category":"ElasticSearch","status":1,"serialNum":"3","createTime":"2021-10-10 11:52:03.249","updateTime":null}
{"index":{"_index":"blog","_id":4}}
{"blogId":4,"title":"Spring Data ElasticSearch学习教程4","content":"这是批量添加的文档4","author":"Captain America","category":"ElasticSearch","status":1,"serialNum":"4","createTime":"2021-10-10 11:52:04.249","updateTime":null}
{"index":{"_index":"blog","_id":5}}
{"blogId":5,"title":"Spring Data ElasticSearch学习教程5","content":"这是批量添加的文档5","author":"Spider Man","category":"ElasticSearch","status":1,"serialNum":"5","createTime":"2021-10-10 11:52:05.249","updateTime":null}
{"index":{"_index":"blog","_id":6}}
{"blogId":6,"title":"Java学习教程6","content":"这是批量添加的文档6","author":"Spider Man","category":"ElasticSearch","status":1,"serialNum":"6","createTime":"2021-10-10 11:52:06.249","updateTime":null}
{"index":{"_index":"blog","_id":7}}
{"blogId":7,"title":"Java学习教程7","content":"这是批量添加的文档7","author":"Iron Man","category":"ElasticSearch","status":1,"serialNum":"7","createTime":"2021-10-10 11:52:07.249","updateTime":null}
{"index":{"_index":"blog","_id":8}}
{"blogId":8,"title":"Java学习教程8","content":"这是批量添加的文档8","author":"Iron Man","category":"ElasticSearch","status":1,"serialNum":"8","createTime":"2021-10-10 11:52:08.249","updateTime":null}
{"index":{"_index":"blog","_id":9}}
{"blogId":9,"title":"Java学习教程9","content":"这是批量添加的文档9","author":"Captain America","category":"ElasticSearch","status":1,"serialNum":"9","createTime":"2021-10-10 11:52:09.249","updateTime":null}
{"index":{"_index":"blog","_id":10}}
{"blogId":10,"title":"Java学习教程10","content":"这是批量添加的文档10","author":"God Of Thunder","category":"ElasticSearch","status":1,"serialNum":"10","createTime":"2021-10-10 11:52:10.249","updateTime":null}

执行后的结果

实体类

package com.example.demo.entity;

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.DateFormat;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.util.Date;

@Data
@Document(indexName = "blog", shards = 1, replicas = 1)
public class Blog {
    //此项作为id,不会写到_source里边。
    @Id
    private Long blogId;

    @Field(type = FieldType.Text)
    private String title;

    @Field(type = FieldType.Text)
    private String content;

    @Field(type = FieldType.Text)
    private String author;

    //博客所属分类。
    @Field(type = FieldType.Keyword)
    private String category;

    //0: 未发布(草稿) 1:已发布 2:已删除
    @Field(type = FieldType.Integer)
    private int status;

    //序列号,用于给外部展示的id
    @Field(type = FieldType.Keyword)
    private String serialNum;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    @Field(type= FieldType.Date, format= DateFormat.custom, pattern="yyyy-MM-dd HH:mm:ss.SSS")
    private Date createTime;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss.SSS")
    @Field(type=FieldType.Date, format=DateFormat.custom, pattern="yyyy-MM-dd HH:mm:ss.SSS")
    private Date updateTime;
}

索引操作(IndexOperations)

本处只是展示索引的操作方法,项目中不会这样创建索引,而是手写DSL去创建。

package com.example.demo.controller;

import com.example.demo.entity.Blog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 本处只是展示索引的操作方法,项目中不会这样创建索引,而是手写DSL去创建。
 */
@Api(tags = "索引操作(用不到)")
@RestController
@RequestMapping("index")
public class IndexController {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;


    @ApiOperation("创建索引")
    @PostMapping("createIndex")
    public String createIndex() {
        // 创建索引,会根据Blog类的@Document注解信息来创建
        elasticsearchRestTemplate.createIndex(Blog.class);

        // 配置映射,会根据Item类中的id、Field等字段来自动完成映射
        elasticsearchRestTemplate.putMapping(Blog.class);
        return "success";
    }

    @ApiOperation("创建索引")
    @PostMapping("deleteIndex")
    public String deleteIndex() {
        // 删除索引,会根据Blog类的@Document注解信息来删除
        elasticsearchRestTemplate.deleteIndex(Blog.class);
        return "success";
    }
}

文档操作(DocumentOperations)

        本处介绍常用的一些方法,这些方法能够满足开发中所有需求:

  1. 添加单个文档
  2. 添加多个文档
  3. 修改单个文档数据
  4. 修改单个文档部分数据
  5. 修改多个文档部分数据
  6. 查看单个文档
  7. 删除单个文档(根据id)
  8. 删除单个文档(根据条件)
  9. 删除所有文档。
package com.example.demo.controller;

import com.example.demo.entity.Blog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Api(tags = "增删改查(RestTemplate方式)")
@RestController
@RequestMapping("crudViaRestTemplate")
public class CrudViaRestTemplateController {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @ApiOperation("添加单个文档")
    @PostMapping("addDocument")
    public Blog addDocument() {
        Long id = 1L;
        Blog blog = new Blog();
        blog.setBlogId(id);
        blog.setTitle("Spring Data ElasticSearch学习教程" + id);
        blog.setContent("这是添加单个文档的实例" + id);
        blog.setAuthor("Tony");
        blog.setCategory("ElasticSearch");
        blog.setCreateTime(new Date());
        blog.setStatus(1);
        blog.setSerialNum(id.toString());

        return elasticsearchRestTemplate.save(blog);
    }

    @ApiOperation("添加多个文档")
    @PostMapping("addDocuments")
    public Object addDocuments(Integer count) {
        List<Blog> blogs = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            Long id = (long)i;
            Blog blog = new Blog();
            blog.setBlogId(id);
            blog.setTitle("Spring Data ElasticSearch学习教程" + id);
            blog.setContent("这是添加单个文档的实例" + id);
            blog.setAuthor("Tony");
            blog.setCategory("ElasticSearch");
            blog.setCreateTime(new Date());
            blog.setStatus(1);
            blog.setSerialNum(id.toString());
            blogs.add(blog);
        }

        return elasticsearchRestTemplate.save(blogs);
    }

    /**
     * 跟新增是同一个方法。若id已存在,则修改。
     * 无法只修改某个字段,只能覆盖所有字段。若某个字段没有值,则会写入null。
     * @return 成功写入的数据
     */
    @ApiOperation("修改单个文档数据")
    @PostMapping("editDocument")
    public Blog editDocument() {
        Long id = 1L;
        Blog blog = new Blog();
        blog.setBlogId(id);
        blog.setTitle("Spring Data ElasticSearch学习教程" + id);
        blog.setContent("这是修改单个文档的实例" + id);

        return elasticsearchRestTemplate.save(blog);
    }

    @ApiOperation("修改单个文档部分数据")
    @PostMapping("editDocumentPart")
    public UpdateResponse editDocumentPart() {
        long id = 1L;

        Document document = Document.create();
        document.put("title", "修改后的标题" + id);
        document.put("content", "修改后的内容" + id);

        UpdateQuery updateQuery = UpdateQuery.builder(Long.toString(id))
                .withDocument(document)
                .build();

        UpdateResponse response = elasticsearchRestTemplate.update(updateQuery, IndexCoordinates.of("blog"));

        return response;
    }

    @ApiOperation("修改多个文档部分数据")
    @PostMapping("editDocumentsPart")
    public void editDocumentsPart(int count) {
        List<UpdateQuery> updateQueryList = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            long id = (long) i;
            Document document = Document.create();
            document.put("title", "修改后的标题" + id);
            document.put("content", "修改后的内容" + id);
            UpdateQuery updateQuery = UpdateQuery.builder(Long.toString(id))
                    .withDocument(document)
                    .build();
            updateQueryList.add(updateQuery);
        }

        elasticsearchRestTemplate.bulkUpdate(updateQueryList, IndexCoordinates.of("blog"));
    }

    @ApiOperation("查看单个文档")
    @GetMapping("findById")
    public Blog findById(Long id) {

        return elasticsearchRestTemplate.get(
                id.toString(), Blog.class, IndexCoordinates.of("blog"));
    }

    @ApiOperation("删除单个文档(根据id)")
    @PostMapping("deleteDocumentById")
    public String deleteDocumentById(Long id) {
        return elasticsearchRestTemplate.delete(id.toString(), Blog.class);
    }

    @ApiOperation("删除单个文档(根据条件)")
    @PostMapping("deleteDocumentByQuery")
    public void deleteDocumentByQuery(String title) {
        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("title", title))
                .build();

        elasticsearchRestTemplate.delete(nativeSearchQuery, Blog.class, IndexCoordinates.of("blog"));
    }

    @ApiOperation("删除所有文档")
    @PostMapping("deleteDocumentAll")
    public void deleteDocumentAll() {
        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchAllQuery())
                .build();
        elasticsearchRestTemplate.delete(nativeSearchQuery, Blog.class, IndexCoordinates.of("blog"));
    }
}

本处就不贴出测试的截图了。功能都是测试通过的。

查询操作(SearchOperations)

简介

查询的方法

Query接口有一个抽象实现和三个实现:

        本处我使用NativeSearchQuery。因为它更贴近ES,语法更偏向于ES原来的命令。CriteriaQuery的用法跟JPA的差不多。

构建Query

        可通过new NativeSearchQueryBuilder()来构建NativeSearchQuery对象NativeSearchQuery中有众多的方法来为我们实现复杂的查询与筛选等操作。其中的build()返回NativeSearchQuery。

QueryBuilders构造复杂查询条件

NativeSearchQueryBuilder中接收QueryBuilder

public NativeSearchQueryBuilder withQuery(QueryBuilder queryBuilder) {
	this.queryBuilder = queryBuilder;
	return this;
}

可以用QueryBuilders构造QueryBuilder对象

基本查询

通过标题和内容来查找博客。

package com.example.demo.controller;

import com.example.demo.dao.BlogRepository;
import com.example.demo.entity.Blog;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.ParsedAvg;
import org.elasticsearch.search.aggregations.metrics.ParsedMax;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Range;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilterBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

@Api(tags = "动态查询")
@RestController
@RequestMapping("dynamicQuery")
public class DynamicQueryController {
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @ApiOperation("简单查询")
    @GetMapping("simple")
    public List<Blog> simple(String title, String content) {
        NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (StringUtils.isNotBlank(title)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", title));
        }

        if (StringUtils.isNotBlank(content)) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("content", content));
        }

        query.withQuery(boolQueryBuilder);
        SearchHits<Blog> searchHits = elasticsearchRestTemplate.search(query.build(), Blog.class);

        List<Blog> blogs = new ArrayList<>();
        for (SearchHit<Blog> searchHit : searchHits) {
            blogs.add(searchHit.getContent());
        }
        return blogs;
    }
}

测试

分页排序

通过标题和作者来查找博客,分页,并根据创建时间倒序排序。

@ApiOperation("分页排序")
@GetMapping("pageAndSort")
public Page<Blog> pageAndSort(String title, String author) {
	PageRequest pageRequest = PageRequest.of(0, 2);

	NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
	BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

	if (StringUtils.isNotBlank(title)) {
		boolQueryBuilder.must(QueryBuilders.matchQuery("title", title));
	}

	if (StringUtils.isNotBlank(author)) {
		boolQueryBuilder.must(QueryBuilders.matchQuery("author", author));
	}

	query.withQuery(boolQueryBuilder);
	query.withPageable(pageRequest);
	query.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));

	SearchHits<Blog> searchHits = elasticsearchRestTemplate.search(query.build(), Blog.class);

	List<Blog> blogs = new ArrayList<>();
	for (SearchHit<Blog> searchHit : searchHits) {
		blogs.add(searchHit.getContent());
	}

	return new PageImpl<Blog>(blogs, pageRequest, searchHits.getTotalHits());
}

测试

去重

通过标题和作者来查找博客,根据作者去重。

@ApiOperation("去重")
@GetMapping("collapse")
public List<Blog> collapse(String title, String author) {
	PageRequest pageRequest = PageRequest.of(0, 2);

	NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
	BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

	if (StringUtils.isNotBlank(title)) {
		boolQueryBuilder.must(QueryBuilders.matchQuery("title", title));
	}

	if (StringUtils.isNotBlank(author)) {
		boolQueryBuilder.must(QueryBuilders.matchQuery("author", author));
	}

	query.withQuery(boolQueryBuilder);
	query.withPageable(pageRequest);
	query.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC));

	// 去重的字段不能是text类型。所以,author的mapping要有keyword,且通过author.keyword去重。
	query.withCollapseField("author.keyword");
	//query.withCollapseField("category");

	SearchHits<Blog> searchHits = elasticsearchRestTemplate.search(query.build(), Blog.class);

	List<Blog> blogs = new ArrayList<>();
	for (SearchHit<Blog> searchHit : searchHits) {
		blogs.add(searchHit.getContent());
	}

	return blogs;
}

测试

聚合

查询标题中带有“java”的每个作者的文章数量。

@ApiOperation("聚合")
@GetMapping("aggregation")
public Map<String, Long> aggregation() {
	NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
	BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

	boolQueryBuilder.must(QueryBuilders.matchQuery("title", "java"));

	query.withQuery(boolQueryBuilder);
	// 作为聚合的字段不能是text类型。所以,author的mapping要有keyword,且通过author.keyword聚合。
	query.addAggregation(AggregationBuilders.terms("per_count").field("author.keyword"));
	// 不需要获取source结果集,在aggregation里可以获取结果
	query.withSourceFilter(new FetchSourceFilterBuilder().build());

	SearchHits<Blog> searchHits = elasticsearchRestTemplate.search(query.build(), Blog.class);

	Aggregations aggregations = searchHits.getAggregations();
	assert aggregations != null;
	//因为结果为字符串类型 所以用ParsedStringTerms。其他还有ParsedLongTerms、ParsedDoubleTerms等
	ParsedStringTerms per_count = aggregations.get("per_count");
	Map<String, Long> map = new HashMap<>();
	for (Terms.Bucket bucket : per_count.getBuckets()) {
		map.put(bucket.getKeyAsString(), bucket.getDocCount());
	}

	return map;
}

测试

嵌套聚合

查询标题中带有“java”的每个作者的文章数量,再查出各个作者的最新的发文时间。

@ApiOperation("嵌套聚合")
@GetMapping("subAggregation")
public Map<String, Map<String, Object>> subAggregation() {
	NativeSearchQueryBuilder query = new NativeSearchQueryBuilder();
	BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

	boolQueryBuilder.must(QueryBuilders.matchQuery("title", "java"));

	query.withQuery(boolQueryBuilder);
	query.addAggregation(AggregationBuilders.terms("per_count").field("author.keyword")
			.subAggregation(AggregationBuilders.max("latest_create_time").field("createTime"))
	);

	// 不需要获取source结果集,在aggregation里可以获取结果
	query.withSourceFilter(new FetchSourceFilterBuilder().build());

	SearchHits<Blog> searchHits = elasticsearchRestTemplate.search(query.build(), Blog.class);

	Aggregations aggregations = searchHits.getAggregations();
	assert aggregations != null;
	//因为结果为字符串类型 所以用ParsedStringTerms。其他还有ParsedLongTerms、ParsedDoubleTerms等
	ParsedStringTerms per_count = aggregations.get("per_count");

	Map<String, Map<String, Object>> map = new HashMap<>();

	for (Terms.Bucket bucket : per_count.getBuckets()) {
		Map<String, Object> objectMap = new HashMap<>();
		objectMap.put("docCount", bucket.getDocCount());

		ParsedMax parsedMax = bucket.getAggregations().get("latest_create_time");
		objectMap.put("latestCreateTime", parsedMax.getValueAsString());
		map.put(bucket.getKeyAsString(), objectMap);
	}

	return map;
}

测试 

 粉丝福利:有很多粉丝私信问我有没有面试及PDF书籍等资料,我把它们进行了整理,包含:真实面试题汇总、简历模板、PDF书籍、PPT模板等。这些是我自己也在用的资料,面试题是面试官问到我的问题的整理,其他资料也是我自用的,真正实用、靠谱。资料可以从这里免费获取:资料地址 

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

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

相关文章

【拓扑排序】课程表系列

文章目录 课程表&#xff08;环检测算法&#xff09;1. DFS2. BFS 课程表 II&#xff08;拓扑序列&#xff09;1. DFS2. BFS 课程表 IV&#xff08;记忆化搜索&#xff09;1. DFS2. BFS 课程表&#xff08;环检测算法&#xff09; 1. DFS 先修课程之间的关系可以用有向图表示&…

AI题目整理

1、网络配置时batchsize的大小怎样设置?过小和过大分别有什么特点? Batch size是指一次迭代过程中&#xff0c;输入到神经网络的样本数量。 batchsize太小的缺点&#xff1a; ①耗时长&#xff0c;训练效率低。 ②训练数据就会非常难收敛&#xff0c;从而导致欠拟合。 batch…

MySQL后台线程详解

前言 MySQL的服务实现通过后台多个线程、内存池、文件交互来实现对外服务的&#xff0c;不同线程实现不同的资源操作&#xff0c;各个线程相互协助&#xff0c;共同来完成数据库的服务。本章简单总结MySQL的一些后台线程以及主要作用。 本章收录在MySQL性能优化原理实战专栏&am…

js常见混淆加密技术

下面&#xff0c;我将通过一个案例来演示如何使用JavaScript混淆加密技术来保护你的网站。 假设你有一个网站&#xff0c;其中包含一个登录页面&#xff0c;该页面的JavaScript代码如下所示&#xff1a; function login(username, password) {if (username "admin"…

Doris(21):Doris的函数—日期函数

1 CONVERT_TZ(DATETIME dt, VARCHAR from_tz, VARCHAR to_tz) 转换datetime值dt,从 from_tz 由给定转到 to_tz 时区给出的时区,并返回的结果值。 如果参数无效该函数返回NULL。 select convert_tz(2019-08-01 13:21:03, Asia/Shanghai, America/Los_Angeles); select co…

大数据-玩转数据-初识FLINK

一、初识Flink Flink采用一只松鼠的彩色图案作为logo Apache Flink是一个框架和分布式处理引擎&#xff0c;用于对无界和有界数据流进行有状态计算。Flink被设计在所有常见的集群环境中运行&#xff0c;以内存执行速度和任意规模来执行计算 二、Flink的重要特点 1、事件驱动…

mysql与redis区别

一、.redis和mysql的区别总结 &#xff08;1&#xff09;类型上 从类型上来说&#xff0c;mysql是关系型数据库&#xff0c;redis是缓存数据库 &#xff08;2&#xff09;作用上 mysql用于持久化的存储数据到硬盘&#xff0c;功能强大&#xff0c;但是速度较慢 redis用于存储使…

一篇你看得懂的SNP

单核苷酸多态性&#xff0c;&#xff08;Single Nucleotide Polymorphism&#xff0c;简称SNP&#xff09;指的是由单个核苷酸—A,T,C或G的改变而引起的DNA序列的改变&#xff0c;造成包括人类在内的物种之间染色体基因组的多样性。是指在基因组上单个核苷酸的变异&#xff0c;…

朴素贝叶斯分类器with案例:基于SMS Spam Collection数据集的广告邮件分类

目录 贝叶斯分类器何为朴素案例&#xff1a;基于SMS Spam Collection数据集的广告邮件分类SMS数据集词向量表示Laplacian平滑训练过程分类过程 完整代码 贝叶斯分类器 首先要理解贝叶斯决策的理论依据&#xff0c;引用西瓜书上的原话&#xff1a;对于分类任务&#xff0c;在所…

(基础算法)高精度加法,高精度减法

高精度加法 什么叫做高精度加法呢&#xff1f;包括接下来的高精度减法&#xff0c;高精度乘法与除法都是同一个道理。正常来讲的话加减乘除&#xff0c;四则运算的数字都是整数&#xff0c;也就是需要在int的范围之内&#xff0c;但当这个操作数变得非常"大"的时候&…

《面试1v1》java反射

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a; 你好&#xff0c;请问你对 Java 反射有了解吗&#xff1f; 候选人&#xff1a; 是的&#xff0c;我了解一些。 面试官&#xff1a; 那你能简单…

离散数学集合论

集合论 主要内容 集合基本概念 属于、包含幂集、空集文氏图等 集合的基本运算 并、交、补、差等 集合恒等式 集合运算的算律&#xff0c;恒等式的证明方法 集合的基本概念 集合的定义 集合没有明确的数学定义 理解&#xff1a;由离散个体构成的整体称为集合&#xff0c…

【五一创作】【Midjourney】Midjourney 连续性人物创作 ② ( 获取大图和 Seed 随机种子 | 通过 seed 随机种子生成类似图像 )

文章目录 一、获取大图和 Seed 随机种子二、通过 seed 种子生成类似图像 一、获取大图和 Seed 随机种子 注意 : 一定是使用 U 按钮 , 在生成的大图的基础上 , 添加 信封 表情 , 才能获取该大图的 Seed 种子编码 ; 在上一篇博客生成图像的基础上 , 点击 U3 获取第三张图的大图 ;…

电子数据取证之宝塔面板

一、宝塔面板介绍 1、官网bt.com&#xff0c;是提升运维效率的服务器管理软件&#xff0c;支持一键WAMP/LAMP/LNMP等100多项服务器管理功能&#xff1b;是跨平台的软件&#xff0c;同时支持Windows和Linux。开源永久免费。提高工作效率&#xff0c;对小白比较友好。 2、怎么看服…

【网络socket编程----预备知识和UDP服务器模拟实现】

文章目录 一、预备知识1.1 理解IP地址和端口号1.2 认识TCP协议和UDP协议1.3 网络字节序1.4 socket编程接口和sockaddr结构 二、封装 UdpSocket 一、预备知识 1.1 理解IP地址和端口号 众所周知&#xff0c;每台主机都有一个IP地址。而主机和主机之间通信&#xff0c;也需要依赖…

对比学习论文阅读:CoCLR算法笔记

标题&#xff1a;Self-supervised Co-training for Video Representation Learning 会议&#xff1a;NIPS2020 论文地址&#xff1a;https://dl.acm.org/doi/abs/10.5555/3495724.3496201 官方代码&#xff1a;https://www.robots.ox.ac.uk/~vgg/research/CoCLR/ 作者单位&…

软考算法-排序篇-上

数据排序 一&#xff1a;故事背景二&#xff1a;直接插入排序2.1 概念2.2 画图表示2.3 代码实现2.4 总结提升 三&#xff1a;希尔排序3.1 概念3.2 画图表示3.3 代码实现3.4 总结提升 四&#xff1a;直接选择排序4.1 概念4.2 画图表示4.3 代码实现4.4 总结提升 五&#xff1a;堆…

组播PIM协议

PIM&#xff08;Protocol Independent Multicast&#xff09;称为协议无关组播&#xff08;组播分发树&#xff09;。这里的协议无关指的是与单播路由协议无关&#xff0c;即PIM不需要维护专门的单播路由信息。作为组播路由解决方案&#xff0c;它直接利用单播路由表的路由信息…

LeetCode:142. 环形链表 II

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 题解目录 一、&#x1f331;[142. 环形链表 II](https://leetcode.cn/problems/linked-l…

瑞吉外卖:后台系统登录功能

文章目录 需求分析代码开发创建实体类导入返回结果类Rcontroller、service与mapperlogin.html 需求分析 点击登录按钮后&#xff0c;浏览器以POST方式向employee/login提交username和password&#xff0c;服务器经过处理后向浏览器返回某种格式的数据&#xff0c;其中包含&…