001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * bdelbosc 016 */ 017package org.nuxeo.elasticsearch.aggregate; 018 019import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_EXTENDED_BOUND_MAX_PROP; 020import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_EXTENDED_BOUND_MIN_PROP; 021import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_INTERVAL_PROP; 022import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_MIN_DOC_COUNT_PROP; 023import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_COUNT_ASC; 024import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_COUNT_DESC; 025import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_KEY_ASC; 026import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_KEY_DESC; 027import static org.nuxeo.elasticsearch.ElasticSearchConstants.AGG_ORDER_PROP; 028 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.List; 032import java.util.Map; 033 034import org.codehaus.jackson.annotate.JsonIgnore; 035import org.elasticsearch.index.query.FilterBuilders; 036import org.elasticsearch.index.query.OrFilterBuilder; 037import org.elasticsearch.index.query.RangeFilterBuilder; 038import org.elasticsearch.search.aggregations.AggregationBuilders; 039import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation; 040import org.elasticsearch.search.aggregations.bucket.histogram.Histogram; 041import org.elasticsearch.search.aggregations.bucket.histogram.HistogramBuilder; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.platform.query.api.AggregateDefinition; 044import org.nuxeo.ecm.platform.query.core.BucketRange; 045 046/** 047 * @since 6.0 048 */ 049public class HistogramAggregate extends AggregateEsBase<BucketRange> { 050 051 private Integer interval; 052 053 public HistogramAggregate(AggregateDefinition definition, DocumentModel searchDocument) { 054 super(definition, searchDocument); 055 } 056 057 @JsonIgnore 058 @Override 059 public HistogramBuilder getEsAggregate() { 060 HistogramBuilder ret = AggregationBuilders.histogram(getId()).field(getField()); 061 Map<String, String> props = getProperties(); 062 ret.interval(getInterval()); 063 if (props.containsKey(AGG_MIN_DOC_COUNT_PROP)) { 064 ret.minDocCount(Long.parseLong(props.get(AGG_MIN_DOC_COUNT_PROP))); 065 } 066 if (props.containsKey(AGG_ORDER_PROP)) { 067 switch (props.get(AGG_ORDER_PROP).toLowerCase()) { 068 case AGG_ORDER_COUNT_DESC: 069 ret.order(Histogram.Order.COUNT_DESC); 070 break; 071 case AGG_ORDER_COUNT_ASC: 072 ret.order(Histogram.Order.COUNT_ASC); 073 break; 074 case AGG_ORDER_KEY_DESC: 075 ret.order(Histogram.Order.KEY_DESC); 076 break; 077 case AGG_ORDER_KEY_ASC: 078 ret.order(Histogram.Order.KEY_ASC); 079 break; 080 default: 081 throw new IllegalArgumentException("Invalid order: " + props.get(AGG_ORDER_PROP)); 082 } 083 } 084 if (props.containsKey(AGG_EXTENDED_BOUND_MAX_PROP) && props.containsKey(AGG_EXTENDED_BOUND_MIN_PROP)) { 085 ret.extendedBounds(Long.parseLong(props.get(AGG_EXTENDED_BOUND_MIN_PROP)), 086 Long.parseLong(props.get(AGG_EXTENDED_BOUND_MAX_PROP))); 087 } 088 return ret; 089 } 090 091 @JsonIgnore 092 @Override 093 public OrFilterBuilder getEsFilter() { 094 if (getSelection().isEmpty()) { 095 return null; 096 } 097 OrFilterBuilder ret = FilterBuilders.orFilter(); 098 for (String sel : getSelection()) { 099 RangeFilterBuilder rangeFilter = FilterBuilders.rangeFilter(getField()); 100 long from = Long.parseLong(sel); 101 long to = from + getInterval(); 102 rangeFilter.gte(from).lt(to); 103 ret.add(rangeFilter); 104 } 105 return ret; 106 } 107 108 @JsonIgnore 109 @Override 110 public void parseEsBuckets(Collection<? extends MultiBucketsAggregation.Bucket> buckets) { 111 List<BucketRange> nxBuckets = new ArrayList<>(buckets.size()); 112 for (MultiBucketsAggregation.Bucket bucket : buckets) { 113 Histogram.Bucket histoBucket = (Histogram.Bucket) bucket; 114 nxBuckets.add(new BucketRange(bucket.getKey(), histoBucket.getKeyAsNumber(), 115 histoBucket.getKeyAsNumber().intValue() + getInterval(), histoBucket.getDocCount())); 116 } 117 this.buckets = nxBuckets; 118 } 119 120 public int getInterval() { 121 if (interval == null) { 122 Map<String, String> props = getProperties(); 123 if (props.containsKey(AGG_INTERVAL_PROP)) { 124 interval = Integer.parseInt(props.get(AGG_INTERVAL_PROP)); 125 } else { 126 throw new IllegalArgumentException("interval property must be defined for " + toString()); 127 } 128 } 129 return interval; 130 } 131}