/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.aggregations.bucket.terms;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.search.aggregations.Aggregator;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.bucket.BucketsAggregator;
import org.elasticsearch.search.aggregations.bucket.terms.InternalOrder.Aggregation;
import org.elasticsearch.search.aggregations.bucket.terms.InternalOrder.CompoundOrder;
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
import org.elasticsearch.search.aggregations.support.AggregationContext;
import org.elasticsearch.search.aggregations.support.AggregationPath;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class TermsAggregator extends BucketsAggregator {
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(TermsAggregator.class));
public static class BucketCountThresholds {
private Explicit<Long> minDocCount;
private Explicit<Long> shardMinDocCount;
private Explicit<Integer> requiredSize;
private Explicit<Integer> shardSize;
public BucketCountThresholds(long minDocCount, long shardMinDocCount, int requiredSize, int shardSize) {
this.minDocCount = new Explicit<>(minDocCount, false);
this.shardMinDocCount = new Explicit<>(shardMinDocCount, false);
this.requiredSize = new Explicit<>(requiredSize, false);
this.shardSize = new Explicit<>(shardSize, false);
}
public BucketCountThresholds() {
this(-1, -1, -1, -1);
}
public BucketCountThresholds(BucketCountThresholds bucketCountThresholds) {
this(bucketCountThresholds.minDocCount.value(), bucketCountThresholds.shardMinDocCount.value(), bucketCountThresholds.requiredSize.value(), bucketCountThresholds.shardSize.value());
}
public void ensureValidity() {
if (shardSize.value() == 0) {
setShardSize(Integer.MAX_VALUE);
DEPRECATION_LOGGER.deprecated("shardSize of 0 in aggregations is deprecated and will be invalid in future versions. "
+ "Please specify a shardSize greater than 0");
}
if (requiredSize.value() == 0) {
setRequiredSize(Integer.MAX_VALUE);
DEPRECATION_LOGGER.deprecated("size of 0 in aggregations is deprecated and will be invalid in future versions. "
+ "Please specify a size greater than 0");
}
// shard_size cannot be smaller than size as we need to at least fetch <size> entries from every shards in order to return <size>
if (shardSize.value() < requiredSize.value()) {
setShardSize(requiredSize.value());
}
// shard_min_doc_count should not be larger than min_doc_count because this can cause buckets to be removed that would match the min_doc_count criteria
if (shardMinDocCount.value() > minDocCount.value()) {
setShardMinDocCount(minDocCount.value());
}
if (requiredSize.value() < 0 || minDocCount.value() < 0) {
throw new ElasticsearchException("parameters [requiredSize] and [minDocCount] must be >=0 in terms aggregation.");
}
}
public long getShardMinDocCount() {
return shardMinDocCount.value();
}
public void setShardMinDocCount(long shardMinDocCount) {
this.shardMinDocCount = new Explicit<>(shardMinDocCount, true);
}
public long getMinDocCount() {
return minDocCount.value();
}
public void setMinDocCount(long minDocCount) {
this.minDocCount = new Explicit<>(minDocCount, true);
}
public int getRequiredSize() {
return requiredSize.value();
}
public void setRequiredSize(int requiredSize) {
this.requiredSize = new Explicit<>(requiredSize, true);
}
public int getShardSize() {
return shardSize.value();
}
public void setShardSize(int shardSize) {
this.shardSize = new Explicit<>(shardSize, true);
}
public void toXContent(XContentBuilder builder) throws IOException {
if (requiredSize.explicit()) {
builder.field(AbstractTermsParametersParser.REQUIRED_SIZE_FIELD_NAME.getPreferredName(), requiredSize.value());
}
if (shardSize.explicit()) {
builder.field(AbstractTermsParametersParser.SHARD_SIZE_FIELD_NAME.getPreferredName(), shardSize.value());
}
if (minDocCount.explicit()) {
builder.field(AbstractTermsParametersParser.MIN_DOC_COUNT_FIELD_NAME.getPreferredName(), minDocCount.value());
}
if (shardMinDocCount.explicit()) {
builder.field(AbstractTermsParametersParser.SHARD_MIN_DOC_COUNT_FIELD_NAME.getPreferredName(), shardMinDocCount.value());
}
}
}
protected final BucketCountThresholds bucketCountThresholds;
protected final Terms.Order order;
protected final Set<Aggregator> aggsUsedForSorting = new HashSet<>();
protected final SubAggCollectionMode collectMode;
public TermsAggregator(String name, AggregatorFactories factories, AggregationContext context, Aggregator parent, BucketCountThresholds bucketCountThresholds, Terms.Order order, SubAggCollectionMode collectMode, List<PipelineAggregator> pipelineAggregators, Map<String, Object> metaData) throws IOException {
super(name, factories, context, parent, pipelineAggregators, metaData);
this.bucketCountThresholds = bucketCountThresholds;
this.order = InternalOrder.validate(order, this);
this.collectMode = collectMode;
// Don't defer any child agg if we are dependent on it for pruning results
if (order instanceof Aggregation){
AggregationPath path = ((Aggregation) order).path();
aggsUsedForSorting.add(path.resolveTopmostAggregator(this));
} else if (order instanceof CompoundOrder) {
CompoundOrder compoundOrder = (CompoundOrder) order;
for (Terms.Order orderElement : compoundOrder.orderElements()) {
if (orderElement instanceof Aggregation) {
AggregationPath path = ((Aggregation) orderElement).path();
aggsUsedForSorting.add(path.resolveTopmostAggregator(this));
}
}
}
}
@Override
protected boolean shouldDefer(Aggregator aggregator) {
return collectMode == SubAggCollectionMode.BREADTH_FIRST
&& aggregator.needsScores() == false
&& !aggsUsedForSorting.contains(aggregator);
}
}