Make Aggregations an abstract class rather than an interface (#24184)

Some of the base methods that don't have to do with reduce phase and serialization can be moved to the base class which is no longer an interface. This will be reusable by the high level REST client further on the road. Also it simplify things as having an interface with a single implementor is not that helpful.
This commit is contained in:
Luca Cavanna 2017-04-20 21:31:34 +02:00 committed by GitHub
parent 077a6c3ee7
commit 82c678b5c7
2 changed files with 71 additions and 100 deletions

View File

@ -18,31 +18,84 @@
*/
package org.elasticsearch.search.aggregations;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static java.util.Collections.unmodifiableMap;
/**
* Represents a set of computed addAggregation.
* Represents a set of {@link Aggregation}s
*/
public interface Aggregations extends Iterable<Aggregation> {
public abstract class Aggregations implements Iterable<Aggregation> {
protected List<? extends Aggregation> aggregations = Collections.emptyList();
protected Map<String, Aggregation> aggregationsAsMap;
protected Aggregations() {
}
protected Aggregations(List<? extends Aggregation> aggregations) {
this.aggregations = aggregations;
}
/**
* Iterates over the {@link Aggregation}s.
*/
@Override
public final Iterator<Aggregation> iterator() {
return aggregations.stream().map((p) -> (Aggregation) p).iterator();
}
/**
* The list of {@link Aggregation}s.
*/
List<Aggregation> asList();
public final List<Aggregation> asList() {
return Collections.unmodifiableList(aggregations);
}
/**
* Returns the {@link Aggregation}s keyed by aggregation name.
*/
Map<String, Aggregation> asMap();
public final Map<String, Aggregation> asMap() {
return getAsMap();
}
/**
* Returns the {@link Aggregation}s keyed by aggregation name.
*/
Map<String, Aggregation> getAsMap();
public final Map<String, Aggregation> getAsMap() {
if (aggregationsAsMap == null) {
Map<String, Aggregation> newAggregationsAsMap = new HashMap<>(aggregations.size());
for (Aggregation aggregation : aggregations) {
newAggregationsAsMap.put(aggregation.getName(), aggregation);
}
this.aggregationsAsMap = unmodifiableMap(newAggregationsAsMap);
}
return aggregationsAsMap;
}
/**
* Returns the aggregation that is associated with the specified name.
*/
<A extends Aggregation> A get(String name);
@SuppressWarnings("unchecked")
public final <A extends Aggregation> A get(String name) {
return (A) asMap().get(name);
}
@Override
public final boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return aggregations.equals(((Aggregations) obj).aggregations);
}
@Override
public final int hashCode() {
return Objects.hash(getClass(), aggregations);
}
}

View File

@ -27,27 +27,18 @@ import org.elasticsearch.search.aggregations.InternalAggregation.ReduceContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableMap;
/**
* An internal implementation of {@link Aggregations}.
*/
public class InternalAggregations implements Aggregations, ToXContent, Streamable {
public final class InternalAggregations extends Aggregations implements ToXContent, Streamable {
public static final InternalAggregations EMPTY = new InternalAggregations();
private List<InternalAggregation> aggregations = Collections.emptyList();
private Map<String, Aggregation> aggregationsAsMap;
private InternalAggregations() {
}
@ -55,55 +46,7 @@ public class InternalAggregations implements Aggregations, ToXContent, Streamabl
* Constructs a new addAggregation.
*/
public InternalAggregations(List<InternalAggregation> aggregations) {
this.aggregations = aggregations;
}
/**
* Iterates over the {@link Aggregation}s.
*/
@Override
public Iterator<Aggregation> iterator() {
return aggregations.stream().map((p) -> (Aggregation) p).iterator();
}
/**
* The list of {@link Aggregation}s.
*/
@Override
public List<Aggregation> asList() {
return aggregations.stream().map((p) -> (Aggregation) p).collect(Collectors.toList());
}
/**
* Returns the {@link Aggregation}s keyed by map.
*/
@Override
public Map<String, Aggregation> asMap() {
return getAsMap();
}
/**
* Returns the {@link Aggregation}s keyed by map.
*/
@Override
public Map<String, Aggregation> getAsMap() {
if (aggregationsAsMap == null) {
Map<String, InternalAggregation> newAggregationsAsMap = new HashMap<>();
for (InternalAggregation aggregation : aggregations) {
newAggregationsAsMap.put(aggregation.getName(), aggregation);
}
this.aggregationsAsMap = unmodifiableMap(newAggregationsAsMap);
}
return aggregationsAsMap;
}
/**
* @return the aggregation of the specified name.
*/
@SuppressWarnings("unchecked")
@Override
public <A extends Aggregation> A get(String name) {
return (A) asMap().get(name);
super(aggregations);
}
/**
@ -118,21 +61,16 @@ public class InternalAggregations implements Aggregations, ToXContent, Streamabl
}
// first we collect all aggregations of the same type and list them together
Map<String, List<InternalAggregation>> aggByName = new HashMap<>();
for (InternalAggregations aggregations : aggregationsList) {
for (InternalAggregation aggregation : aggregations.aggregations) {
List<InternalAggregation> aggs = aggByName.get(aggregation.getName());
if (aggs == null) {
aggs = new ArrayList<>(aggregationsList.size());
aggByName.put(aggregation.getName(), aggs);
}
aggs.add(aggregation);
for (Aggregation aggregation : aggregations.aggregations) {
List<InternalAggregation> aggs = aggByName.computeIfAbsent(
aggregation.getName(), k -> new ArrayList<>(aggregationsList.size()));
aggs.add((InternalAggregation)aggregation);
}
}
// now we can use the first aggregation of each list to handle the reduce of its list
List<InternalAggregation> reducedAggregations = new ArrayList<>();
for (Map.Entry<String, List<InternalAggregation>> entry : aggByName.entrySet()) {
List<InternalAggregation> aggregations = entry.getValue();
@ -142,23 +80,18 @@ public class InternalAggregations implements Aggregations, ToXContent, Streamabl
return new InternalAggregations(reducedAggregations);
}
/** The fields required to write this addAggregation to xcontent */
static class Fields {
public static final String AGGREGATIONS = "aggregations";
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (aggregations.isEmpty()) {
return builder;
}
builder.startObject(Fields.AGGREGATIONS);
builder.startObject("aggregations");
toXContentInternal(builder, params);
return builder.endObject();
}
/**
* Directly write all the addAggregation without their bounding object. Used by sub-addAggregation (non top level addAggregation)
* Directly write all the aggregations without their bounding object. Used by sub-aggregations (non top level aggs)
*/
public XContentBuilder toXContentInternal(XContentBuilder builder, Params params) throws IOException {
for (Aggregation aggregation : aggregations) {
@ -167,16 +100,13 @@ public class InternalAggregations implements Aggregations, ToXContent, Streamabl
return builder;
}
public static InternalAggregations readAggregations(StreamInput in) throws IOException {
InternalAggregations result = new InternalAggregations();
result.readFrom(in);
return result;
}
public static InternalAggregations readOptionalAggregations(StreamInput in) throws IOException {
return in.readOptionalStreamable(InternalAggregations::new);
}
@Override
public void readFrom(StreamInput in) throws IOException {
aggregations = in.readList(stream -> in.readNamedWriteable(InternalAggregation.class));
@ -186,20 +116,8 @@ public class InternalAggregations implements Aggregations, ToXContent, Streamabl
}
@Override
@SuppressWarnings("unchecked")
public void writeTo(StreamOutput out) throws IOException {
out.writeNamedWriteableList(aggregations);
}
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return aggregations.equals(((InternalAggregations) obj).aggregations);
}
@Override
public int hashCode() {
return Objects.hash(getClass(), aggregations);
out.writeNamedWriteableList((List<InternalAggregation>)aggregations);
}
}