ES 聚合索引简介

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES 聚合索引简介相关的知识,希望对你有一定的参考价值。

参考技术A 本章会简单介绍es的聚合索引,通过分析bucket和metric和使用来进一步了解聚合索引。

Bucket Aggregation通过字段term对field字段进行桶排序,以上语句含义为:划分到达不同目的地国家的统计。

以上我们看到搜索中hits是为空的,聚合下可以看到以目的地国家为划分的不同的桶

以上通过目的地国家进行分桶,并且对最大,最小,平均值进行了统计计算。

以上我们能看到不同的桶下对三种类型计算产生的输出结果。

本章介绍了聚合,通过两个demo简单介绍了Bucket和Metric两种聚合使用。

Spring Boot Elasticsearch7.6.2实现创建索引删除索引判断索引是否存在获取/添加/删除/更新索引别名单条/批量插入单条/批量更新删除数据递归统计ES聚合的数据

Spring Boot Elasticsearch7.6.2实现创建索引、根据索引名删除索引、根据索引名判断索引是否存在、获取索引对应的别名、为索引添加别名、为索引删除别名、为索引更换别名 旧的换为新的 不会判断旧的是否存在、单条数据插入、批量插入、单条数据更新、根据maps批量更新、根据id删除数据、根据id批量删除数据、递归统计ES聚合的数据

注意:我的版本是elasticsearch7.6.2、spring-boot-starter-data-elasticsearch-2.5.6

引入依赖

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
	</dependency>
package com.mar.elasticsearchUtils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.BeanUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasAction;
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.AliasData;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.StringQuery;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @author mar
 * @date 2021年10月28日 11:34
 */
@Component
@Slf4j
public class EsUtil 

    private static ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    public EsUtil(ElasticsearchRestTemplate elasticsearchRestTemplate) 
        EsUtil.elasticsearchRestTemplate = elasticsearchRestTemplate;
    

    public static <T> boolean createIndex(Class<T> destinationClass, String indexName, String... withAliases) 
        if (ObjectUtil.isNull(destinationClass)) throw new RuntimeException("参数类不能为空");
        if (StrUtil.isEmpty(indexName)) throw new RuntimeException("参数名不能为空");
        if (withAliases == null || withAliases.length <= 0) throw new RuntimeException("索引别名至少有一个");
        for (String withAlias : withAliases) 
            if (!withAlias.endsWith("_search")) 
                throw new RuntimeException("索引别名只能以‘_search’结尾");
            
        
        return createIndex(destinationClass, indexName, 3, 2, Integer.MAX_VALUE, withAliases);
    

    /**
     * 创建索引
     *
     * @param destinationClass 映射对象
     * @param withAliases      别名 必须如:"***_search"用来搜索
     * @author mar
     * @date 2021/10/28 14:54
     */
    public static <T> boolean createIndex(Class<T> destinationClass, String indexName, Integer shards, Integer replicas, Integer maxResult, String... withAliases) 
        if (ObjectUtil.isNull(destinationClass)) throw new RuntimeException("参数类不能为空");
        if (StrUtil.isEmpty(indexName)) throw new RuntimeException("参数名不能为空");
        if (withAliases == null || withAliases.length <= 0) throw new RuntimeException("索引别名至少有一个");
        for (String withAlias : withAliases) 
            if (!withAlias.endsWith("_search")) 
                throw new RuntimeException("索引别名只能以‘_search’结尾");
            
        

        IndexCoordinates of = IndexCoordinates.of(indexName);
        IndexOperations indexOperations = elasticsearchRestTemplate.indexOps(of);
        boolean exists = indexOperations.exists();
        if (exists) 
            indexOperations.delete();
        
        Map<String, Object> settings = new HashMap<>();
        settings.put("index.number_of_shards", shards > 0 ? shards : 3);
        settings.put("index.number_of_replicas", replicas > 0 ? replicas : 2);
        settings.put("index.max_result_window", maxResult > 0 ? maxResult : Integer.MAX_VALUE);
        Document mapping = indexOperations.createMapping(destinationClass);
        indexOperations.create(settings, mapping);
        AliasAction.Add add = new AliasAction.Add(AliasActionParameters.builder()
                .withIndices(indexName).withAliases(withAliases).build());
        return indexOperations.alias(new AliasActions(add));
    

    /**
     * 根据索引名删除索引
     *
     * @param indexName 索引名称
     * @author mar
     * @date 2021/10/28 16:29
     */
    public static boolean deleteIndexByName(String indexName) 
        return elasticsearchRestTemplate.indexOps(IndexCoordinates.of(indexName)).delete();
    

    /**
     * 根据索引名判断索引是否存在
     *
     * @param indexName
     * @return boolean
     * @author mar
     * @date 2021/11/29 16:39
     */
    public static boolean isExists(String indexName) 
        IndexCoordinates of = IndexCoordinates.of(indexName);
        return elasticsearchRestTemplate.indexOps(of).exists();
    

    /**
     * 获取索引对应的别名
     */
    public static Set<String> getAlias(String index) 
        if (StrUtil.isEmpty(index)) throw new RuntimeException("索引名不能为空");
        Map<String, Set<AliasData>> aliasesForIndex = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(index)).getAliasesForIndex();
        Set<String> set = new HashSet<>();
        aliasesForIndex.forEach((key, value) -> 
            value.forEach(data -> set.add(data.getAlias()));
        );
        return set;
    

    /**
     * 为索引添加别名
     *
     * @param index 真实索引
     * @param alias 别名
     */
    public static boolean addAlias(String index, String... alias) 
        Preconditions.checkNotNull(index);
        Preconditions.checkNotNull(alias);
        final IndexOperations indexOps = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(index));
        AliasActions aliasActions = new AliasActions(new AliasAction.Add(
                AliasActionParameters.builder().withIndices(index).withAliases(alias).build()
        ));
        return indexOps.alias(aliasActions);
    

    /**
     * 为索引删除别名
     *
     * @param index 真实索引
     * @param alias 别名
     */
    public static boolean delAlias(String index, String... alias) 
        Preconditions.checkNotNull(index);
        Preconditions.checkNotNull(alias);
        final IndexOperations indexOps = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(index));
        AliasActions aliasActions = new AliasActions(new AliasAction.Remove(
                AliasActionParameters.builder().withIndices(index).withAliases(alias).build()
        ));
        return indexOps.alias(aliasActions);
    

    /**
     * 为索引更换别名 旧的换为新的 不会判断旧的是否存在
     *
     * @param index    真实索引
     * @param oldAlias 要删除的别名
     * @param newAlias 要新增的别名
     */
    public static boolean replaceAlias(String index, String oldAlias, String newAlias) 
        Preconditions.checkNotNull(index);
        Preconditions.checkNotNull(oldAlias);
        Preconditions.checkNotNull(newAlias);
        final IndexOperations indexOps = elasticsearchRestTemplate.indexOps(IndexCoordinates.of(index));
        final AliasAction.Add add = new AliasAction.Add(AliasActionParameters.builder().withIndices(index).withAliases(newAlias).build());
        final AliasAction.Remove remove = new AliasAction.Remove(AliasActionParameters.builder().withIndices(index).withAliases(oldAlias).build());
        AliasActions aliasActions = new AliasActions(add, remove);
        return indexOps.alias(aliasActions);
    

    /**
     * 单条数据插入
     *
     * @param t         待插入的数据实体
     * @param indexName 索引名
     * @return java.lang.String 返回文档id
     */
    public static <T> void saveByEntity(T t, String indexName) 
        //这里的操作就是指定文档id
        String id = getFieldId(t);
        IndexQuery build = new IndexQueryBuilder()
                .withId(id)
                .withObject(t).build();
        elasticsearchRestTemplate.index(build, IndexCoordinates.of(indexName));
    

    public static <T> void saveBatchByEntities(Map<String, List<T>> map) 
        if (map != null && map.size() > 0)
            map.forEach((key, value) -> saveBatchByEntities(value, key));
    

    /**
     * 批量插入
     *
     * @param sourceList 待插入的数据实体集合
     * @param indexName  索引名
     * @return java.util.List<java.lang.String> 返回idList
     */
    public static <T> void saveBatchByEntities(List<T> sourceList, String indexName) 
        List<IndexQuery> queryList = new ArrayList<>();
        for (T source : sourceList) 
            String id = getFieldId(source);
            IndexQuery build = new IndexQueryBuilder().withId(id).withObject(source).build();
            queryList.add(build);
        
        elasticsearchRestTemplate.bulkIndex(queryList, IndexCoordinates.of(indexName));
    

    /**
     * 单条数据更新
     *
     * @param entity    待更新的数据实体
     * @param indexName 索引名
     * @return void
     */
    public static <T> void updateByEntity(T entity, String indexName) 
        String id = getFieldId(entity);
        Map<String, String> map = null;
        try 
            map = BeanUtils.describe(entity);
         catch (Exception e) 
            e.printStackTrace();
        
        Document document = Document.from(map);
        document.setId(id);
        // 这里的UpdateQuery需要构造一个Document对象,但是Document对象不能用实体类转化而来
        //(可见Document的源码,位于:org.springframework.data.elasticsearch.core.document
        // 包下),因此上面才会BeanUtils.describe(entity),将实体类转化成一个map,由map转化
        // 为Document对象。
        //不加默认false。true表示更新时不存在就插入
        UpdateQuery build = UpdateQuery.builder(id)
                .withDocAsUpsert(false)
                .withDocument(document)
                .build();
        elasticsearchRestTemplate.update(build, IndexCoordinates.of(indexName));
    

    /**
     * 根据maps批量更新
     *
     * @param list      待更新的数据实体集合
     * @param indexName 索引名
     * @return void
     * @author Innocence
     */
    public static <T> void updateByMaps(List<T> list, String indexName) 
        List<Map<String, Object>> maps = listToMap(list);
        List<UpdateQuery> updateQueries = new ArrayList<>();
        maps.forEach(item -> 
            Document document = 以上是关于ES 聚合索引简介的主要内容,如果未能解决你的问题,请参考以下文章

elasticsearch系列六:聚合分析(聚合分析简介指标聚合桶聚合)

基础Elasticsearch 基础

好玩的ES--第四篇之聚合查询和集群

好玩的ES--第四篇之聚合查询和集群

好玩的ES--第四篇之聚合查询和集群

ElasticSearch01_简介安装es以及kibana详解倒排索引检索es基本信息增删改查文档