
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 extends Bucket> 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 extends Bucket> 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 extends Bucket> 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 extends Bucket> 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
© 2015 - 2025 Weber Informatics LLC | Privacy Policy