refactoring of facets to be more open for different facet types

This commit is contained in:
kimchy 2010-06-05 21:49:10 +03:00
parent d9d9304f47
commit 88ba980f79
20 changed files with 173 additions and 258 deletions

View File

@ -254,7 +254,7 @@ public class SearchRequestBuilder {
* @param name The logical name of the facet, it will be returned under the name
* @param query The query facet
*/
public SearchRequestBuilder addQueryFacet(String name, XContentQueryBuilder query) {
public SearchRequestBuilder addFacetQuery(String name, XContentQueryBuilder query) {
facetsBuilder().queryFacet(name, query);
return this;
}
@ -263,22 +263,21 @@ public class SearchRequestBuilder {
* Adds a query facet (which results in a count facet returned) with an option to
* be global on the index or bounded by the search query.
*
* @param name The logical name of the facet, it will be returned under the name
* @param query The query facet
* @param global Should the facet be executed globally or not
* @param name The logical name of the facet, it will be returned under the name
* @param query The query facet
*/
public SearchRequestBuilder addQueryFacet(String name, XContentQueryBuilder query, boolean global) {
facetsBuilder().queryFacet(name, query, global);
public SearchRequestBuilder addFacetGlobalQuery(String name, XContentQueryBuilder query) {
facetsBuilder().queryFacetGlobal(name, query);
return this;
}
public SearchRequestBuilder addTermFacet(String name, String fieldName, int size) {
facetsBuilder().termFacet(name, fieldName, size);
public SearchRequestBuilder addFacetTerms(String name, String fieldName, int size) {
facetsBuilder().termsFacet(name, fieldName, size);
return this;
}
public SearchRequestBuilder addTermFacet(String name, String fieldName, int size, boolean global) {
facetsBuilder().termFacet(name, fieldName, size, global);
public SearchRequestBuilder addFacetGlobalTerms(String name, String fieldName, int size) {
facetsBuilder().termsFacetGlobal(name, fieldName, size);
return this;
}

View File

@ -20,8 +20,8 @@
package org.elasticsearch.search.builder;
import org.elasticsearch.index.query.xcontent.XContentQueryBuilder;
import org.elasticsearch.search.facets.collector.query.QueryFacetCollectorParser;
import org.elasticsearch.search.facets.collector.term.TermFacetCollectorParser;
import org.elasticsearch.search.facets.query.QueryFacetCollectorParser;
import org.elasticsearch.search.facets.terms.TermFacetCollectorParser;
import org.elasticsearch.util.xcontent.ToXContent;
import org.elasticsearch.util.xcontent.builder.XContentBuilder;
@ -39,7 +39,7 @@ import static org.elasticsearch.util.collect.Lists.*;
public class SearchSourceFacetsBuilder implements ToXContent {
private List<QueryFacet> queryFacets;
private List<TermFacet> termFacets;
private List<TermsFacet> termsFacets;
/**
* Adds a query facet (which results in a count facet returned).
@ -48,7 +48,11 @@ public class SearchSourceFacetsBuilder implements ToXContent {
* @param query The query facet
*/
public SearchSourceFacetsBuilder queryFacet(String name, XContentQueryBuilder query) {
return queryFacet(name, query, null);
if (queryFacets == null) {
queryFacets = newArrayListWithCapacity(2);
}
queryFacets.add(new QueryFacet(name, query, false));
return this;
}
/**
@ -59,28 +63,32 @@ public class SearchSourceFacetsBuilder implements ToXContent {
* @param query The query facet
* @param global Should the facet be executed globally or not
*/
public SearchSourceFacetsBuilder queryFacet(String name, XContentQueryBuilder query, Boolean global) {
public SearchSourceFacetsBuilder queryFacetGlobal(String name, XContentQueryBuilder query) {
if (queryFacets == null) {
queryFacets = newArrayListWithCapacity(2);
}
queryFacets.add(new QueryFacet(name, query, global));
queryFacets.add(new QueryFacet(name, query, true));
return this;
}
public SearchSourceFacetsBuilder termFacet(String name, String fieldName, int size) {
return termFacet(name, fieldName, size, null);
public SearchSourceFacetsBuilder termsFacet(String name, String fieldName, int size) {
if (termsFacets == null) {
termsFacets = newArrayListWithCapacity(2);
}
termsFacets.add(new TermsFacet(name, fieldName, size, false));
return this;
}
public SearchSourceFacetsBuilder termFacet(String name, String fieldName, int size, Boolean global) {
if (termFacets == null) {
termFacets = newArrayListWithCapacity(2);
public SearchSourceFacetsBuilder termsFacetGlobal(String name, String fieldName, int size) {
if (termsFacets == null) {
termsFacets = newArrayListWithCapacity(2);
}
termFacets.add(new TermFacet(name, fieldName, size, global));
termsFacets.add(new TermsFacet(name, fieldName, size, true));
return this;
}
@Override public void toXContent(XContentBuilder builder, Params params) throws IOException {
if (queryFacets == null && termFacets == null) {
if (queryFacets == null && termsFacets == null) {
return;
}
builder.field("facets");
@ -98,17 +106,17 @@ public class SearchSourceFacetsBuilder implements ToXContent {
builder.endObject();
}
}
if (termFacets != null) {
for (TermFacet termFacet : termFacets) {
builder.startObject(termFacet.name());
if (termsFacets != null) {
for (TermsFacet termsFacet : termsFacets) {
builder.startObject(termsFacet.name());
builder.startObject(TermFacetCollectorParser.NAME);
builder.field("field", termFacet.fieldName());
builder.field("size", termFacet.size());
builder.field("field", termsFacet.fieldName());
builder.field("size", termsFacet.size());
builder.endObject();
if (termFacet.global() != null) {
builder.field("global", termFacet.global());
if (termsFacet.global() != null) {
builder.field("global", termsFacet.global());
}
builder.endObject();
@ -118,13 +126,13 @@ public class SearchSourceFacetsBuilder implements ToXContent {
builder.endObject();
}
private static class TermFacet {
private static class TermsFacet {
private final String name;
private final String fieldName;
private final int size;
private final Boolean global;
private TermFacet(String name, String fieldName, int size, Boolean global) {
private TermsFacet(String name, String fieldName, int size, Boolean global) {
this.name = name;
this.fieldName = fieldName;
this.size = size;

View File

@ -30,7 +30,7 @@ import org.elasticsearch.search.dfs.AggregatedDfs;
import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facets.Facet;
import org.elasticsearch.search.facets.Facets;
import org.elasticsearch.search.facets.internal.InternalFacet;
import org.elasticsearch.search.facets.InternalFacet;
import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.FetchSearchResultProvider;
import org.elasticsearch.search.internal.InternalSearchHit;

View File

@ -32,24 +32,24 @@ public interface Facet {
/**
* Count type facet.
*/
COUNT((byte) 0),
MULTI_COUNT((byte) 1);
TERMS(0),
QUERY(1);
byte id;
int id;
Type(byte id) {
Type(int id) {
this.id = id;
}
public byte id() {
public int id() {
return id;
}
public static Type fromId(byte id) {
public static Type fromId(int id) {
if (id == 0) {
return COUNT;
return TERMS;
} else if (id == 1) {
return MULTI_COUNT;
return QUERY;
} else {
throw new ElasticSearchIllegalArgumentException("No match for id [" + id + "]");
}

View File

@ -19,7 +19,8 @@
package org.elasticsearch.search.facets;
import org.elasticsearch.search.facets.internal.InternalFacet;
import org.elasticsearch.search.facets.query.InternalQueryFacet;
import org.elasticsearch.search.facets.terms.InternalTermsFacet;
import org.elasticsearch.util.collect.ImmutableList;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
@ -32,8 +33,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import static org.elasticsearch.search.facets.internal.InternalCountFacet.*;
import static org.elasticsearch.search.facets.internal.InternalMultiCountFacet.*;
import static org.elasticsearch.util.collect.Lists.*;
import static org.elasticsearch.util.collect.Maps.*;
@ -98,10 +97,10 @@ public class Facets implements Streamable, ToXContent, Iterable<Facet> {
}
/**
* A specific count facet against the registered facet name.
* Returns the facet by name already casted to the specified type.
*/
public CountFacet countFacet(String name) {
return (CountFacet) facet(name);
public <T extends Facet> T facet(Class<T> facetType, String name) {
return facetType.cast(facet(name));
}
/**
@ -132,11 +131,11 @@ public class Facets implements Streamable, ToXContent, Iterable<Facet> {
} else {
facets = newArrayListWithCapacity(size);
for (int i = 0; i < size; i++) {
byte id = in.readByte();
if (id == Facet.Type.COUNT.id()) {
facets.add(readCountFacet(in));
} else if (id == Facet.Type.MULTI_COUNT.id()) {
facets.add(readMultiCountFacet(in));
int id = in.readVInt();
if (id == Facet.Type.TERMS.id()) {
facets.add(InternalTermsFacet.readTermsFacet(in));
} else if (id == Facet.Type.QUERY.id()) {
facets.add(InternalQueryFacet.readCountFacet(in));
} else {
throw new IOException("Can't handle facet type with id [" + id + "]");
}
@ -147,7 +146,7 @@ public class Facets implements Streamable, ToXContent, Iterable<Facet> {
@Override public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(facets.size());
for (Facet facet : facets) {
out.writeByte(facet.type().id());
out.writeVInt(facet.type().id());
((InternalFacet) facet).writeTo(out);
}
}

View File

@ -23,8 +23,8 @@ import org.elasticsearch.search.SearchParseElement;
import org.elasticsearch.search.SearchParseException;
import org.elasticsearch.search.facets.collector.FacetCollector;
import org.elasticsearch.search.facets.collector.FacetCollectorParser;
import org.elasticsearch.search.facets.collector.query.QueryFacetCollectorParser;
import org.elasticsearch.search.facets.collector.term.TermFacetCollectorParser;
import org.elasticsearch.search.facets.query.QueryFacetCollectorParser;
import org.elasticsearch.search.facets.terms.TermFacetCollectorParser;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.util.MapBuilder;
import org.elasticsearch.util.collect.ImmutableMap;

View File

@ -17,9 +17,8 @@
* under the License.
*/
package org.elasticsearch.search.facets.internal;
package org.elasticsearch.search.facets;
import org.elasticsearch.search.facets.Facet;
import org.elasticsearch.util.io.stream.Streamable;
import org.elasticsearch.util.xcontent.ToXContent;

View File

@ -17,10 +17,10 @@
* under the License.
*/
package org.elasticsearch.search.facets.internal;
package org.elasticsearch.search.facets.query;
import org.elasticsearch.search.facets.CountFacet;
import org.elasticsearch.search.facets.Facet;
import org.elasticsearch.search.facets.InternalFacet;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
import org.elasticsearch.util.xcontent.builder.XContentBuilder;
@ -30,23 +30,23 @@ import java.io.IOException;
/**
* @author kimchy (Shay Banon)
*/
public class InternalCountFacet implements CountFacet, InternalFacet {
public class InternalQueryFacet implements QueryFacet, InternalFacet {
private String name;
private long count;
private InternalCountFacet() {
private InternalQueryFacet() {
}
public InternalCountFacet(String name, long count) {
public InternalQueryFacet(String name, long count) {
this.name = name;
this.count = count;
}
@Override public Type type() {
return Type.COUNT;
return Type.QUERY;
}
@Override public Type getType() {
@ -86,18 +86,21 @@ public class InternalCountFacet implements CountFacet, InternalFacet {
int count = 0;
for (Facet facet : facets) {
if (facet.name().equals(name)) {
count += ((InternalCountFacet) facet).count();
count += ((QueryFacet) facet).count();
}
}
return new InternalCountFacet(name, count);
return new InternalQueryFacet(name, count);
}
@Override public void toXContent(XContentBuilder builder, Params params) throws IOException {
builder.field(name, count);
builder.startObject(name);
builder.field("_type", "query");
builder.field("count", count);
builder.endObject();
}
public static CountFacet readCountFacet(StreamInput in) throws IOException {
InternalCountFacet result = new InternalCountFacet();
public static QueryFacet readCountFacet(StreamInput in) throws IOException {
InternalQueryFacet result = new InternalQueryFacet();
result.readFrom(in);
return result;
}
@ -111,5 +114,4 @@ public class InternalCountFacet implements CountFacet, InternalFacet {
out.writeUTF(name);
out.writeVLong(count);
}
}
}

View File

@ -17,14 +17,16 @@
* under the License.
*/
package org.elasticsearch.search.facets;
package org.elasticsearch.search.facets.query;
import org.elasticsearch.search.facets.Facet;
/**
* A count facet is a facet that holds a count.
* A query facets returns the count (number of hits) for a facet based on a query.
*
* @author kimchy (shay.banon)
*/
public interface CountFacet extends Facet {
public interface QueryFacet extends Facet {
/**
* The count of the facet.
@ -35,4 +37,4 @@ public interface CountFacet extends Facet {
* The count of the facet.
*/
long getCount();
}
}

View File

@ -17,14 +17,13 @@
* under the License.
*/
package org.elasticsearch.search.facets.collector.query;
package org.elasticsearch.search.facets.query;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.*;
import org.elasticsearch.index.cache.filter.FilterCache;
import org.elasticsearch.search.facets.Facet;
import org.elasticsearch.search.facets.collector.FacetCollector;
import org.elasticsearch.search.facets.internal.InternalCountFacet;
import org.elasticsearch.util.lucene.docset.DocSet;
import org.elasticsearch.util.lucene.docset.DocSets;
@ -72,6 +71,6 @@ public class QueryFacetCollector extends FacetCollector {
}
@Override public Facet facet() {
return new InternalCountFacet(name, count);
return new InternalQueryFacet(name, count);
}
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
package org.elasticsearch.search.facets.collector.query;
package org.elasticsearch.search.facets.query;
import org.apache.lucene.search.Query;
import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser;

View File

@ -17,10 +17,10 @@
* under the License.
*/
package org.elasticsearch.search.facets.internal;
package org.elasticsearch.search.facets.terms;
import org.elasticsearch.search.facets.Facet;
import org.elasticsearch.search.facets.MultiCountFacet;
import org.elasticsearch.search.facets.InternalFacet;
import org.elasticsearch.util.BoundedTreeSet;
import org.elasticsearch.util.ThreadLocals;
import org.elasticsearch.util.collect.ImmutableList;
@ -40,24 +40,21 @@ import java.util.List;
/**
* @author kimchy (Shay Banon)
*/
public class InternalMultiCountFacet<T extends Comparable> implements InternalFacet, MultiCountFacet<T> {
public class InternalTermsFacet implements InternalFacet, TermsFacet {
private String name;
private int requiredSize;
private Collection<Entry<T>> entries = ImmutableList.of();
private ValueType valueType;
private Collection<Entry> entries = ImmutableList.of();
private ComparatorType comparatorType;
private InternalMultiCountFacet() {
private InternalTermsFacet() {
}
public InternalMultiCountFacet(String name, ValueType valueType, ComparatorType comparatorType, int requiredSize, Collection<Entry<T>> entries) {
public InternalTermsFacet(String name, ComparatorType comparatorType, int requiredSize, Collection<Entry> entries) {
this.name = name;
this.valueType = valueType;
this.comparatorType = comparatorType;
this.requiredSize = requiredSize;
this.entries = entries;
@ -72,130 +69,96 @@ public class InternalMultiCountFacet<T extends Comparable> implements InternalFa
}
@Override public Type type() {
return Type.MULTI_COUNT;
return Type.TERMS;
}
@Override public Type getType() {
return type();
}
public ValueType valueType() {
return valueType;
}
public ValueType getValueType() {
return valueType();
}
@Override public List<Entry<T>> entries() {
@Override public List<Entry> entries() {
return Lists.newArrayList(this);
}
@Override public List<Entry<T>> getEntries() {
@Override public List<Entry> getEntries() {
return Lists.newArrayList(this);
}
@Override public Iterator<Entry<T>> iterator() {
@Override public Iterator<Entry> iterator() {
return entries.iterator();
}
private static ThreadLocal<ThreadLocals.CleanableValue<TObjectIntHashMap<Object>>> aggregateCache = new ThreadLocal<ThreadLocals.CleanableValue<TObjectIntHashMap<Object>>>() {
@Override protected ThreadLocals.CleanableValue<TObjectIntHashMap<Object>> initialValue() {
return new ThreadLocals.CleanableValue<TObjectIntHashMap<java.lang.Object>>(new TObjectIntHashMap<Object>());
private static ThreadLocal<ThreadLocals.CleanableValue<TObjectIntHashMap<String>>> aggregateCache = new ThreadLocal<ThreadLocals.CleanableValue<TObjectIntHashMap<String>>>() {
@Override protected ThreadLocals.CleanableValue<TObjectIntHashMap<String>> initialValue() {
return new ThreadLocals.CleanableValue<TObjectIntHashMap<String>>(new TObjectIntHashMap<String>());
}
};
@Override public Facet aggregate(Iterable<Facet> facets) {
TObjectIntHashMap<Object> aggregated = aggregateCache.get().get();
TObjectIntHashMap<String> aggregated = aggregateCache.get().get();
aggregated.clear();
for (Facet facet : facets) {
if (!facet.name().equals(name)) {
continue;
}
MultiCountFacet<T> mFacet = (MultiCountFacet<T>) facet;
for (Entry<T> entry : mFacet) {
aggregated.adjustOrPutValue(entry.value(), entry.count(), entry.count());
TermsFacet mFacet = (TermsFacet) facet;
for (Entry entry : mFacet) {
aggregated.adjustOrPutValue(entry.term(), entry.count(), entry.count());
}
}
BoundedTreeSet<Entry<T>> ordered = new BoundedTreeSet<Entry<T>>(comparatorType.comparator(), requiredSize);
for (TObjectIntIterator<Object> it = aggregated.iterator(); it.hasNext();) {
BoundedTreeSet<Entry> ordered = new BoundedTreeSet<Entry>(comparatorType.comparator(), requiredSize);
for (TObjectIntIterator<String> it = aggregated.iterator(); it.hasNext();) {
it.advance();
ordered.add(new Entry<T>((T) it.key(), it.value()));
ordered.add(new Entry(it.key(), it.value()));
}
return new InternalMultiCountFacet<T>(name, valueType, comparatorType, requiredSize, ordered);
return new InternalTermsFacet(name, comparatorType, requiredSize, ordered);
}
@Override public void toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startArray(name());
for (Entry<T> entry : entries) {
builder.startObject(name);
builder.field("_type", "terms");
builder.startArray("terms");
for (Entry entry : entries) {
builder.startObject();
builder.field("value", entry.value());
builder.field("term", entry.term());
builder.field("count", entry.count());
builder.endObject();
}
builder.endArray();
builder.endObject();
}
public static InternalMultiCountFacet readMultiCountFacet(StreamInput in) throws IOException {
InternalMultiCountFacet facet = new InternalMultiCountFacet();
public static InternalTermsFacet readTermsFacet(StreamInput in) throws IOException {
InternalTermsFacet facet = new InternalTermsFacet();
facet.readFrom(in);
return facet;
}
@Override public void readFrom(StreamInput in) throws IOException {
name = in.readUTF();
valueType = ValueType.fromId(in.readByte());
comparatorType = ComparatorType.fromId(in.readByte());
requiredSize = in.readVInt();
int size = in.readVInt();
entries = new ArrayList<Entry<T>>(size);
entries = new ArrayList<Entry>(size);
for (int i = 0; i < size; i++) {
Object value = null;
if (valueType == ValueType.STRING) {
value = in.readUTF();
} else if (valueType == ValueType.SHORT) {
value = in.readShort();
} else if (valueType == ValueType.INT) {
value = in.readInt();
} else if (valueType == ValueType.LONG) {
value = in.readLong();
} else if (valueType == ValueType.FLOAT) {
value = in.readFloat();
} else if (valueType == ValueType.DOUBLE) {
value = in.readDouble();
}
entries.add(new Entry<T>((T) value, in.readVInt()));
entries.add(new Entry(in.readUTF(), in.readVInt()));
}
}
@Override public void writeTo(StreamOutput out) throws IOException {
out.writeUTF(name);
out.writeByte(valueType.id());
out.writeByte(comparatorType.id());
out.writeVInt(requiredSize);
out.writeVInt(entries.size());
for (Entry<T> entry : entries) {
if (valueType == ValueType.STRING) {
out.writeUTF((String) entry.value());
} else if (valueType == ValueType.SHORT) {
out.writeShort((Short) entry.value());
} else if (valueType == ValueType.INT) {
out.writeInt((Integer) entry.value());
} else if (valueType == ValueType.LONG) {
out.writeLong((Long) entry.value());
} else if (valueType == ValueType.FLOAT) {
out.writeFloat((Float) entry.value());
} else if (valueType == ValueType.DOUBLE) {
out.writeDouble((Double) entry.value());
}
for (Entry entry : entries) {
out.writeUTF(entry.term());
out.writeVInt(entry.count());
}
}
}
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
package org.elasticsearch.search.facets.collector.term;
package org.elasticsearch.search.facets.terms;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Scorer;
@ -26,9 +26,7 @@ import org.elasticsearch.index.field.FieldData;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.search.facets.Facet;
import org.elasticsearch.search.facets.MultiCountFacet;
import org.elasticsearch.search.facets.collector.FacetCollector;
import org.elasticsearch.search.facets.internal.InternalMultiCountFacet;
import org.elasticsearch.util.BoundedTreeSet;
import org.elasticsearch.util.ThreadLocals;
import org.elasticsearch.util.collect.ImmutableList;
@ -104,15 +102,15 @@ public class TermFacetCollector extends FacetCollector {
TObjectIntHashMap<String> facets = aggregator.facets();
if (facets.isEmpty()) {
pushFacets(facets);
return new InternalMultiCountFacet<String>(name, MultiCountFacet.ValueType.STRING, MultiCountFacet.ComparatorType.COUNT, size, ImmutableList.<MultiCountFacet.Entry<String>>of());
return new InternalTermsFacet(name, InternalTermsFacet.ComparatorType.COUNT, size, ImmutableList.<InternalTermsFacet.Entry>of());
} else {
BoundedTreeSet<MultiCountFacet.Entry<String>> ordered = new BoundedTreeSet<MultiCountFacet.Entry<String>>(MultiCountFacet.ComparatorType.COUNT.comparator(), size);
BoundedTreeSet<InternalTermsFacet.Entry> ordered = new BoundedTreeSet<InternalTermsFacet.Entry>(InternalTermsFacet.ComparatorType.COUNT.comparator(), size);
for (TObjectIntIterator<String> it = facets.iterator(); it.hasNext();) {
it.advance();
ordered.add(new MultiCountFacet.Entry<String>(it.key(), it.value()));
ordered.add(new InternalTermsFacet.Entry(it.key(), it.value()));
}
pushFacets(facets);
return new InternalMultiCountFacet<String>(name, MultiCountFacet.ValueType.STRING, MultiCountFacet.ComparatorType.COUNT, size, ordered);
return new InternalTermsFacet(name, InternalTermsFacet.ComparatorType.COUNT, size, ordered);
}
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
package org.elasticsearch.search.facets.collector.term;
package org.elasticsearch.search.facets.terms;
import org.elasticsearch.search.facets.collector.FacetCollector;
import org.elasticsearch.search.facets.collector.FacetCollectorParser;
@ -31,7 +31,7 @@ import java.io.IOException;
*/
public class TermFacetCollectorParser implements FacetCollectorParser {
public static final String NAME = "term";
public static final String NAME = "terms";
@Override public String name() {
return NAME;

View File

@ -17,9 +17,10 @@
* under the License.
*/
package org.elasticsearch.search.facets;
package org.elasticsearch.search.facets.terms;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.search.facets.Facet;
import java.util.Comparator;
import java.util.List;
@ -27,7 +28,7 @@ import java.util.List;
/**
* @author kimchy (Shay Banon)
*/
public interface MultiCountFacet<T extends Comparable> extends Facet, Iterable<MultiCountFacet.Entry<T>> {
public interface TermsFacet extends Facet, Iterable<TermsFacet.Entry> {
public static enum ComparatorType {
COUNT((byte) 0, new Comparator<Entry>() {
@ -35,7 +36,7 @@ public interface MultiCountFacet<T extends Comparable> extends Facet, Iterable<M
@Override public int compare(Entry o1, Entry o2) {
int i = o2.count() - o1.count();
if (i == 0) {
i = o2.value().compareTo(o1.value());
i = o2.term().compareTo(o1.term());
if (i == 0) {
i = System.identityHashCode(o2) - System.identityHashCode(o1);
}
@ -43,10 +44,10 @@ public interface MultiCountFacet<T extends Comparable> extends Facet, Iterable<M
return i;
}
}),
VALUE((byte) 1, new Comparator<Entry>() {
TERM((byte) 1, new Comparator<Entry>() {
@Override public int compare(Entry o1, Entry o2) {
int i = o2.value().compareTo(o1.value());
int i = o2.term().compareTo(o1.term());
if (i == 0) {
i = o2.count() - o1.count();
if (i == 0) {
@ -78,83 +79,28 @@ public interface MultiCountFacet<T extends Comparable> extends Facet, Iterable<M
if (id == 0) {
return COUNT;
} else if (id == 1) {
return VALUE;
return TERM;
}
throw new ElasticSearchIllegalArgumentException("No type argument match for multi count comparator [" + id + "]");
}
}
public static enum ValueType {
STRING((byte) 0),
SHORT((byte) 1),
INT((byte) 2),
LONG((byte) 3),
FLOAT((byte) 4),
DOUBLE((byte) 5);
public class Entry {
private final byte id;
ValueType(byte id) {
this.id = id;
}
public byte id() {
return id;
}
public static ValueType fromId(byte id) {
if (id == 0) {
return STRING;
} else if (id == 1) {
return SHORT;
} else if (id == 2) {
return INT;
} else if (id == 3) {
return LONG;
} else if (id == 4) {
return FLOAT;
} else if (id == 5) {
return DOUBLE;
}
throw new ElasticSearchIllegalArgumentException("No type argument match for multi count facet [" + id + "]");
}
}
public class Entry<T extends Comparable> {
private T value;
private String term;
private int count;
public Entry(T value, int count) {
this.value = value;
public Entry(String term, int count) {
this.term = term;
this.count = count;
}
public T value() {
return value;
public String term() {
return term;
}
public T getValue() {
return value;
}
public String valueAsString() {
return value.toString();
}
public String getValueAsString() {
return valueAsString();
}
public Number valueAsNumber() {
if (value instanceof Number) {
return (Number) value;
}
return null;
}
public Number getValueAsNumber() {
return valueAsNumber();
public String getTerm() {
return term;
}
public int count() {
@ -166,12 +112,7 @@ public interface MultiCountFacet<T extends Comparable> extends Facet, Iterable<M
}
}
List<Entry> entries();
ValueType valueType();
ValueType getValueType();
List<Entry<T>> entries();
List<Entry<T>> getEntries();
}
List<Entry> getEntries();
}

View File

@ -30,6 +30,7 @@ import org.elasticsearch.search.controller.SearchPhaseController;
import org.elasticsearch.search.controller.ShardDoc;
import org.elasticsearch.search.dfs.AggregatedDfs;
import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.search.fetch.FetchSearchRequest;
import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.QueryFetchSearchResult;
@ -150,8 +151,8 @@ public class SingleInstanceEmbeddedSearchTests extends AbstractNodesTests {
searchSource().query(wildcardQuery("name", "te*"))
.facets(facets().queryFacet("age2", termQuery("age", 2)).queryFacet("age1", termQuery("age", 1)))
));
assertThat(queryResult.facets().countFacet("age2").count(), equalTo(4l));
assertThat(queryResult.facets().countFacet("age1").count(), equalTo(1l));
assertThat(queryResult.facets().facet(QueryFacet.class, "age2").count(), equalTo(4l));
assertThat(queryResult.facets().facet(QueryFacet.class, "age1").count(), equalTo(1l));
}
@Test public void testQueryFetchKeepAliveTimeout() throws Exception {

View File

@ -27,6 +27,7 @@ import org.elasticsearch.client.Requests;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.collect.Sets;
@ -253,14 +254,14 @@ public class TransportTwoServersSearchTests extends AbstractNodesTests {
SearchSourceBuilder sourceBuilder = searchSource()
.query(termQuery("multi", "test"))
.from(0).size(20).explain(true)
.facets(facets().queryFacet("all", termQuery("multi", "test"), true).queryFacet("test1", termQuery("name", "test1")));
.facets(facets().queryFacetGlobal("all", termQuery("multi", "test")).queryFacet("test1", termQuery("name", "test1")));
SearchResponse searchResponse = client.search(searchRequest("test").source(sourceBuilder)).actionGet();
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
assertThat(searchResponse.hits().totalHits(), equalTo(100l));
assertThat(searchResponse.facets().countFacet("test1").count(), equalTo(1l));
assertThat(searchResponse.facets().countFacet("all").count(), equalTo(100l));
assertThat(searchResponse.facets().facet(QueryFacet.class, "test1").count(), equalTo(1l));
assertThat(searchResponse.facets().facet(QueryFacet.class, "all").count(), equalTo(100l));
}
@Test public void testSimpleFacetsTwice() throws Exception {

View File

@ -32,6 +32,7 @@ import org.elasticsearch.search.controller.SearchPhaseController;
import org.elasticsearch.search.controller.ShardDoc;
import org.elasticsearch.search.dfs.AggregatedDfs;
import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.search.fetch.FetchSearchRequest;
import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.QueryFetchSearchResult;
@ -344,8 +345,8 @@ public class TwoInstanceEmbeddedSearchTests extends AbstractNodesTests {
InternalSearchResponse searchResponse = searchPhaseController.merge(sortedShardList, queryResults, fetchResults);
assertThat(searchResponse.hits().totalHits(), equalTo(100l));
assertThat(searchResponse.facets().countFacet("test1").count(), equalTo(1l));
assertThat(searchResponse.facets().countFacet("all").count(), equalTo(100l));
assertThat(searchResponse.facets().facet(QueryFacet.class, "test1").count(), equalTo(1l));
assertThat(searchResponse.facets().facet(QueryFacet.class, "all").count(), equalTo(100l));
}
@Test public void testSimpleFacetsTwice() {

View File

@ -36,6 +36,7 @@ import org.elasticsearch.search.controller.SearchPhaseController;
import org.elasticsearch.search.controller.ShardDoc;
import org.elasticsearch.search.dfs.AggregatedDfs;
import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.search.fetch.FetchSearchRequest;
import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.QueryFetchSearchResult;
@ -350,8 +351,8 @@ public class TwoInstanceUnbalancedShardsEmbeddedSearchTests extends AbstractNode
InternalSearchResponse searchResponse = searchPhaseController.merge(sortedShardList, queryResults, fetchResults);
assertThat(searchResponse.hits().totalHits(), equalTo(100l));
assertThat(searchResponse.facets().countFacet("test1").count(), equalTo(1l));
assertThat(searchResponse.facets().countFacet("all").count(), equalTo(100l));
assertThat(searchResponse.facets().facet(QueryFacet.class, "test1").count(), equalTo(1l));
assertThat(searchResponse.facets().facet(QueryFacet.class, "all").count(), equalTo(100l));
}
@Test public void testSimpleFacetsTwice() {

View File

@ -21,15 +21,15 @@ package org.elasticsearch.test.integration.search.facets;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.search.facets.MultiCountFacet;
import org.elasticsearch.search.facets.terms.TermsFacet;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.hamcrest.MatcherAssert;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
import static org.elasticsearch.util.xcontent.XContentFactory.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
/**
@ -72,24 +72,25 @@ public class SimpleFacetsTests extends AbstractNodesTests {
.field("stag", "111")
.startArray("tag").value("zzz").value("yyy").endArray()
.endObject()).execute().actionGet();
client.admin().indices().prepareRefresh().execute().actionGet();
SearchResponse searchResponse = client.prepareSearch()
.setQuery(termQuery("stag", "111"))
.addTermFacet("facet1", "stag", 10)
.addTermFacet("facet2", "tag", 10)
.addFacetTerms("facet1", "stag", 10)
.addFacetTerms("facet2", "tag", 10)
.execute().actionGet();
MultiCountFacet<String> facet = (MultiCountFacet<String>) searchResponse.facets().facet("facet1");
MatcherAssert.assertThat(facet.name(), equalTo("facet1"));
MatcherAssert.assertThat(facet.entries().size(), equalTo(1));
MatcherAssert.assertThat(facet.entries().get(0).value(), equalTo("111"));
MatcherAssert.assertThat(facet.entries().get(0).count(), equalTo(2));
TermsFacet facet = searchResponse.facets().facet(TermsFacet.class, "facet1");
assertThat(facet.name(), equalTo("facet1"));
assertThat(facet.entries().size(), equalTo(1));
assertThat(facet.entries().get(0).term(), equalTo("111"));
assertThat(facet.entries().get(0).count(), equalTo(2));
facet = (MultiCountFacet<String>) searchResponse.facets().facet("facet2");
MatcherAssert.assertThat(facet.name(), equalTo("facet2"));
MatcherAssert.assertThat(facet.entries().size(), equalTo(3));
MatcherAssert.assertThat(facet.entries().get(0).value(), equalTo("yyy"));
MatcherAssert.assertThat(facet.entries().get(0).count(), equalTo(2));
facet = searchResponse.facets().facet(TermsFacet.class, "facet2");
assertThat(facet.name(), equalTo("facet2"));
assertThat(facet.entries().size(), equalTo(3));
assertThat(facet.entries().get(0).term(), equalTo("yyy"));
assertThat(facet.entries().get(0).count(), equalTo(2));
}
}