package org.com.cn;


import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.InnerHitBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.join.aggregations.Children;
import org.elasticsearch.join.aggregations.JoinAggregationBuilders;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.join.query.HasParentQueryBuilder;
import org.elasticsearch.join.query.JoinQueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBounds;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHits;
import org.elasticsearch.search.aggregations.metrics.tophits.TopHitsAggregationBuilder;

import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class AggDemo {
    public Object aggDemo() {
        SearchRequest searchRequest = new SearchRequest("表明");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //精确查询
        boolQueryBuilder.must(QueryBuilders.termQuery("字段名", "要查询的字段值"));
        //模糊查询 看下面嵌套查询
        // must 相当于 and  should  相当于 or
        //如果 两个字段为shuoul 和另一个字段为must  (A or B ) and C
        BoolQueryBuilder boolQueryBuilderShould = QueryBuilders.boolQuery();
        BoolQueryBuilder boolQueryBuilderShouldMust = QueryBuilders.boolQuery();
        boolQueryBuilderShould.should(QueryBuilders.termQuery("字段名称A","字段值"));
        boolQueryBuilderShould.should(QueryBuilders.termQuery("字段名称B","字段值"));
        //这里 可能有人迷惑 为什么 外层还要加一个must 因为 如果不加这个must should 和must 就是并列关系 没有中间的 括号 当两个should 都不满足的时候就会只返回must符合条件的
        //但是往往我们需要的时候 should 里面必须 有且至少有一个 符合条件的跟must的条件and 所以 这里外层加一个must
        boolQueryBuilderShouldMust.must(boolQueryBuilderShould);
        boolQueryBuilder.must(boolQueryBuilderShouldMust);
        //如果不加must 可以使用minimumShouldMatch 字面意思是至少 匹配一个 但是大神说这样的效率没有上面的高 如果压测遇到问题不妨改成上面的方式
        boolQueryBuilderShould.minimumShouldMatch(1);
        boolQueryBuilder.must(boolQueryBuilderShould);
        //子文档聚合
        JoinAggregationBuilders.children("a","child-index");
        //字段聚合
        TermsAggregationBuilder size = AggregationBuilders.terms("聚合的别名 在取值的 get 这个别名取出聚合的内容").field("要聚合的字段").size(100);
        TermsAggregationBuilder field = AggregationBuilders.terms("聚合的别名 在取值的 get 这个别名取出聚合的内容").field("要聚合的字段");
        //聚合后返回要的字段 类似分组求 top N   或者去重 都可以 用这个
        /*
         * topHits 聚合名称
         * fetchSource 返回的字段
         * sort 排序
         * */
        TopHitsAggregationBuilder sort = AggregationBuilders.topHits("A").fetchSource(new String[]{""}, null)
                //聚合后按照某个字段排序
                .sort("s", SortOrder.DESC);
        //顺序自己定义好 别错了
        size.subAggregation(sort);
        field.subAggregation(size);
        //时间聚合
        DateHistogramAggregationBuilder date = AggregationBuilders.dateHistogram("B").field("C")
                //按小时聚合年 天 等等
                .dateHistogramInterval(DateHistogramInterval.HOUR)
                //每2小时聚合一桶
                .interval(2l)
                /*
                 * 以上的min_doc_count参数会强制返回空桶,extended_bounds参数会强制返回一整年的数据。
                 * 这两个参数会强制返回该年中的所有月份,无论它们的文档数量是多少。min_doc_count的意思很容易懂:它强制返回哪怕为空的桶。
                 * extended_bounds参数需要一些解释。min_doc_count会强制返回空桶,但是默认ES只会返回在你的数据中的最小值和最大值之间的桶。
                 * 因此如果你的数据分布在四月到七月,你得到的桶只会表示四月到七月中的几个月(可能为空,如果使用了min_doc_count=0)。为了得到一整年的桶,我们需要告诉ES需要得到的桶的范围。
                 *  extended_bounds参数就是用来告诉ES这一范围的。一旦你添加了这两个设置,得到的响应就很容易被图形生成库处理而最终得到下图:
                 * */
                .minDocCount(0)
                //强制返回一整年的数据
                .extendedBounds(new ExtendedBounds("2021-01-01", "2021-12-31"));
        //第一个先聚合 之后在聚合第二个 一次类推 这里是做 演示 可以根据 需要 来做 但是做聚合操作 如果 数据类型不是keyword 一定要在参数名称后面加上.keyword text类型 需要 其他基础类型不需要添加
        date.subAggregation(field);
        //嵌套 查询
        BoolQueryBuilder boolQueryBuilderNest = QueryBuilders.boolQuery();
        //模糊查询  *aaa 前模糊查询 aa* 后模糊查询  ?1* 第二位为1 匹配 ?占位符号
        boolQueryBuilderNest.must(QueryBuilders.wildcardQuery("嵌套的路径(也是嵌套的名称).字段名称", "*"));
        NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("嵌套的路径(也是嵌套的名称)", boolQueryBuilderNest, ScoreMode.None);
        //嵌套聚合查询
        NestedAggregationBuilder nested = AggregationBuilders.nested("nested", "nestedIndex");
        TermsAggregationBuilder nestKey = AggregationBuilders.terms("nestKey").field("nestedIndex.字段名称");
        nested.subAggregation(nestKey);
        //父子当查询
        //子查父  父文档 字段 当作跟属性 直接查询  子文档属性要添加到boolQueryBuilderChild 中来写
        BoolQueryBuilder boolQueryBuilderParent = QueryBuilders.boolQuery();
        BoolQueryBuilder boolQueryBuilderOne = boolQueryBuilderParent.must(QueryBuilders.termQuery("父文档字段名", ""));
        BoolQueryBuilder boolQueryBuilderChild = QueryBuilders.boolQuery();
        HasChildQueryBuilder hasChildQueryBuilder = JoinQueryBuilders.hasChildQuery("子文档名称",boolQueryBuilderChild,ScoreMode.None);
        hasChildQueryBuilder.innerHit(new InnerHitBuilder());
        boolQueryBuilderOne.must(hasChildQueryBuilder);
        //子查父 亦是
        HasParentQueryBuilder hasParentQueryBuilder = JoinQueryBuilders.hasParentQuery("父文档", QueryBuilders.boolQuery().must(QueryBuilders.termQuery("父文档字段名", "")), false);
        //如果要返回其中的本文档属性的数据 需要添加                                                            //排序 升序还是降序    //返回的数量
        hasParentQueryBuilder.innerHit(new InnerHitBuilder().addSort(SortBuilders.fieldSort("排序的字段").order(SortOrder.DESC)).setSize(100));
       /*
       * 这里说下 父文档和子文档 是相互独立的文档 当对跟属性进行排序的时候影响不到父文档 或者子文档 对子文档或者父文档排序也不会影响到跟属性  如果 需要排序建议将排序的字段提出来做成一个表的数据进行排序
       * */

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //这里 需要将 上面拼接好的dsl添加到这里
        searchSourceBuilder.query(boolQueryBuilder);
        //分页 设置 es 默认 最多返回1W 条数据所以 在 需求的时候要注意 如果两个es 表相互查询可能造成数据丢失
        searchSourceBuilder.from(1).size(100);
        // 这里排序只能针对跟属性  要注意 嵌套和父子档排序这里不起作用
        searchSourceBuilder.sort("排序的字段名称",SortOrder.DESC);
        //这里注意 如果 对象类型需要 返回需要 表明.字段名 数据全要可以直接写表明, 如果是嵌套类型也可以写表明 但是 只取其中的某些字段需要在hit里面解析过滤过滤。
        searchSourceBuilder.fetchSource(new String[]{"字段名称","字段名称"},null);
        //这里还有深度分页这里以后在说
        SearchRequest source = searchRequest.source(searchSourceBuilder);
        SearchResponse search = TemClientDemo.getSearch(source);
        //这个符合文档的总条数

        long totalHits = search.getHits().totalHits;
        //注意前面的数据类型 不要转化错了
        //普通聚合
        Terms aggTerms = search.getAggregations().get("当时设置的别名");

        for (Terms.Bucket bucket : aggTerms.getBuckets()) {
            Number keyAsNumber = bucket.getKeyAsNumber();
        }
        //聚合后要返回字段的
        TopHits aggTopHits = search.getAggregations().get("当时设置的别名");
        for (SearchHit hit : aggTopHits.getHits()) {
            //聚合分组后设置 返回的字段
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();

        }
        //子聚合
        Children childe =    search.getAggregations().get("当时设置的别名");
        //按照时间聚合的
      
        Histogram aggHistogram = search.getAggregations().get("当时设置的别名");
        List<? extends Histogram.Bucket> buckets = aggHistogram.getBuckets();
        for (Histogram.Bucket bucket : buckets) {

            long docCount = bucket.getDocCount();
        }

        for (SearchHit hit : search.getHits().getHits()) {
            //这个返回的文档id
            String id = hit.getId();
            //这个是跟属性的 返回的字段和值
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String value = (String) sourceAsMap.getOrDefault("要取的跟属性的值", "");
            SearchHits inner = hit.getInnerHits().get("父子档名称//嵌套名称");
            for (SearchHit innerHit : inner.getHits()) {
                Map<String, Object> sourceAsMap1 = innerHit.getSourceAsMap();
            }

        }

        return new ArrayList<>();
    }
}