spring整合es后的查询(普通查询与聚合查询)

内容简介


1.spring整合es
2.普通查询的使用方法
3.聚合查询的使用方法
4.普通查询与聚合查询的使用区别

spring整合es


1.service层的接口

public interface EsClient {

    public TransportClient getClient();


    public QueryBuilder getQueryCondition(final String name, final String value);
}

2.serviceimpl层的实现类

@Service("esClient")
public class EsClientImpl implements EsClient{

    @Value("${es.clustername}")
    private String clusterName;
    @Value("${es.port}")
    private String port;
    @Value("${es.hosts}")
    private String hosts;


    private TransportClient transportClient = null;

    /**
     * 条件分割器,多条件使用逗号分割
     *
     * @param name  属性名
     * @param value 查询值
     * @return
     */
    public QueryBuilder getQueryCondition(final String name, final String value) {
        if (value.contains(",")) {
            final BoolQueryBuilder boolQuery = QueryBuilders.boolQuery().minimumShouldMatch(1);
            for (final String v : value.split(",")) {
                boolQuery.should(QueryBuilders.termQuery(name, v));
            }
            return boolQuery;
        } else {
            return QueryBuilders.termQuery(name, value);
        }
    }


    @PostConstruct
    public void init() throws Exception {
        connectEs();
    }

    @PreDestroy
    public void destory() {
        disConnectEs();
    }

    private void disConnectEs() {
        if (null != transportClient) {
            transportClient.close();
        }
    }

    private void connectEs() throws Exception {
        Settings settings = Settings.builder().put("cluster.name", clusterName).build();
        transportClient = new PreBuiltTransportClient(settings);
        if (StringUtils.isNotBlank(hosts)) {
            String[] hostArray = hosts.split(",");
            for (String host : hostArray) {
                InetSocketTransportAddress ist = new InetSocketTransportAddress(InetAddress.getByName(host), Integer.parseInt(port));
                transportClient.addTransportAddress(ist);
            }
        }
    }

    public TransportClient getClient() {
        return this.transportClient;
    }
}

3.属性注入(多种属性文件,这里只是一种pom文件存放属性的方式)

<!--es-->
                <sink.es.clustername>dolphin</sink.es.clustername>
                <sink.es.port>9300</sink.es.port>
                <sink.es.hosts>172.20.78.56,172.20.78.57,172.20.78.58</sink.es.hosts>
                <sink.es.index>sinkreport</sink.es.index>
                <sink.es.type>mg_sink_user_d</sink.es.type>

普通查询


1.关于es模块提供的查询

public SearchResponse queryFlowInspireActiveChart(CommonQueryParam commonQueryParam) {
        
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (StringUtils.isNotEmpty(commonQueryParam.getProvince())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("province", commonQueryParam.getProvince()));
        }

        if (StringUtils.isNotEmpty(commonQueryParam.getCity())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("city", commonQueryParam.getCity()));
        }
        
        boolQueryBuilder.must(QueryBuilders.termQuery("content_id", "c0001"));
        
        if (StringUtils.isNotEmpty(commonQueryParam.getStart()) && StringUtils.isNotEmpty(commonQueryParam.getEnd())) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("dayid");
            rangeQuery.from(commonQueryParam.getStart());
            rangeQuery.to(commonQueryParam.getEnd());
            boolQueryBuilder.must(rangeQuery);
        }
        
        SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(index)
                .setTypes(typeName).setSize(10000)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(boolQueryBuilder)
                .addSort("dayid",SortOrder.ASC);//普通查询按照dayid升序排列
        
         //打印es语句
        LOGGER.debug(searchRequestBuilder.toString());
        SearchResponse response = searchRequestBuilder.execute().actionGet();
        return response;
    }

2.业务层得到es层返回的数据进行业务开发

SearchHits hits = response.getHits();
Iterator<SearchHit> it = hits.iterator();
while(it.hasNext()){
            Map<String,Object> map = it.next().getSource();
//日期
       String dayid = map.get("dayid").toString())
//日新增手机用户数
       String userNewActive = map.get("user_new_active").toString());
//日存量手机用户数 = 日活跃手机用户数减去日新增手机用户数
int resultNumber = Integer.valueOf(map.get("user_active").toString()) - Integer.valueOf(map.get("user_new_active").toString());
        }

3.小总结
普通查询很简单,对于普通查询的各种条件,比如限定日期,限定省市,直接在boolQueryBuilder作用must即可。
对于对数据进行限定size分页,直接跟在esClient.getClient().prepareSearch(index)
.setTypes(typeName).setSize(10000)即可,这里就是设置返回最大1000条数据。

聚合查询


es的聚合是聚合,普通查询是普通查询,取数据的方式不同,所以不能两个同时使用,所以对数据进行限定的时候,应该放到聚合语句里面限定,关闭掉普通的size。
1.es聚合查询

public SearchResponse queryFlowInspireTopTenDataByProvince(
            CommonQueryParam commonQueryParam) {
        
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        
        if (StringUtils.isNotEmpty(commonQueryParam.getProvince())) {
            boolQueryBuilder.must(QueryBuilders.termQuery("province", commonQueryParam.getProvince()));
        }
        //省数据内容 排除city=0001
        if (StringUtils.isNotEmpty(commonQueryParam.getCity())) {
            if("0001".equals(commonQueryParam.getCity())){
                boolQueryBuilder.mustNot(QueryBuilders.termQuery("city", "0001"));
            }
        }
        
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("content_id", "c0001"));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("content_name", "ignore"));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("level1_name", "ignore"));
        
        //排除掉-2异常数据
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("flow", "-2"));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("flow_mobile", "-2"));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("flow_wifi", "-2"));
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("avg_flow_mobile", "-2"));
        
        if (StringUtils.isNotEmpty(commonQueryParam.getStart()) && StringUtils.isNotEmpty(commonQueryParam.getEnd())) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("dayid");
            rangeQuery.from(commonQueryParam.getEnd());
            rangeQuery.to(commonQueryParam.getEnd());
            boolQueryBuilder.must(rangeQuery);
        }
        
        SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(index)
                .setTypes(typeName).setSize(0)
                .setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
                .setQuery(boolQueryBuilder);
//order对别名为contentName的桶数据按照求和后的flow进行降序排序
//如果不对求和数据进行排序,仅针对桶排序,即仅对contentName排序,很简单:将order的参数替换为Terms.Order.asc即可
        searchRequestBuilder.addAggregation(AggregationBuilders.terms("contentName").field("content_name").size(2147483647).order(Terms.Order.aggregation("flow", false))
                                .subAggregation(AggregationBuilders.sum("flow").field("flow"))
                                .subAggregation(AggregationBuilders.sum("flowMobile").field("flow_mobile"))
                                .subAggregation(AggregationBuilders.sum("flowWifi").field("flow_wifi"))
                                .subAggregation(AggregationBuilders.sum("avgFlowMobile").field("avg_flow_mobile"))
        );
         LOGGER.debug(searchRequestBuilder.toString());
         SearchResponse response = searchRequestBuilder.execute().actionGet();
         return response;
    }

以上因为使用了聚合,所以关闭的普通查询的数据size,将其设置为0,相反的,打开聚合需要的数据size。
注意:一定要看清楚aggregation的层级,在了解到es桶特性之后,其实想要排序只能对桶进行排序,一般情况下是对单个桶里面的数据进行排序,多个桶也能排序,只需要将求和,平均这些函数方法放到最后一个桶即可,但是es支持不友好很有可能多个桶返回的数据并没有按照预想的排序。
2.业务层获取es数据进行业务开发

Aggregations firstAggs = response.getAggregations();
        if (null == firstAggs) {
            return null;
        }

        Terms contentNameTerms = firstAggs.get("contentName");
        for (Terms.Bucket contentNameBucket : contentNameTerms.getBuckets()) {
            number++;
            if(number >= 11){
                break;
            }
            String contentName = contentNameBucket.getKeyAsString();
                    
                    TableData td = new TableData();
                    td.setRank(String.valueOf(number));
                    td.setName(contentName);
                    td.setKind(this.queryLevel1NameByContentName(commonQueryParam,contentName));
                    Sum flow = contentNameBucket.getAggregations().get("flow");
                    td.setTotalFlow(String.format("%.0f", flow.getValue()));
                    Sum flowMobile = contentNameBucket.getAggregations().get("flowMobile");
                    td.setMobileFlow(String.format("%.0f", flowMobile.getValue()));
                    Sum flowWifi = contentNameBucket.getAggregations().get("flowWifi");
                    td.setWifiFlow(String.format("%.0f", flowWifi.getValue()));
                    Sum avgFlowMobile = contentNameBucket.getAggregations().get("avgFlowMobile");
                    td.setAvgFlow(String.format("%.2f", avgFlowMobile.getValue()*1024));
                    tableDataList.add(td);
                }
        return tableDataList;
    }

要清楚聚合返回数据的结构要去了解es桶的原理,简单来说就是,有多少tems就要for循环多少次,tems循环出来的就是桶(bucket),一个桶可以就是group by的数据,通过这个桶可以拿到group by的字段名称比如contentName,然后可以根据这个桶再去拿桶里面聚合好的数据比如contentNameBucket.getAggregations().get("flow");

普通查询与聚合查询的使用区别


1.普通查询在javaApi里面只能用普通查询的方式获取值
2.聚合查询在javaApi里面只能用聚合查询方式获取值
3.由于两个的不相关性,他们的size要分别设置,如果要求聚合,那么在普通查询的size设置将失去意义,应该设置普通查询size参数为0,而在聚合查询的size参数尽量设置大一些,比如.size(2147483647),聚合的size越大越能保证桶内聚合的时候,sum(value)的数据尽量准确。

补充一个多字段聚合的例子说明多字段的层级关系

注意多少个group by就需要多少个terms,最后一个terms里面才放sum等函数聚合,而不是sum聚合函数放到terms的任意层级!

//设置用户运营指标聚合
    private void setMemberServiceAgg(SearchRequestBuilder searchRequestBuilder) {
        searchRequestBuilder.addAggregation(AggregationBuilders.terms("dayid").field("dayid").size(2147483647)
                .subAggregation(AggregationBuilders.terms("provinceName").field("province_name").size(2147483647)
                        .subAggregation(AggregationBuilders.terms("cityName").field("city_name").size(2147483647).order(Terms.Order.term(true))
                                .subAggregation(AggregationBuilders.sum("sumNewPayMember").field("new_pay_member"))
                                .subAggregation(AggregationBuilders.sum("sumCancelMember").field("cancel_member"))
                                .subAggregation(AggregationBuilders.sum("sumMembers").field("members"))
                                .subAggregation(AggregationBuilders.sum("sumMemberActive").field("member_active"))
                        )
                )
        );
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342