All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.onetwo.ext.es.SimpleSearchQueryBuilder Maven / Gradle / Ivy

package org.onetwo.ext.es;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.suggest.SuggestResponse;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
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.HasAggregations;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation.Bucket;
import org.elasticsearch.search.aggregations.bucket.nested.Nested;
import org.elasticsearch.search.aggregations.bucket.nested.NestedBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
import org.elasticsearch.search.aggregations.support.AggregationPath;
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.suggest.SuggestBuilder.SuggestionBuilder;
import org.jasig.cas.client.util.CommonUtils;
import org.onetwo.common.reflect.ReflectUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.elasticsearch.core.DefaultResultMapper;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.ResultsExtractor;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.util.Assert;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;



public class SimpleSearchQueryBuilder {
//	private static final Logger logger = LoggerFactory.getLogger(SimpleSearchQueryBuilder.class);

	public static SimpleSearchQueryBuilder newBuilder(){
		return new SimpleSearchQueryBuilder();
	}
	public static SimpleSearchQueryBuilder from(String index, String type){
		return new SimpleSearchQueryBuilder().addIndices(index).addTypes(type);
	}
	
	/***
	 * 
	 * @param pageNo from 0
	 * @param pageSize
	 * @return
	 */
	public static SimpleSearchQueryBuilder newBuilder(int pageNo, int pageSize){
		return new SimpleSearchQueryBuilder(pageNo, pageSize);
	}

	private NativeSearchQueryBuilder searchQueryBuilder;
	private QueryBuilder queryBuilder;
	private SimpleBooleanQueryBuilder booleanQuery;
//	private boolean built = false;
	private NativeSearchQuery nativeSearchQuery;
	private List sorts = Lists.newArrayList();
	

	private String[] includeSources;
	private String[] excludeSources;
	
	private List indices = Lists.newArrayList();
	private List types = Lists.newArrayList();
	
	private SimpleSearchQueryBuilder() {
		this(new NativeSearchQueryBuilder());
	}

	public SimpleSearchQueryBuilder(int pageNo, int pageSize) {
		this(new NativeSearchQueryBuilder());
		withPageable(pageNo, pageSize);
	}
	
	public SimpleSearchQueryBuilder addIndices(String... indeces){
		Collections.addAll(this.indices, indeces);
		return this;
	}
	
	public SimpleSearchQueryBuilder addTypes(String... types){
		Collections.addAll(this.types, types);
		return this;
	}
	
	final public SimpleSearchQueryBuilder withPageable(int pageNo, int pageSize){
		this.searchQueryBuilder.withPageable(new PageRequest(pageNo, pageSize));
		return this;
	}

	public SimpleSearchQueryBuilder(NativeSearchQueryBuilder searchQueryBuilder) {
		super();
		this.searchQueryBuilder = searchQueryBuilder;
	}

	public SimpleSearchQueryBuilder includeSources(String... includeSources) {
		this.includeSources = includeSources;
		return this;
	}

	public SimpleSearchQueryBuilder excludeSources(String... excludeSources) {
		this.excludeSources = excludeSources;
		return this;
	}

	public SimpleBooleanQueryBuilder bool(){
		if(booleanQuery==null){
			booleanQuery = new SimpleBooleanQueryBuilder<>(this);
		}
		return booleanQuery;
	}
	
	public SimpleSearchQueryBuilder boolTerm(String field, Object value){
		bool().mustTerm(field, value);
		return this;
	}

	public SimpleSearchQueryBuilder match(String text, String...fields){
		return matchAnalyzer(false, text, null, fields);
	}
	public SimpleSearchQueryBuilder matchAnalyzer(boolean useFilterIfQueryExists, String text, String analyzer, String...fields){
		if(StringUtils.isBlank(text) || fields==null || fields.length==0){
			return this;
		}
		if(queryBuilder!=null && useFilterIfQueryExists){
			bool().multiMustTerm(text, fields);
			return this;
		}
		
		if(fields.length==1){
			queryBuilder = matchQuery(fields[0], text).analyzer(analyzer);
		}else{
			queryBuilder = multiMatchQuery(text, fields).analyzer(analyzer);
		}
		return this;
	}

	
	public SimpleSearchQueryBuilder matchAll(){
		MatchAllQueryBuilder q = matchAllQuery();
		queryBuilder = q;
		return this;
	}
	
	public MatchAllQueryBuilder matchAllBuilder(){
		MatchAllQueryBuilder q = matchAllQuery();
		queryBuilder = q;
		return q;
	}
	
	public  T query(T queryBuilder){
		this.queryBuilder = queryBuilder;
		return queryBuilder;
	}
	
	public MatchQueryBuilder matchBuilder(String text, String...fields){
		MatchQueryBuilder q = matchQuery(text, fields);
		queryBuilder = q;
		return q;
	}
	
	public MultiMatchQueryBuilder multiMatchBuilder(String text, String...fields){
		MultiMatchQueryBuilder q = multiMatchQuery(text, fields);
		queryBuilder = q;
		return q;
	}

	public SimpleSearchQueryBuilder orderByAsc(String...fields){
		return order(Direction.ASC, fields);
	}
	public SimpleSearchQueryBuilder orderByDesc(String...fields){
		return order(Direction.DESC, fields);
	}
	public GeoDistanceSortExtBuilder geoDistanceSort(String fieldName){
		GeoDistanceSortExtBuilder geosort = new GeoDistanceSortExtBuilder(fieldName);
		withSortBuilder(geosort);
		return geosort;
	}
	
	public SimpleSearchQueryBuilder order(Direction direct, String...fields){
		if(ArrayUtils.isEmpty(fields))
			return this;
		Sort sort = new Sort(direct, fields);
		this.sorts.add(sort);
		return this;
	}
	
	public SimpleSearchQueryBuilder sort(Sort sort){
		if(sort==null)
			return this;
		this.sorts.add(sort);
		return this;
	}
	
	public SimpleSearchQueryBuilder withSortBuilder(SortBuilder sortBuilder){
		if(sortBuilder==null)
			return this;
		this.searchQueryBuilder.withSort(sortBuilder);
		return this;
	}

	public SimpleAggregationBuilder aggs(String name, String field) {
		return aggs(name, field, null);
	}

	public SimpleAggregationBuilder aggs(String name, String field, Integer size) {
		SimpleAggregationBuilder aggsBuilder = new SimpleAggregationBuilder<>(this, name, field, size);
		SimpleSearchQueryBuilder.this.searchQueryBuilder.addAggregation(aggsBuilder.aggsBuilder);
		return aggsBuilder;
	}
	public SimpleAggregationBuilder aggsNested(String name, String path) {
		NestedBuilder nested = AggregationBuilders.nested(name).path(path);
		SimpleAggregationBuilder aggsBuilder = new SimpleAggregationBuilder<>(this, nested);
		SimpleSearchQueryBuilder.this.searchQueryBuilder.addAggregation(aggsBuilder.aggsBuilder);
		return aggsBuilder;
	}
	
	public NativeSearchQuery build(boolean matchAllIfQueryNotExists){
		if(nativeSearchQuery!=null){
			return nativeSearchQuery;
		}
		
		if(queryBuilder!=null){
			searchQueryBuilder.withQuery(queryBuilder);
		}else if(matchAllIfQueryNotExists){
			searchQueryBuilder.withQuery(matchAllQuery());
		}
		if(booleanQuery!=null && booleanQuery.hasClauses()){
			booleanQuery.build();
			searchQueryBuilder.withFilter(booleanQuery.boolQuery);
		}
		this.nativeSearchQuery = searchQueryBuilder.build();
		this.nativeSearchQuery.addIndices(this.indices.toArray(new String[0]));
		this.nativeSearchQuery.addTypes(this.types.toArray(new String[0]));
		this.nativeSearchQuery.addSourceFilter(new FetchSourceFilter(includeSources, excludeSources));
		this.sorts.forEach(s->this.nativeSearchQuery.addSort(s));
//		built = true;
		return nativeSearchQuery;
	}
	
	public NativeSearchQuery getNativeSearchQuery() {
		return nativeSearchQuery;
	}

	public  Page queryForPage(ElasticsearchTemplate elasticsearchTemplate, Class clazz){
		NativeSearchQuery searchQuery = build(true);
		Page page = elasticsearchTemplate.queryForPage(searchQuery, clazz);
		return page;
	}
	
	public Aggregations queryAggs(ElasticsearchTemplate elasticsearchTemplate){
		return doQuery(elasticsearchTemplate, response->{
			/*Terms agg = response.getAggregations().get("hots");
			List buckets = agg.getBuckets();
			Object key = buckets.get(0).getKey();
			Object doc = buckets.get(0).getDocCount();*/
			return response.getAggregations();
		});
	}
	
	public QueryResult doQueryResult(ElasticsearchTemplate elasticsearchTemplate){
		return doQuery(elasticsearchTemplate, response->{
			return new QueryResult(elasticsearchTemplate, this, response);
		});
	}
	
	public  R doQuery(Function func){
		NativeSearchQuery searchQuery = build(true);
		return func.apply(searchQuery);
	}
	
	public  R doQuery(ElasticsearchTemplate elasticsearchTemplate, ResultsExtractor resultsExtractor){
		NativeSearchQuery searchQuery = build(true);
		return elasticsearchTemplate.query(searchQuery, resultsExtractor);
	}
	
	public SuggestResponse querySuggest(ElasticsearchTemplate elasticsearchTemplate, SuggestionBuilder suggestion){
		SuggestResponse reponse = elasticsearchTemplate.suggest(suggestion, this.indices.toArray(new String[0]));
		return reponse;
	}

	static public class QueryResult {
		final private SimpleSearchQueryBuilder queryBuilder;
		final private SearchResponse searchResponse;
		private AggregationsResult aggregationsResult;
		private final SearchResultMapper mapper;
		public QueryResult(ElasticsearchTemplate elasticsearchTemplate, SimpleSearchQueryBuilder queryBuilder, SearchResponse response) {
			super();
			this.queryBuilder = queryBuilder;
			this.searchResponse = response;
			this.aggregationsResult = new AggregationsResult(this.searchResponse.getAggregations());
			this.mapper = new DefaultResultMapper(elasticsearchTemplate.getElasticsearchConverter().getMappingContext());
		}
		
		public  Page getPage(Class clazz){
			return mapper.mapResults(searchResponse, clazz, queryBuilder.getNativeSearchQuery().getPageable());
		}
		
		public AggregationsResult getAggregationsResult(){
			return aggregationsResult;
		}

	}

    @SuppressWarnings("unchecked")
    static public class AggregationsResult {

	    static public List getBuckets(Aggregation agg) {
	    	if(agg instanceof MultiBucketsAggregation){
	    		return ((MultiBucketsAggregation)agg).getBuckets();
	    	}else{
	    		return ImmutableList.of();
	    	}
	    }
	    
    	private Aggregations aggregations;

		public AggregationsResult(Aggregations aggregations) {
			super();
			this.aggregations = aggregations;
		}

	    public  T getAggregation(String name) {
	    	this.checkAggs();
	        return aggregations.get(name);
	    }

	    public Terms getTerms(String name) {
	        return (Terms)getAggregation(name);
	    }

		public  T getAggregationByPath(String path) {
	    	this.checkAggs();
	    	List paths = AggregationPath.parse(path).getPathElementsAsStringList();
	    	T agg = aggregations.get(paths.get(0));
	    	for (int i = 1; i < paths.size(); i++) {
	    		String attr = paths.get(i);
	    		if(agg instanceof HasAggregations){
	    			HasAggregations hasagg = (HasAggregations) agg;
	    			agg = hasagg.getAggregations().get(attr);
	    		}else if(agg instanceof MultiBucketsAggregation){
	    			MultiBucketsAggregation magg = (MultiBucketsAggregation) agg;
	    			if(magg.getBuckets().isEmpty()){
	    				return agg;
	    			}
	    			Bucket bucket = magg.getBuckets().get(0);
	    			agg = bucket.getAggregations().get(attr);
	    		}else{
	    			break;
	    		}
			}
	    	return agg;
	    }

	    public List getBucketsByPath(String path) {
	    	this.checkAggs();
	    	Aggregation val = getAggregationByPath(path);
	    	return getBuckets(val);
	    }
	    
	    public  BucketMappingObjectBuilder createBucketsMapping(String key, Class targetClass, String keyField) {
	    	this.checkAggs();
//	    	Aggregation agg = getAggregationByPath(key);
	    	return new BucketMappingObjectBuilder<>(this, this, key, targetClass, keyField);
	    }
	    
	    /****
	     * 
	     * @param path
	     * @return 返回的数组,里面的值可能是数组,深度对应路径深度
	     */
	    public Object[] getRawKeysByPath(String path) {
	    	this.checkAggs();
	    	return (Object[])aggregations.getProperty(path+"._key");
	    }
	    public Object[] getKeysByPath(String path) {
	    	this.checkAggs();
		    Object[] keys = getRawKeysByPath(path);
		    if(keys==null || keys.length==0){
			    return null;
		    }else{
		        int size = AggregationPath.parse(path).getPathElementsAsStringList().size();
		        for (int i = 0; i < size; i++) {
		    	    if(i!=size-1){
		    		    keys = (Object[])keys[0];
		    	    }
		        }
		        return keys;
		    }
	    }

	    /****
	     * 
	     * @param path
	     * @return 
	     */
	    public  T getKeyByPath(String path) {
	    	this.checkAggs();
		    Object[] keys = getKeysByPath(path);
		    if(keys==null || keys.length==0){
			    return null;
		    }else{
		        return (T)keys[0];
		    }
	    }

	    public Nested getNestedAggregation(String name) {
	    	this.checkAggs();
	    	Nested nested = aggregations.get(name);
	    	return nested;
	    }

	    private void checkAggs() {
	    	if(aggregations==null){
	    		throw new RuntimeException("aggs not found!");
	    	}
	    }
    }
    
    public static class BucketMappingObjectBuilder {
    	private BucketMapping mapping;
//    	private List buckets;
    	private AggregationsResult aggResult;
    	private P parent;
    	private String key;
//    	private final Aggregation aggregation;
    	
    	public BucketMappingObjectBuilder(P parent, AggregationsResult aggResult, String key, Class targetClass, String keyField) {
			super();
			this.parent = parent;
			this.key = key;
			this.mapping = new BucketMapping(targetClass, keyField);
			this.aggResult = aggResult;
//			this.aggregation = aggregation;
//			this.buckets = ar.getBucketsByPath(key);
		}

    	public P end(){
    		return this.parent;
    	}
    	/***
    	 * @param path
    	 * @param property
    	 * @return
    	 */
		public BucketMappingObjectBuilder mapKey(String path, String property){
    		this.mapping.map(path, property);
    		return this;
    	}
		
		public  BucketMappingObjectBuilder> mapBuckets(String key, String property, Class targetClass, String keyField){
			BucketMappingObjectBuilder> builder = new BucketMappingObjectBuilder<>(this, null, key, targetClass, keyField);
			this.mapping.map(property, builder);
    		return builder;
    	}

		public List buildTargetList(){
			return this.buildTargetList(aggResult);
		}

		public List buildTargetList(AggregationsResult aggResult){
			Assert.notNull(aggResult);
			List buckets = aggResult.getBucketsByPath(key);
			if(buckets==null)
				return Collections.emptyList();
			return buckets.stream().map(b->mapping.buildTarget(b)).collect(Collectors.toList());
		}
    }
    
    public static class BucketMapping {
//    	private String key;
    	private Class targetClass;
    	private String keyProperty;
    	private Map fieldMappings = Maps.newHashMap();
    	
    	public BucketMapping(Class targetClass, String keyField) {
			super();
//			this.key = key;
			this.targetClass = targetClass;
			this.keyProperty = keyField;
		}

		public BucketMapping map(String path, Object property){
    		this.fieldMappings.put(path, property);
    		return this;
    	}

		public T buildTarget(Bucket bucket){
			T obj = ReflectUtils.newInstance(targetClass);
			BeanWrapper bw = newBeanWrapper(obj);
			bw.setPropertyValue(keyProperty, bucket.getKey());
			AggregationsResult ar = new AggregationsResult(bucket.getAggregations());
			this.fieldMappings.forEach((path, prop)->{
				if(prop instanceof BucketMappingObjectBuilder){
					BucketMappingObjectBuilder b = (BucketMappingObjectBuilder)prop;
					List values = b.buildTargetList(ar);
					bw.setPropertyValue(path, values);
				}else{
					/*Object[] value = ar.getKeysByPath(path);
					SimpleSearchQueryBuilder.logger.info("values:"+ArrayUtils.toString(value));*/
					Object val = ar.getKeyByPath(path);
					bw.setPropertyValue(prop.toString(), val);
				}
			});
			return obj;
		}

		public BeanWrapper newBeanWrapper(Object obj){
			BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(obj);
			bw.setAutoGrowNestedPaths(true);
			return bw;
		}
    }
	
	abstract public class ExtBaseQueryBuilder {
		protected PB parentBuilder;
		
		public ExtBaseQueryBuilder(PB parentBuilder) {
			super();
			this.parentBuilder = parentBuilder;
		}

		public PB end(){
			return parentBuilder;
		}
	}
	class GeoDistanceSortExtBuilder extends GeoDistanceSortBuilder {
		GeoDistanceSortExtBuilder(String fieldName){
			super(fieldName);
		}
		public SimpleSearchQueryBuilder end(){
			return SimpleSearchQueryBuilder.this;
		}
	}
	
	public class SimpleNestedQueryBuilder extends ExtBaseQueryBuilder {
		final private SimpleBooleanQueryBuilder> boolQuery = new SimpleBooleanQueryBuilder<>(this);
		final private String path;
		public SimpleNestedQueryBuilder(PB parentBuilder, String path) {
			super(parentBuilder);
			this.path = path;
		}
		public SimpleBooleanQueryBuilder> bool() {
			return boolQuery;
		}
		public String getPath() {
			return path;
		}

	}
	
	public class SimpleBooleanQueryBuilder extends ExtBaseQueryBuilder {
		private BoolQueryBuilder boolQuery = boolQuery();
		private List>> nestedQuerys = Lists.newArrayList();

		private Supplier conditionSupplier;
		
		private List>> orBoolQuerys = Lists.newArrayList();
		private List>> andBoolQuerys = Lists.newArrayList();
		private List>> notBoolQuerys = Lists.newArrayList();
		
		public SimpleBooleanQueryBuilder(PB parentBuilder) {
			super(parentBuilder);
		}

		public SimpleBooleanQueryBuilder withCondition(Supplier condition) {
			this.conditionSupplier = condition;
			return this;
		}

		public PB end(){
			andBoolQuerys.forEach(b->{
				boolQuery.must(b.boolQuery);
			});
			orBoolQuerys.forEach(b->{
				boolQuery.should(b.boolQuery);
			});
			notBoolQuerys.forEach(b->{
				boolQuery.mustNot(b.boolQuery);
			});
			return parentBuilder;
		}

		public SimpleBooleanQueryBuilder endCondition() {
			this.conditionSupplier = null;
			return this;
		}

		public boolean hasClauses() {	
			if(boolQuery.hasClauses()){
				return true;
			}
			return this.nestedQuerys.size()>0;
		}

		private void build(){
			this.nestedQuerys.stream()
								.filter(nested->nested.boolQuery.hasClauses())
								.forEach(nested->{
									nested.bool().build();
									must(QueryBuilders.nestedQuery(nested.getPath(), nested.bool().boolQuery));
								});
		}

		public SimpleBooleanQueryBuilder mustTerm(String field, Object value){
			Assert.hasText(field);
			if(!org.onetwo.common.utils.StringUtils.isNullOrBlankString(value))
				must(termQuery(field, value));
			return this;
		}

		public SimpleBooleanQueryBuilder> or(){
			SimpleBooleanQueryBuilder> simpleBooleanQuery = new SimpleBooleanQueryBuilder<>(this);
			orBoolQuerys.add(simpleBooleanQuery);
			return simpleBooleanQuery;
		}

		public SimpleBooleanQueryBuilder> and(){
			SimpleBooleanQueryBuilder> simpleBooleanQuery = new SimpleBooleanQueryBuilder<>(this);
			andBoolQuerys.add(simpleBooleanQuery);
			return simpleBooleanQuery;
		}

		public SimpleBooleanQueryBuilder> not(){
			SimpleBooleanQueryBuilder> simpleBooleanQuery = new SimpleBooleanQueryBuilder<>(this);
			notBoolQuerys.add(simpleBooleanQuery);
			return simpleBooleanQuery;
		}

		public SimpleNestedQueryBuilder> nested(String path){
			SimpleNestedQueryBuilder> nestedBuilder = new SimpleNestedQueryBuilder<>(this, path);
			nestedQuerys.add(nestedBuilder);
			return nestedBuilder;
		}

		/*public SimpleBooleanQueryBuilder mustNestedTerm(String path, String field, Object value){
			if(!CommonUtils.isNullOrBlankString(value)){
				TermQueryBuilder termQuery = QueryBuilders.termQuery(field, value);
				must(QueryBuilders.nestedQuery(path, termQuery));
			}
			return this;
		}
		
		public SimpleBooleanQueryBuilder mustNestedTerms(String path, String field, Object... values){
			Assert.hasText(field);
			if(values!=null && values.length>0){
				List listValue = Lists.newArrayList(values);
				listValue.removeIf(Objects::isNull);
				if(!listValue.isEmpty()){
					TermsQueryBuilder termQuery = QueryBuilders.termsQuery(field, listValue.toArray(new Object[0]));
					must(QueryBuilders.nestedQuery(path, termQuery));
				}
			}
			return this;
		}*/

		public SimpleBooleanQueryBuilder doTerms(String field, Consumer consumer, Object... values){
			Assert.hasText(field);
			if(values==null || values.length==0){
				return this;
			}
			List listValue = Lists.newArrayList(values);
			listValue.removeIf(Objects::isNull);
			if(listValue.isEmpty()){
				return this;
			}
			TermsQueryBuilder termQueryBuilder = null;
			if(listValue.get(0) instanceof Collection){
				Collection colValue = (Collection) listValue.get(0);
				if(!colValue.isEmpty()){
					termQueryBuilder = QueryBuilders.termsQuery(field, colValue);
				}
			}else{
				termQueryBuilder = QueryBuilders.termsQuery(field, listValue.toArray(new Object[0]));
			}
//					mustNot(termQueryBuilder);
			consumer.accept(termQueryBuilder);
			return this;
		}
		

		public SimpleBooleanQueryBuilder mustNotTerms(String field, Object... values){
			return doTerms(field, q->mustNot(q), values);
		}
		
		public SimpleBooleanQueryBuilder mustTerms(String field, Object... values){
			/*Assert.hasText(field);
			if(values!=null && values.length>0){
				List listValue = Lists.newArrayList(values);
				listValue.removeIf(Objects::isNull);
				if(!listValue.isEmpty()){
					must(QueryBuilders.termsQuery(field, listValue.toArray(new Object[0])));
				}
			}*/
			return doTerms(field, q->must(q), values);
		}
		
		public SimpleBooleanQueryBuilder mustTerms(String field, Collection values){
			Assert.hasText(field);
			if(values!=null && !values.isEmpty())
				must(QueryBuilders.termsQuery(field, values));
			return this;
		}
		
		public SimpleBooleanQueryBuilder mustTermOrMissing(String field, Object value){
			Assert.hasText(field);
			if(value==null){
				return mustMissing(field);
			}else{
				return mustTerm(field, value);
			}
		}

		/***
		 * or is not null
		 * @param field
		 * @return
		 */
		public SimpleBooleanQueryBuilder shouldExists(String field){
			Assert.hasText(field);
			should(QueryBuilders.existsQuery(field));
			return this;
		}
		/****
		 * and is null
		 * @param field
		 * @return
		 */
		public SimpleBooleanQueryBuilder mustMissing(String field){
			return mustNotExists(field);
		}
		public SimpleBooleanQueryBuilder mustIsNull(String field){
			return mustNotExists(field);
		}
		public SimpleBooleanQueryBuilder mustIsNotNull(String field){
			return mustExists(field);
		}
		
		/***
		 * and is null
		 * @param field
		 * @return
		 */
		public SimpleBooleanQueryBuilder mustNotExists(String field){
			Assert.hasText(field);
//			boolQuery.mustNot(QueryBuilders.existsQuery(field));
			mustNot(QueryBuilders.existsQuery(field));
			return this;
		}
		/***
		 * and is not null
		 * @param field
		 * @return
		 */
		public SimpleBooleanQueryBuilder mustExists(String field){
			Assert.hasText(field);
			must(QueryBuilders.existsQuery(field));
			return this;
		}
		
/*		public SimpleBooleanQueryBuilder shouldTerms(String field, Collection values){
			Assert.hasText(field);
			if(values!=null && !values.isEmpty()){
				should(QueryBuilders.termsQuery(field, values));
//				boolQuery.should(QueryBuilders.termsQuery(field, values));
			}
			return this;
		}
*/		
		public SimpleBooleanQueryBuilder shouldTerms(String field, Object... values){
			Assert.hasText(field);
			if(values!=null && values.length>0){
				if(values.length==1 && values[0] instanceof Collection){
					Collection col = (Collection)values[0];
					if(!col.isEmpty()){
						should(QueryBuilders.termsQuery(field, col));
					}
				}else{
					should(QueryBuilders.termsQuery(field, values));
				}
			}
			return this;
		}
		
		public SimpleBooleanQueryBuilder multiMustTerm(Object value, String... fields){
			if(ArrayUtils.isEmpty(fields))
				return this;
//			Stream.of(fields).forEach(f->mustTerm(f, value));
//			boolQuery.must(QueryBuilders.multiMatchQuery(value, fields));
			must(QueryBuilders.multiMatchQuery(value, fields));
			return this;
		}
		
		public SimpleBooleanQueryBuilder minShouldMatch(int minimumNumberShouldMatch){
			boolQuery.minimumNumberShouldMatch(minimumNumberShouldMatch);
			return this;
		}
		
		public RangeQueryBuilder rangeBuilder(String name){
			RangeQueryBuilder range = new RangeQueryBuilder(name);
			must(range);
			return range;
		}
		
		public  T must(T queryBuilder){
			Assert.notNull(queryBuilder);
//			boolQuery.must(queryBuilder);
			addToBoolQuery(()->boolQuery.must(queryBuilder));
			return queryBuilder;
		}
		
		public  T mustNot(T queryBuilder){
			Assert.notNull(queryBuilder);
//			boolQuery.mustNot(queryBuilder);
			addToBoolQuery(()->boolQuery.mustNot(queryBuilder));
			return queryBuilder;
		}
		
		public  T should(T queryBuilder){
			Assert.notNull(queryBuilder);
//			boolQuery.should(queryBuilder);
			addToBoolQuery(()->boolQuery.should(queryBuilder));
			return queryBuilder;
		}
		
		private  void addToBoolQuery(Runnable runnable){
			if(conditionSupplier==null || conditionSupplier.get().booleanValue()){
				runnable.run();
			}
		}

	}

	public class SimpleAggregationBuilder extends ExtBaseQueryBuilder {
		protected AggregationBuilder aggsBuilder;
		
		public SimpleAggregationBuilder(PB parentBuilder, AggregationBuilder aggTerms) {
			super(parentBuilder);
			this.aggsBuilder = aggTerms;
		}
		
		public SimpleSearchQueryBuilder endAllAggs(){
			return SimpleSearchQueryBuilder.this;
		}

		/***
		 * 
		"aggs": {
	        "hots": {
	            "terms": {
	                "field": "keyword"
	            }
	        }
		 * @param name
		 * @param field
		 * @return
		 */
		public SimpleAggregationBuilder(PB parentBuilder, String name, String field, Integer size) {
			super(parentBuilder);
			TermsBuilder terms = AggregationBuilders.terms(name);
			if(size!=null){
				terms.field(field).size(size);
			}else{
				terms.field(field);
			}
			terms.order(Terms.Order.count(false));//COUNT_DESC
			this.aggsBuilder = terms;
		}
		
		private TermsBuilder asTermsBuilder(){
			return (TermsBuilder) this.aggsBuilder;
		}
		
		public SimpleAggregationBuilder minDocCount(Long minDocCount){
			if(minDocCount!=null){
				this.asTermsBuilder().minDocCount(minDocCount);
			}
			return this;
		}

		/*public SimpleAggregationBuilder aggs(String name, String field) {
			return aggs(name, field, null);
		}

		public SimpleAggregationBuilder aggs(String name, String field, Integer size) {
			SimpleSearchQueryBuilder.this.aggs(name, field, size);
			return this;
		}

		public SimpleAggregationBuilder aggsNested(String name, String path) {
			SimpleSearchQueryBuilder.this.aggsNested(name, path);
			return this;
		}*/

		public SimpleAggregationBuilder subAggsNested(String name, String path) {
			NestedBuilder nested = AggregationBuilders.nested(name).path(path);
			SimpleAggregationBuilder subAggs = new SimpleAggregationBuilder<>(this.parentBuilder, nested);
			this.aggsBuilder.subAggregation(subAggs.aggsBuilder);
			return this;
		}

		public SimpleAggregationBuilder> subAggs(String name, String field) {
			return subAggs(name, field, null);
		}

		public SimpleAggregationBuilder> subAggs(String name, String field, Integer size) {
			SimpleAggregationBuilder> subAggs = new SimpleAggregationBuilder<>(this, name, field, size);
			this.aggsBuilder.subAggregation(subAggs.aggsBuilder);
			return subAggs;
		}
		
	}

}