001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bdelbosc 018 */ 019package org.nuxeo.elasticsearch.aggregate; 020 021import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_EXTENDED_BOUND_MAX_PROP; 022import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_EXTENDED_BOUND_MIN_PROP; 023import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_INTERVAL_PROP; 024import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_MIN_DOC_COUNT_PROP; 025import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_COUNT_ASC; 026import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_COUNT_DESC; 027import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_KEY_ASC; 028import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_KEY_DESC; 029import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_PROP; 030 031import java.util.ArrayList; 032import java.util.Collection; 033import java.util.List; 034import java.util.Map; 035 036import org.codehaus.jackson.annotate.JsonIgnore; 037import org.elasticsearch.index.query.FilterBuilders; 038import org.elasticsearch.index.query.OrFilterBuilder; 039import org.elasticsearch.index.query.RangeFilterBuilder; 040import org.elasticsearch.search.aggregations.AggregationBuilders; 041import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; 042import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; 043import org.elasticsearch.search.aggregations.bucket.histogram.HistogramBuilder; 044import org.nuxeo.ecm.core.api.DocumentModel; 045import org.nuxeo.ecm.platform.query.api.AggregateDefinition; 046import org.nuxeo.ecm.platform.query.core.BucketRange; 047 048/** 049 * @since 6.0 050 */ 051public class HistogramAggregate extends AggregateEsBase<BucketRange> { 052 053 private Integer interval; 054 055 public HistogramAggregate(AggregateDefinition definition, DocumentModel searchDocument) { 056 super(definition, searchDocument); 057 } 058 059 @JsonIgnore 060 @Override 061 public HistogramBuilder getEsAggregate() { 062 HistogramBuilder ret = AggregationBuilders.histogram(getId()).field(getField()); 063 Map<String, String> props = getProperties(); 064 ret.interval(getInterval()); 065 if (props.containsKey(AGG_MIN_DOC_COUNT_PROP)) { 066 ret.minDocCount(Long.parseLong(props.get(AGG_MIN_DOC_COUNT_PROP))); 067 } 068 if (props.containsKey(AGG_ORDER_PROP)) { 069 switch (props.get(AGG_ORDER_PROP).toLowerCase()) { 070 case AGG_ORDER_COUNT_DESC: 071 ret.order(Histogram.Order.COUNT_DESC); 072 break; 073 case AGG_ORDER_COUNT_ASC: 074 ret.order(Histogram.Order.COUNT_ASC); 075 break; 076 case AGG_ORDER_KEY_DESC: 077 ret.order(Histogram.Order.KEY_DESC); 078 break; 079 case AGG_ORDER_KEY_ASC: 080 ret.order(Histogram.Order.KEY_ASC); 081 break; 082 default: 083 throw new IllegalArgumentException("Invalid order: " + props.get(AGG_ORDER_PROP)); 084 } 085 } 086 if (props.containsKey(AGG_EXTENDED_BOUND_MAX_PROP) && props.containsKey(AGG_EXTENDED_BOUND_MIN_PROP)) { 087 ret.extendedBounds(Long.parseLong(props.get(AGG_EXTENDED_BOUND_MIN_PROP)), 088 Long.parseLong(props.get(AGG_EXTENDED_BOUND_MAX_PROP))); 089 } 090 return ret; 091 } 092 093 @JsonIgnore 094 @Override 095 public OrFilterBuilder getEsFilter() { 096 if (getSelection().isEmpty()) { 097 return null; 098 } 099 OrFilterBuilder ret = FilterBuilders.orFilter(); 100 for (String sel : getSelection()) { 101 RangeFilterBuilder rangeFilter = FilterBuilders.rangeFilter(getField()); 102 long from = Long.parseLong(sel); 103 long to = from + getInterval(); 104 rangeFilter.gte(from).lt(to); 105 ret.add(rangeFilter); 106 } 107 return ret; 108 } 109 110 @JsonIgnore 111 @Override 112 public void parseEsBuckets(Collection<? extends MultiBucketsAggregation.Bucket> buckets) { 113 List<BucketRange> nxBuckets = new ArrayList<>(buckets.size()); 114 for (MultiBucketsAggregation.Bucket bucket : buckets) { 115 Histogram.Bucket histoBucket = (Histogram.Bucket) bucket; 116 nxBuckets.add(new BucketRange(bucket.getKey(), histoBucket.getKeyAsNumber(), 117 histoBucket.getKeyAsNumber().intValue() + getInterval(), histoBucket.getDocCount())); 118 } 119 this.buckets = nxBuckets; 120 } 121 122 public int getInterval() { 123 if (interval == null) { 124 Map<String, String> props = getProperties(); 125 if (props.containsKey(AGG_INTERVAL_PROP)) { 126 interval = Integer.parseInt(props.get(AGG_INTERVAL_PROP)); 127 } else { 128 throw new IllegalArgumentException("interval property must be defined for " + toString()); 129 } 130 } 131 return interval; 132 } 133}