Skip to content

Meta data support with each aggregation request/response #8279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 3, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions docs/reference/search/aggregations.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The following snippet captures the basic structure of aggregations:
"<aggregation_type>" : {
<aggregation_body>
}
[,"meta" : { [<meta_data_body>] } ]?
[,"aggregations" : { [<sub_aggregation>]+ } ]?
}
[,"<aggregation_name_2>" : { ... } ]*
Expand Down Expand Up @@ -148,6 +149,49 @@ $ curl -XGET 'http://localhost:9200/twitter/tweet/_search?search_type=count' -d
Setting `search_type` to `count` avoids executing the fetch phase of the search making the request more efficient. See
<<search-request-search-type>> for more information on the `search_type` parameter.

[float]
=== Metadata

You can associate a piece of metadata with individual aggregations at request time that will be returned in place
at response time.

Consider this example where we want to associate the color blue with our `terms` aggregation.

[source,js]
--------------------------------------------------
{
...
aggs": {
"titles": {
"terms": {
"field": "title"
},
"meta": {
"color": "blue"
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this "," can be removed ;)

}
}
}
--------------------------------------------------

Then that piece of metadata will be returned in place for our `titles` terms aggregation

[source,js]
--------------------------------------------------
{
...
"aggregations": {
"titles": {
"meta": {
"color" : "blue"
},
"buckets": [
]
}
}
}
--------------------------------------------------

include::aggregations/metrics.asciidoc[]

include::aggregations/bucket.asciidoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
package org.elasticsearch.search.aggregations;

import java.util.Map;

/**
* An aggregation
*/
Expand All @@ -28,4 +30,8 @@ public interface Aggregation {
*/
String getName();

/**
* Get the optional byte array metadata that was set on the aggregation
*/
Map<String, Object> getMetaData();
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public abstract class AggregationBuilder<B extends AggregationBuilder<B>> extend

private List<AbstractAggregationBuilder> aggregations;
private BytesReference aggregationsBinary;
private Map<String, Object> metaData;

/**
* Sole constructor, typically used by sub-classes.
Expand Down Expand Up @@ -101,10 +102,21 @@ public B subAggregation(Map<String, Object> aggs) {
}
}

/**
* Sets the meta data to be included in the aggregation response
*/
public B setMetaData(Map<String, Object> metaData) {
this.metaData = metaData;
return (B)this;
}

@Override
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(getName());

if (this.metaData != null) {
builder.field("meta", this.metaData);
}
builder.field(type);
internalXContent(builder, params);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public boolean apply(Aggregator aggregator) {
return aggregator.shouldCollect();
}
};
private final Map<String, Object> metaData;

/**
* Returns whether any of the parent aggregators has {@link BucketAggregationMode#PER_BUCKET} as a bucket aggregation mode.
Expand All @@ -60,6 +61,10 @@ public static boolean hasParentBucketAggregator(Aggregator parent) {

public static final ParseField COLLECT_MODE = new ParseField("collect_mode");

public Map<String, Object> getMetaData() {
return this.metaData;
}

/**
* Defines the nature of the aggregator's aggregation execution when nested in other aggregators and the buckets they create.
*/
Expand Down Expand Up @@ -177,9 +182,11 @@ public int nextDoc() throws IOException {
* @param estimatedBucketsCount When served as a sub-aggregator, indicate how many buckets the parent aggregator will generate.
* @param context The aggregation context
* @param parent The parent aggregator (may be {@code null} for top level aggregators)
* @param metaData The metaData associated with this aggregator
*/
protected Aggregator(String name, BucketAggregationMode bucketAggregationMode, AggregatorFactories factories, long estimatedBucketsCount, AggregationContext context, Aggregator parent) {
protected Aggregator(String name, BucketAggregationMode bucketAggregationMode, AggregatorFactories factories, long estimatedBucketsCount, AggregationContext context, Aggregator parent, Map<String, Object> metaData) {
this.name = name;
this.metaData = metaData;
this.parent = parent;
this.estimatedBucketCount = estimatedBucketsCount;
this.context = context;
Expand Down Expand Up @@ -217,6 +224,7 @@ public void gatherAnalysis(BucketAnalysisCollector results, long bucketOrdinal)
}
};
}

protected void preCollection() {
Iterable<Aggregator> collectables = Iterables.filter(Arrays.asList(subAggregators), COLLECTABLE_AGGREGATOR);
List<BucketCollector> nextPassCollectors = new ArrayList<>();
Expand Down Expand Up @@ -362,9 +370,6 @@ public void gatherAnalysis(BucketAnalysisCollector results, long bucketOrdinal)
results.add(buildAggregation(bucketOrdinal));
}




public abstract InternalAggregation buildEmptyAggregation();

protected final InternalAggregations buildEmptySubAggregations() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public Aggregator[] createSubAggregators(Aggregator parent, final long estimated
continue;
}
// the aggregator doesn't support multiple ordinals, let's wrap it so that it does.
aggregators[i] = new Aggregator(first.name(), BucketAggregationMode.MULTI_BUCKETS, AggregatorFactories.EMPTY, 1, first.context(), first.parent()) {
aggregators[i] = new Aggregator(first.name(), BucketAggregationMode.MULTI_BUCKETS, AggregatorFactories.EMPTY, 1, first.context(), first.parent(), first.getMetaData()) {

ObjectArray<Aggregator> aggregators;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

import org.elasticsearch.search.aggregations.support.AggregationContext;

import java.util.Map;

/**
* A factory that knows how to create an {@link Aggregator} of a specific type.
*/
Expand All @@ -29,6 +31,7 @@ public abstract class AggregatorFactory {
protected String type;
protected AggregatorFactory parent;
protected AggregatorFactories factories = AggregatorFactories.EMPTY;
protected Map<String, Object> metaData;

/**
* Constructs a new aggregator factory.
Expand Down Expand Up @@ -79,9 +82,17 @@ public AggregatorFactory parent() {
*
* @return The created aggregator
*/
public abstract Aggregator create(AggregationContext context, Aggregator parent, long expectedBucketsCount);
protected abstract Aggregator createInternal(AggregationContext context, Aggregator parent, long expectedBucketsCount, Map<String, Object> metaData);

public Aggregator create(AggregationContext context, Aggregator parent, long expectedBucketsCount) {
Aggregator aggregator = createInternal(context, parent, expectedBucketsCount, this.metaData);
return aggregator;
}

public void doValidate() {
}

public void setMetaData(Map<String, Object> metaData) {
this.metaData = metaData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.elasticsearch.search.internal.SearchContext;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -100,6 +101,8 @@ private AggregatorFactories parseAggregators(XContentParser parser, SearchContex
AggregatorFactory factory = null;
AggregatorFactories subFactories = null;

Map<String, Object> metaData = null;

while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (token != XContentParser.Token.FIELD_NAME) {
throw new SearchParseException(context, "Expected [" + XContentParser.Token.FIELD_NAME + "] under a [" + XContentParser.Token.START_OBJECT + "], but got a [" + token + "] in [" + aggregationName + "]");
Expand All @@ -112,6 +115,9 @@ private AggregatorFactories parseAggregators(XContentParser parser, SearchContex
}

switch (fieldName) {
case "meta":
metaData = parser.map();
break;
case "aggregations":
case "aggs":
if (subFactories != null) {
Expand All @@ -135,6 +141,10 @@ private AggregatorFactories parseAggregators(XContentParser parser, SearchContex
throw new SearchParseException(context, "Missing definition for aggregation [" + aggregationName + "]");
}

if (metaData != null) {
factory.setMetaData(metaData);
}

if (subFactories != null) {
factory.subFactories(subFactories);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.elasticsearch.search.aggregations;

import org.elasticsearch.Version;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput;
Expand All @@ -31,12 +32,14 @@

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
* An internal implementation of {@link Aggregation}. Serves as a base class for all aggregation implementations.
*/
public abstract class InternalAggregation implements Aggregation, ToXContent, Streamable {


/**
* The aggregation type that holds all the string types that are associated with an aggregation:
* <ul>
Expand Down Expand Up @@ -111,6 +114,8 @@ public ScriptService scriptService() {

protected String name;

protected Map<String, Object> metaData;

/** Constructs an un initialized addAggregation (used for serialization) **/
protected InternalAggregation() {}

Expand All @@ -119,8 +124,9 @@ protected InternalAggregation() {}
*
* @param name The name of the get.
*/
protected InternalAggregation(String name) {
protected InternalAggregation(String name, Map<String, Object> metaData) {
this.name = name;
this.metaData = metaData;
}

@Override
Expand Down Expand Up @@ -158,21 +164,50 @@ protected static void writeSize(int size, StreamOutput out) throws IOException {
}
out.writeVInt(size);
}


public Map<String, Object> getMetaData() {
return metaData;
}

@Override
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(name);
if (this.metaData != null) {
builder.field(CommonFields.META);
builder.map(this.metaData);
}
doXContentBody(builder, params);
builder.endObject();
return builder;
}

public abstract XContentBuilder doXContentBody(XContentBuilder builder, Params params) throws IOException;

public final void writeTo(StreamOutput out) throws IOException {
out.writeString(name);
if (out.getVersion().onOrAfter(Version.V_1_5_0)) {
out.writeGenericValue(metaData);
}
doWriteTo(out);
}

protected abstract void doWriteTo(StreamOutput out) throws IOException;

public final void readFrom(StreamInput in) throws IOException {
name = in.readString();
if (in.getVersion().onOrAfter(Version.V_1_5_0)) {
metaData = in.readMap();
}
doReadFrom(in);
}

protected abstract void doReadFrom(StreamInput in) throws IOException;

/**
* Common xcontent fields that are shared among addAggregation
*/
public static final class CommonFields {
public static final XContentBuilderString META = new XContentBuilderString("meta");
public static final XContentBuilderString BUCKETS = new XContentBuilderString("buckets");
public static final XContentBuilderString VALUE = new XContentBuilderString("value");
public static final XContentBuilderString VALUES = new XContentBuilderString("values");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,16 @@
import org.elasticsearch.search.aggregations.support.AggregationContext;

import java.io.IOException;
import java.util.Map;

/**
* An aggregator that is not collected, this can typically be used when running an aggregation over a field that doesn't have
* a mapping.
*/
public abstract class NonCollectingAggregator extends Aggregator {

protected NonCollectingAggregator(String name, AggregationContext context, Aggregator parent) {
super(name, BucketAggregationMode.MULTI_BUCKETS, AggregatorFactories.EMPTY, 0, context, parent);
protected NonCollectingAggregator(String name, AggregationContext context, Aggregator parent, Map<String, Object> metaData) {
super(name, BucketAggregationMode.MULTI_BUCKETS, AggregatorFactories.EMPTY, 0, context, parent, metaData);
}

private void fail() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

/**
*
Expand All @@ -35,8 +36,8 @@ public abstract class BucketsAggregator extends Aggregator {
private IntArray docCounts;

public BucketsAggregator(String name, BucketAggregationMode bucketAggregationMode, AggregatorFactories factories,
long estimatedBucketsCount, AggregationContext context, Aggregator parent) {
super(name, bucketAggregationMode, factories, estimatedBucketsCount, context, parent);
long estimatedBucketsCount, AggregationContext context, Aggregator parent, Map<String, Object> metaData) {
super(name, bucketAggregationMode, factories, estimatedBucketsCount, context, parent, metaData);
docCounts = bigArrays.newIntArray(estimatedBucketsCount, true);
}

Expand Down
Loading