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 name The logical name of the facet, it will be returned under the name
* @param query The query facet * @param query The query facet
*/ */
public SearchRequestBuilder addQueryFacet(String name, XContentQueryBuilder query) { public SearchRequestBuilder addFacetQuery(String name, XContentQueryBuilder query) {
facetsBuilder().queryFacet(name, query); facetsBuilder().queryFacet(name, query);
return this; return this;
} }
@ -265,20 +265,19 @@ public class SearchRequestBuilder {
* *
* @param name The logical name of the facet, it will be returned under the name * @param name The logical name of the facet, it will be returned under the name
* @param query The query facet * @param query The query facet
* @param global Should the facet be executed globally or not
*/ */
public SearchRequestBuilder addQueryFacet(String name, XContentQueryBuilder query, boolean global) { public SearchRequestBuilder addFacetGlobalQuery(String name, XContentQueryBuilder query) {
facetsBuilder().queryFacet(name, query, global); facetsBuilder().queryFacetGlobal(name, query);
return this; return this;
} }
public SearchRequestBuilder addTermFacet(String name, String fieldName, int size) { public SearchRequestBuilder addFacetTerms(String name, String fieldName, int size) {
facetsBuilder().termFacet(name, fieldName, size); facetsBuilder().termsFacet(name, fieldName, size);
return this; return this;
} }
public SearchRequestBuilder addTermFacet(String name, String fieldName, int size, boolean global) { public SearchRequestBuilder addFacetGlobalTerms(String name, String fieldName, int size) {
facetsBuilder().termFacet(name, fieldName, size, global); facetsBuilder().termsFacetGlobal(name, fieldName, size);
return this; return this;
} }

View File

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

View File

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

View File

@ -19,7 +19,8 @@
package org.elasticsearch.search.facets; 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.collect.ImmutableList;
import org.elasticsearch.util.io.stream.StreamInput; import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput; import org.elasticsearch.util.io.stream.StreamOutput;
@ -32,8 +33,6 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; 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.Lists.*;
import static org.elasticsearch.util.collect.Maps.*; 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) { public <T extends Facet> T facet(Class<T> facetType, String name) {
return (CountFacet) facet(name); return facetType.cast(facet(name));
} }
/** /**
@ -132,11 +131,11 @@ public class Facets implements Streamable, ToXContent, Iterable<Facet> {
} else { } else {
facets = newArrayListWithCapacity(size); facets = newArrayListWithCapacity(size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
byte id = in.readByte(); int id = in.readVInt();
if (id == Facet.Type.COUNT.id()) { if (id == Facet.Type.TERMS.id()) {
facets.add(readCountFacet(in)); facets.add(InternalTermsFacet.readTermsFacet(in));
} else if (id == Facet.Type.MULTI_COUNT.id()) { } else if (id == Facet.Type.QUERY.id()) {
facets.add(readMultiCountFacet(in)); facets.add(InternalQueryFacet.readCountFacet(in));
} else { } else {
throw new IOException("Can't handle facet type with id [" + id + "]"); 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 { @Override public void writeTo(StreamOutput out) throws IOException {
out.writeVInt(facets.size()); out.writeVInt(facets.size());
for (Facet facet : facets) { for (Facet facet : facets) {
out.writeByte(facet.type().id()); out.writeVInt(facet.type().id());
((InternalFacet) facet).writeTo(out); ((InternalFacet) facet).writeTo(out);
} }
} }

View File

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

View File

@ -17,9 +17,8 @@
* under the License. * 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.io.stream.Streamable;
import org.elasticsearch.util.xcontent.ToXContent; import org.elasticsearch.util.xcontent.ToXContent;

View File

@ -17,10 +17,10 @@
* under the License. * 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.Facet;
import org.elasticsearch.search.facets.InternalFacet;
import org.elasticsearch.util.io.stream.StreamInput; import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput; import org.elasticsearch.util.io.stream.StreamOutput;
import org.elasticsearch.util.xcontent.builder.XContentBuilder; import org.elasticsearch.util.xcontent.builder.XContentBuilder;
@ -30,23 +30,23 @@ import java.io.IOException;
/** /**
* @author kimchy (Shay Banon) * @author kimchy (Shay Banon)
*/ */
public class InternalCountFacet implements CountFacet, InternalFacet { public class InternalQueryFacet implements QueryFacet, InternalFacet {
private String name; private String name;
private long count; private long count;
private InternalCountFacet() { private InternalQueryFacet() {
} }
public InternalCountFacet(String name, long count) { public InternalQueryFacet(String name, long count) {
this.name = name; this.name = name;
this.count = count; this.count = count;
} }
@Override public Type type() { @Override public Type type() {
return Type.COUNT; return Type.QUERY;
} }
@Override public Type getType() { @Override public Type getType() {
@ -86,18 +86,21 @@ public class InternalCountFacet implements CountFacet, InternalFacet {
int count = 0; int count = 0;
for (Facet facet : facets) { for (Facet facet : facets) {
if (facet.name().equals(name)) { 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 { @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 { public static QueryFacet readCountFacet(StreamInput in) throws IOException {
InternalCountFacet result = new InternalCountFacet(); InternalQueryFacet result = new InternalQueryFacet();
result.readFrom(in); result.readFrom(in);
return result; return result;
} }
@ -112,4 +115,3 @@ public class InternalCountFacet implements CountFacet, InternalFacet {
out.writeVLong(count); out.writeVLong(count);
} }
} }

View File

@ -17,14 +17,16 @@
* under the License. * 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) * @author kimchy (shay.banon)
*/ */
public interface CountFacet extends Facet { public interface QueryFacet extends Facet {
/** /**
* The count of the facet. * The count of the facet.

View File

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

View File

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

View File

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

View File

@ -17,7 +17,7 @@
* under the License. * 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.index.IndexReader;
import org.apache.lucene.search.Scorer; 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.FieldMapper;
import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.search.facets.Facet; import org.elasticsearch.search.facets.Facet;
import org.elasticsearch.search.facets.MultiCountFacet;
import org.elasticsearch.search.facets.collector.FacetCollector; import org.elasticsearch.search.facets.collector.FacetCollector;
import org.elasticsearch.search.facets.internal.InternalMultiCountFacet;
import org.elasticsearch.util.BoundedTreeSet; import org.elasticsearch.util.BoundedTreeSet;
import org.elasticsearch.util.ThreadLocals; import org.elasticsearch.util.ThreadLocals;
import org.elasticsearch.util.collect.ImmutableList; import org.elasticsearch.util.collect.ImmutableList;
@ -104,15 +102,15 @@ public class TermFacetCollector extends FacetCollector {
TObjectIntHashMap<String> facets = aggregator.facets(); TObjectIntHashMap<String> facets = aggregator.facets();
if (facets.isEmpty()) { if (facets.isEmpty()) {
pushFacets(facets); 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 { } 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();) { for (TObjectIntIterator<String> it = facets.iterator(); it.hasNext();) {
it.advance(); it.advance();
ordered.add(new MultiCountFacet.Entry<String>(it.key(), it.value())); ordered.add(new InternalTermsFacet.Entry(it.key(), it.value()));
} }
pushFacets(facets); 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. * 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.FacetCollector;
import org.elasticsearch.search.facets.collector.FacetCollectorParser; import org.elasticsearch.search.facets.collector.FacetCollectorParser;
@ -31,7 +31,7 @@ import java.io.IOException;
*/ */
public class TermFacetCollectorParser implements FacetCollectorParser { public class TermFacetCollectorParser implements FacetCollectorParser {
public static final String NAME = "term"; public static final String NAME = "terms";
@Override public String name() { @Override public String name() {
return NAME; return NAME;

View File

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

View File

@ -30,6 +30,7 @@ import org.elasticsearch.search.controller.SearchPhaseController;
import org.elasticsearch.search.controller.ShardDoc; import org.elasticsearch.search.controller.ShardDoc;
import org.elasticsearch.search.dfs.AggregatedDfs; import org.elasticsearch.search.dfs.AggregatedDfs;
import org.elasticsearch.search.dfs.DfsSearchResult; import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.search.fetch.FetchSearchRequest; import org.elasticsearch.search.fetch.FetchSearchRequest;
import org.elasticsearch.search.fetch.FetchSearchResult; import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.QueryFetchSearchResult; import org.elasticsearch.search.fetch.QueryFetchSearchResult;
@ -150,8 +151,8 @@ public class SingleInstanceEmbeddedSearchTests extends AbstractNodesTests {
searchSource().query(wildcardQuery("name", "te*")) searchSource().query(wildcardQuery("name", "te*"))
.facets(facets().queryFacet("age2", termQuery("age", 2)).queryFacet("age1", termQuery("age", 1))) .facets(facets().queryFacet("age2", termQuery("age", 2)).queryFacet("age1", termQuery("age", 1)))
)); ));
assertThat(queryResult.facets().countFacet("age2").count(), equalTo(4l)); assertThat(queryResult.facets().facet(QueryFacet.class, "age2").count(), equalTo(4l));
assertThat(queryResult.facets().countFacet("age1").count(), equalTo(1l)); assertThat(queryResult.facets().facet(QueryFacet.class, "age1").count(), equalTo(1l));
} }
@Test public void testQueryFetchKeepAliveTimeout() throws Exception { @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.Scroll;
import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.test.integration.AbstractNodesTests; import org.elasticsearch.test.integration.AbstractNodesTests;
import org.elasticsearch.util.Unicode; import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.collect.Sets; import org.elasticsearch.util.collect.Sets;
@ -253,14 +254,14 @@ public class TransportTwoServersSearchTests extends AbstractNodesTests {
SearchSourceBuilder sourceBuilder = searchSource() SearchSourceBuilder sourceBuilder = searchSource()
.query(termQuery("multi", "test")) .query(termQuery("multi", "test"))
.from(0).size(20).explain(true) .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(); SearchResponse searchResponse = client.search(searchRequest("test").source(sourceBuilder)).actionGet();
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0)); assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
assertThat(searchResponse.hits().totalHits(), equalTo(100l)); assertThat(searchResponse.hits().totalHits(), equalTo(100l));
assertThat(searchResponse.facets().countFacet("test1").count(), equalTo(1l)); assertThat(searchResponse.facets().facet(QueryFacet.class, "test1").count(), equalTo(1l));
assertThat(searchResponse.facets().countFacet("all").count(), equalTo(100l)); assertThat(searchResponse.facets().facet(QueryFacet.class, "all").count(), equalTo(100l));
} }
@Test public void testSimpleFacetsTwice() throws Exception { @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.controller.ShardDoc;
import org.elasticsearch.search.dfs.AggregatedDfs; import org.elasticsearch.search.dfs.AggregatedDfs;
import org.elasticsearch.search.dfs.DfsSearchResult; import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.search.fetch.FetchSearchRequest; import org.elasticsearch.search.fetch.FetchSearchRequest;
import org.elasticsearch.search.fetch.FetchSearchResult; import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.QueryFetchSearchResult; import org.elasticsearch.search.fetch.QueryFetchSearchResult;
@ -344,8 +345,8 @@ public class TwoInstanceEmbeddedSearchTests extends AbstractNodesTests {
InternalSearchResponse searchResponse = searchPhaseController.merge(sortedShardList, queryResults, fetchResults); InternalSearchResponse searchResponse = searchPhaseController.merge(sortedShardList, queryResults, fetchResults);
assertThat(searchResponse.hits().totalHits(), equalTo(100l)); assertThat(searchResponse.hits().totalHits(), equalTo(100l));
assertThat(searchResponse.facets().countFacet("test1").count(), equalTo(1l)); assertThat(searchResponse.facets().facet(QueryFacet.class, "test1").count(), equalTo(1l));
assertThat(searchResponse.facets().countFacet("all").count(), equalTo(100l)); assertThat(searchResponse.facets().facet(QueryFacet.class, "all").count(), equalTo(100l));
} }
@Test public void testSimpleFacetsTwice() { @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.controller.ShardDoc;
import org.elasticsearch.search.dfs.AggregatedDfs; import org.elasticsearch.search.dfs.AggregatedDfs;
import org.elasticsearch.search.dfs.DfsSearchResult; import org.elasticsearch.search.dfs.DfsSearchResult;
import org.elasticsearch.search.facets.query.QueryFacet;
import org.elasticsearch.search.fetch.FetchSearchRequest; import org.elasticsearch.search.fetch.FetchSearchRequest;
import org.elasticsearch.search.fetch.FetchSearchResult; import org.elasticsearch.search.fetch.FetchSearchResult;
import org.elasticsearch.search.fetch.QueryFetchSearchResult; import org.elasticsearch.search.fetch.QueryFetchSearchResult;
@ -350,8 +351,8 @@ public class TwoInstanceUnbalancedShardsEmbeddedSearchTests extends AbstractNode
InternalSearchResponse searchResponse = searchPhaseController.merge(sortedShardList, queryResults, fetchResults); InternalSearchResponse searchResponse = searchPhaseController.merge(sortedShardList, queryResults, fetchResults);
assertThat(searchResponse.hits().totalHits(), equalTo(100l)); assertThat(searchResponse.hits().totalHits(), equalTo(100l));
assertThat(searchResponse.facets().countFacet("test1").count(), equalTo(1l)); assertThat(searchResponse.facets().facet(QueryFacet.class, "test1").count(), equalTo(1l));
assertThat(searchResponse.facets().countFacet("all").count(), equalTo(100l)); assertThat(searchResponse.facets().facet(QueryFacet.class, "all").count(), equalTo(100l));
} }
@Test public void testSimpleFacetsTwice() { @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.action.search.SearchResponse;
import org.elasticsearch.client.Client; 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.elasticsearch.test.integration.AbstractNodesTests;
import org.hamcrest.MatcherAssert;
import org.testng.annotations.AfterClass; import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*; import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
import static org.elasticsearch.util.xcontent.XContentFactory.*; import static org.elasticsearch.util.xcontent.XContentFactory.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
/** /**
@ -72,24 +72,25 @@ public class SimpleFacetsTests extends AbstractNodesTests {
.field("stag", "111") .field("stag", "111")
.startArray("tag").value("zzz").value("yyy").endArray() .startArray("tag").value("zzz").value("yyy").endArray()
.endObject()).execute().actionGet(); .endObject()).execute().actionGet();
client.admin().indices().prepareRefresh().execute().actionGet(); client.admin().indices().prepareRefresh().execute().actionGet();
SearchResponse searchResponse = client.prepareSearch() SearchResponse searchResponse = client.prepareSearch()
.setQuery(termQuery("stag", "111")) .setQuery(termQuery("stag", "111"))
.addTermFacet("facet1", "stag", 10) .addFacetTerms("facet1", "stag", 10)
.addTermFacet("facet2", "tag", 10) .addFacetTerms("facet2", "tag", 10)
.execute().actionGet(); .execute().actionGet();
MultiCountFacet<String> facet = (MultiCountFacet<String>) searchResponse.facets().facet("facet1"); TermsFacet facet = searchResponse.facets().facet(TermsFacet.class, "facet1");
MatcherAssert.assertThat(facet.name(), equalTo("facet1")); assertThat(facet.name(), equalTo("facet1"));
MatcherAssert.assertThat(facet.entries().size(), equalTo(1)); assertThat(facet.entries().size(), equalTo(1));
MatcherAssert.assertThat(facet.entries().get(0).value(), equalTo("111")); assertThat(facet.entries().get(0).term(), equalTo("111"));
MatcherAssert.assertThat(facet.entries().get(0).count(), equalTo(2)); assertThat(facet.entries().get(0).count(), equalTo(2));
facet = (MultiCountFacet<String>) searchResponse.facets().facet("facet2"); facet = searchResponse.facets().facet(TermsFacet.class, "facet2");
MatcherAssert.assertThat(facet.name(), equalTo("facet2")); assertThat(facet.name(), equalTo("facet2"));
MatcherAssert.assertThat(facet.entries().size(), equalTo(3)); assertThat(facet.entries().size(), equalTo(3));
MatcherAssert.assertThat(facet.entries().get(0).value(), equalTo("yyy")); assertThat(facet.entries().get(0).term(), equalTo("yyy"));
MatcherAssert.assertThat(facet.entries().get(0).count(), equalTo(2)); assertThat(facet.entries().get(0).count(), equalTo(2));
} }
} }