From dc6ef326d91993e3c6922e931f8383f2f238efd0 Mon Sep 17 00:00:00 2001 From: kimchy Date: Mon, 2 Aug 2010 19:17:57 +0300 Subject: [PATCH] Facets: Filter based facet, closes #289. --- .../search/facets/AbstractFacetBuilder.java | 20 +++- .../elasticsearch/search/facets/Facet.java | 7 +- .../search/facets/FacetBuilders.java | 10 ++ .../search/facets/FacetsParseElement.java | 6 +- .../collector/FacetCollectorParser.java | 2 +- .../search/facets/filter/FilterFacet.java | 40 +++++++ .../facets/filter/FilterFacetBuilder.java | 73 +++++++++++ .../facets/filter/FilterFacetCollector.java | 61 ++++++++++ .../filter/FilterFacetCollectorParser.java | 47 ++++++++ .../facets/filter/InternalFilterFacet.java | 113 ++++++++++++++++++ .../geodistance/GeoDistanceFacetBuilder.java | 13 +- .../GeoDistanceFacetCollectorParser.java | 2 +- .../histogram/HistogramFacetBuilder.java | 13 +- .../HistogramFacetCollectorParser.java | 2 +- .../HistogramScriptFacetBuilder.java | 13 +- .../facets/internal/InternalFacets.java | 3 + .../facets/query/InternalQueryFacet.java | 6 +- .../facets/query/QueryFacetBuilder.java | 12 +- .../query/QueryFacetCollectorParser.java | 2 +- .../facets/range/RangeFacetBuilder.java | 13 +- .../range/RangeFacetCollectorParser.java | 2 +- .../facets/range/RangeScriptFacetBuilder.java | 13 +- .../statistical/StatisticalFacetBuilder.java | 13 +- .../StatisticalFacetCollectorParser.java | 2 +- .../StatisticalScriptFacetBuilder.java | 13 +- .../facets/terms/TermsFacetBuilder.java | 12 +- .../terms/TermsFacetCollectorParser.java | 2 +- .../search/facets/SimpleFacetsTests.java | 37 +++++- 28 files changed, 445 insertions(+), 107 deletions(-) create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacet.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetBuilder.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollector.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollectorParser.java create mode 100644 modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/InternalFilterFacet.java diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/AbstractFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/AbstractFacetBuilder.java index aea6e2d4201..7ccef55378f 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/AbstractFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/AbstractFacetBuilder.java @@ -20,8 +20,11 @@ package org.elasticsearch.search.facets; import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.builder.XContentBuilder; import org.elasticsearch.index.query.xcontent.XContentFilterBuilder; +import java.io.IOException; + /** * @author kimchy (shay.banon) */ @@ -31,14 +34,14 @@ public abstract class AbstractFacetBuilder implements ToXContent { protected Boolean global; - protected XContentFilterBuilder filter; + protected XContentFilterBuilder facetFilter; protected AbstractFacetBuilder(String name) { this.name = name; } - public AbstractFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public AbstractFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -46,4 +49,15 @@ public abstract class AbstractFacetBuilder implements ToXContent { this.global = global; return this; } + + protected void addFilterFacetAndGlobal(XContentBuilder builder, Params params) throws IOException { + if (facetFilter != null) { + builder.field("facet_filter"); + facetFilter.toXContent(builder, params); + } + + if (global != null) { + builder.field("global", global); + } + } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/Facet.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/Facet.java index 21d1f2c3369..93a03e02ae8 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/Facet.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/Facet.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.facets; import org.elasticsearch.ElasticSearchIllegalArgumentException; +import org.elasticsearch.search.facets.filter.FilterFacet; import org.elasticsearch.search.facets.geodistance.GeoDistanceFacet; import org.elasticsearch.search.facets.histogram.HistogramFacet; import org.elasticsearch.search.facets.query.QueryFacet; @@ -61,7 +62,11 @@ public interface Facet { /** * Geo Distance facet type, matching {@link RangeFacet}. */ - RANGE(5, RangeFacet.class); + RANGE(5, RangeFacet.class), + /** + * Filter facet type, matching {@link FilterFacet}. + */ + FILTER(6, FilterFacet.class); private int id; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetBuilders.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetBuilders.java index 7427e724ca8..dc1c4864c4b 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetBuilders.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetBuilders.java @@ -19,7 +19,9 @@ package org.elasticsearch.search.facets; +import org.elasticsearch.index.query.xcontent.XContentFilterBuilder; import org.elasticsearch.index.query.xcontent.XContentQueryBuilder; +import org.elasticsearch.search.facets.filter.FilterFacetBuilder; import org.elasticsearch.search.facets.geodistance.GeoDistanceFacetBuilder; import org.elasticsearch.search.facets.histogram.HistogramFacetBuilder; import org.elasticsearch.search.facets.histogram.HistogramScriptFacetBuilder; @@ -43,6 +45,14 @@ public class FacetBuilders { return new QueryFacetBuilder(facetName).query(query); } + public static FilterFacetBuilder filterFacet(String facetName) { + return new FilterFacetBuilder(facetName); + } + + public static FilterFacetBuilder filterFacet(String facetName, XContentFilterBuilder filter) { + return new FilterFacetBuilder(facetName).filter(filter); + } + public static TermsFacetBuilder termsFacet(String facetName) { return new TermsFacetBuilder(facetName); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetsParseElement.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetsParseElement.java index 1bdc7b6f7d8..2ee959d6d04 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetsParseElement.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/FacetsParseElement.java @@ -29,6 +29,7 @@ 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.filter.FilterFacetCollectorParser; import org.elasticsearch.search.facets.geodistance.GeoDistanceFacetCollectorParser; import org.elasticsearch.search.facets.histogram.HistogramFacetCollectorParser; import org.elasticsearch.search.facets.query.QueryFacetCollectorParser; @@ -72,6 +73,7 @@ public class FacetsParseElement implements SearchParseElement { addFacetParser(builder, new HistogramFacetCollectorParser()); addFacetParser(builder, new GeoDistanceFacetCollectorParser()); addFacetParser(builder, new RangeFacetCollectorParser()); + addFacetParser(builder, new FilterFacetCollectorParser()); this.facetCollectorParsers = builder.immutableMap(); } @@ -100,7 +102,7 @@ public class FacetsParseElement implements SearchParseElement { if (token == XContentParser.Token.FIELD_NAME) { facetFieldName = parser.currentName(); } else if (token == XContentParser.Token.START_OBJECT) { - if ("filter".equals(facetFieldName)) { + if ("facet_filter".equals(facetFieldName) || "facetFilter".equals(facetFieldName)) { XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser(); filter = indexQueryParser.parseInnerFilter(parser); } else { @@ -108,7 +110,7 @@ public class FacetsParseElement implements SearchParseElement { if (facetCollectorParser == null) { throw new SearchParseException(context, "No facet type for [" + facetFieldName + "]"); } - facet = facetCollectorParser.parser(topLevelFieldName, parser, context); + facet = facetCollectorParser.parse(topLevelFieldName, parser, context); } } else if (token.isValue()) { if ("global".equals(facetFieldName)) { diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/collector/FacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/collector/FacetCollectorParser.java index d08a716bae8..2dd4a602801 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/collector/FacetCollectorParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/collector/FacetCollectorParser.java @@ -31,5 +31,5 @@ public interface FacetCollectorParser { String[] names(); - FacetCollector parser(String facetName, XContentParser parser, SearchContext context) throws IOException; + FacetCollector parse(String facetName, XContentParser parser, SearchContext context) throws IOException; } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacet.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacet.java new file mode 100644 index 00000000000..d587a9ca2b6 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacet.java @@ -0,0 +1,40 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search licenses this + * file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.facets.filter; + +import org.elasticsearch.search.facets.Facet; + +/** + * A query facets returns the count (number of hits) for a facet based on a query. + * + * @author kimchy (shay.banon) + */ +public interface FilterFacet extends Facet { + + /** + * The count of the facet. + */ + long count(); + + /** + * The count of the facet. + */ + long getCount(); +} \ No newline at end of file diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetBuilder.java new file mode 100644 index 00000000000..1fc03b3c24b --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetBuilder.java @@ -0,0 +1,73 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search licenses this + * file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.facets.filter; + +import org.elasticsearch.common.xcontent.builder.XContentBuilder; +import org.elasticsearch.index.query.xcontent.XContentFilterBuilder; +import org.elasticsearch.search.builder.SearchSourceBuilderException; +import org.elasticsearch.search.facets.AbstractFacetBuilder; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +public class FilterFacetBuilder extends AbstractFacetBuilder { + + private XContentFilterBuilder filter; + + public FilterFacetBuilder(String name) { + super(name); + } + + public FilterFacetBuilder global(boolean global) { + this.global = global; + return this; + } + + public FilterFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; + return this; + } + + public FilterFacetBuilder filter(XContentFilterBuilder filter) { + this.filter = filter; + return this; + } + + @Override public void toXContent(XContentBuilder builder, Params params) throws IOException { + if (filter == null) { + throw new SearchSourceBuilderException("filter must be set on filter facet for facet [" + name + "]"); + } + builder.startObject(name); + builder.field(FilterFacetCollectorParser.NAME); + filter.toXContent(builder, params); + + if (facetFilter != null) { + builder.field("filter"); + facetFilter.toXContent(builder, params); + } + + if (global != null) { + builder.field("global", global); + } + builder.endObject(); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollector.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollector.java new file mode 100644 index 00000000000..8d2ceac2820 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollector.java @@ -0,0 +1,61 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search licenses this + * file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.facets.filter; + +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.Filter; +import org.elasticsearch.common.lucene.docset.DocSet; +import org.elasticsearch.common.lucene.docset.DocSets; +import org.elasticsearch.index.cache.filter.FilterCache; +import org.elasticsearch.search.facets.Facet; +import org.elasticsearch.search.facets.support.AbstractFacetCollector; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +public class FilterFacetCollector extends AbstractFacetCollector { + + private final Filter filter; + + private DocSet docSet; + + private int count = 0; + + public FilterFacetCollector(String facetName, Filter filter, FilterCache filterCache) { + super(facetName); + this.filter = filterCache.cache(filter); + } + + @Override protected void doSetNextReader(IndexReader reader, int docBase) throws IOException { + docSet = DocSets.convert(reader, filter.getDocIdSet(reader)); + } + + @Override protected void doCollect(int doc) throws IOException { + if (docSet.get(doc)) { + count++; + } + } + + @Override public Facet facet() { + return new InternalFilterFacet(facetName, count); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollectorParser.java new file mode 100644 index 00000000000..b0ae86a59b2 --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/FilterFacetCollectorParser.java @@ -0,0 +1,47 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search licenses this + * file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.facets.filter; + +import org.apache.lucene.search.Filter; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.query.xcontent.XContentIndexQueryParser; +import org.elasticsearch.search.facets.collector.FacetCollector; +import org.elasticsearch.search.facets.collector.FacetCollectorParser; +import org.elasticsearch.search.internal.SearchContext; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +public class FilterFacetCollectorParser implements FacetCollectorParser { + + public static final String NAME = "filter"; + + @Override public String[] names() { + return new String[]{"filter"}; + } + + @Override public FacetCollector parse(String facetName, XContentParser parser, SearchContext context) throws IOException { + XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser(); + Filter facetFilter = indexQueryParser.parseInnerFilter(parser); + return new FilterFacetCollector(facetName, facetFilter, context.filterCache()); + } +} diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/InternalFilterFacet.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/InternalFilterFacet.java new file mode 100644 index 00000000000..ea7a525421f --- /dev/null +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/filter/InternalFilterFacet.java @@ -0,0 +1,113 @@ +/* + * Licensed to Elastic Search and Shay Banon under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. Elastic Search licenses this + * file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.facets.filter; + +import org.elasticsearch.common.io.stream.StreamInput; +import org.elasticsearch.common.io.stream.StreamOutput; +import org.elasticsearch.common.xcontent.builder.XContentBuilder; +import org.elasticsearch.search.facets.Facet; +import org.elasticsearch.search.facets.internal.InternalFacet; + +import java.io.IOException; + +/** + * @author kimchy (shay.banon) + */ +public class InternalFilterFacet implements FilterFacet, InternalFacet { + + private String name; + + private long count; + + private InternalFilterFacet() { + + } + + public InternalFilterFacet(String name, long count) { + this.name = name; + this.count = count; + } + + @Override public Type type() { + return Type.FILTER; + } + + @Override public Type getType() { + return type(); + } + + /** + * The "logical" name of the facet. + */ + public String name() { + return name; + } + + @Override public String getName() { + return name(); + } + + /** + * The count of the facet. + */ + public long count() { + return count; + } + + /** + * The count of the facet. + */ + public long getCount() { + return count; + } + + @Override public Facet aggregate(Iterable facets) { + int count = 0; + for (Facet facet : facets) { + if (facet.name().equals(name)) { + count += ((FilterFacet) facet).count(); + } + } + return new InternalFilterFacet(name, count); + } + + @Override public void toXContent(XContentBuilder builder, Params params) throws IOException { + builder.startObject(name); + builder.field("_type", FilterFacetCollectorParser.NAME); + builder.field("count", count); + builder.endObject(); + } + + public static FilterFacet readFilterFacet(StreamInput in) throws IOException { + InternalFilterFacet result = new InternalFilterFacet(); + result.readFrom(in); + return result; + } + + @Override public void readFrom(StreamInput in) throws IOException { + name = in.readUTF(); + count = in.readVLong(); + } + + @Override public void writeTo(StreamOutput out) throws IOException { + out.writeUTF(name); + out.writeVLong(count); + } +} \ No newline at end of file diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java index b23bc446720..3ad8afb192c 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetBuilder.java @@ -191,8 +191,8 @@ public class GeoDistanceFacetBuilder extends AbstractFacetBuilder { return this; } - public GeoDistanceFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public GeoDistanceFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -248,14 +248,7 @@ public class GeoDistanceFacetBuilder extends AbstractFacetBuilder { builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetCollectorParser.java index cc606d93fb2..2a8d0cbce9f 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetCollectorParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/geodistance/GeoDistanceFacetCollectorParser.java @@ -53,7 +53,7 @@ public class GeoDistanceFacetCollectorParser implements FacetCollectorParser { return new String[]{NAME, "geoDistance"}; } - @Override public FacetCollector parser(String facetName, XContentParser parser, SearchContext context) throws IOException { + @Override public FacetCollector parse(String facetName, XContentParser parser, SearchContext context) throws IOException { String fieldName = null; String valueFieldName = null; String valueScript = null; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetBuilder.java index 8b501f70ed4..8473734099b 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetBuilder.java @@ -108,8 +108,8 @@ public class HistogramFacetBuilder extends AbstractFacetBuilder { /** * An additional filter used to further filter down the set of documents the facet will run on. */ - public HistogramFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public HistogramFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -135,14 +135,7 @@ public class HistogramFacetBuilder extends AbstractFacetBuilder { } builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetCollectorParser.java index eb23c880da1..0b6548cc1e0 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetCollectorParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramFacetCollectorParser.java @@ -40,7 +40,7 @@ public class HistogramFacetCollectorParser implements FacetCollectorParser { return new String[]{NAME}; } - @Override public FacetCollector parser(String facetName, XContentParser parser, SearchContext context) throws IOException { + @Override public FacetCollector parse(String facetName, XContentParser parser, SearchContext context) throws IOException { String keyField = null; String valueField = null; String keyScript = null; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramScriptFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramScriptFacetBuilder.java index 3dafbf1829d..a5253c74454 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramScriptFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/histogram/HistogramScriptFacetBuilder.java @@ -75,8 +75,8 @@ public class HistogramScriptFacetBuilder extends AbstractFacetBuilder { return this; } - public HistogramScriptFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public HistogramScriptFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -104,14 +104,7 @@ public class HistogramScriptFacetBuilder extends AbstractFacetBuilder { } builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/internal/InternalFacets.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/internal/InternalFacets.java index f75eb56fc5b..5f4fc0d2510 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/internal/InternalFacets.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/internal/InternalFacets.java @@ -29,6 +29,7 @@ import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.builder.XContentBuilder; import org.elasticsearch.search.facets.Facet; import org.elasticsearch.search.facets.Facets; +import org.elasticsearch.search.facets.filter.InternalFilterFacet; import org.elasticsearch.search.facets.geodistance.InternalGeoDistanceFacet; import org.elasticsearch.search.facets.histogram.InternalHistogramFacet; import org.elasticsearch.search.facets.query.InternalQueryFacet; @@ -148,6 +149,8 @@ public class InternalFacets implements Facets, Streamable, ToXContent, Iterable< facets.add(InternalGeoDistanceFacet.readGeoDistanceFacet(in)); } else if (id == Facet.Type.RANGE.id()) { facets.add(InternalRangeDistanceFacet.readRangeFacet(in)); + } else if (id == Facet.Type.FILTER.id()) { + facets.add(InternalFilterFacet.readFilterFacet(in)); } else { throw new IOException("Can't handle facet type with id [" + id + "]"); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/InternalQueryFacet.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/InternalQueryFacet.java index f6eb8b9f06f..fc8a66b118f 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/InternalQueryFacet.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/InternalQueryFacet.java @@ -78,10 +78,6 @@ public class InternalQueryFacet implements QueryFacet, InternalFacet { return count; } - public void increment(long increment) { - count += increment; - } - @Override public Facet aggregate(Iterable facets) { int count = 0; for (Facet facet : facets) { @@ -94,7 +90,7 @@ public class InternalQueryFacet implements QueryFacet, InternalFacet { @Override public void toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(name); - builder.field("_type", "query"); + builder.field("_type", QueryFacetCollectorParser.NAME); builder.field("count", count); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetBuilder.java index e20d7134eb3..854b5b21946 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetBuilder.java @@ -43,8 +43,8 @@ public class QueryFacetBuilder extends AbstractFacetBuilder { return this; } - public QueryFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public QueryFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -61,14 +61,8 @@ public class QueryFacetBuilder extends AbstractFacetBuilder { builder.field(QueryFacetCollectorParser.NAME); query.toXContent(builder, params); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } + addFilterFacetAndGlobal(builder, params); - if (global != null) { - builder.field("global", global); - } builder.endObject(); } } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetCollectorParser.java index 6bf8ea8b2a8..2a4e79ffa35 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetCollectorParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/query/QueryFacetCollectorParser.java @@ -37,7 +37,7 @@ public class QueryFacetCollectorParser implements FacetCollectorParser { return new String[]{"query"}; } - @Override public FacetCollector parser(String facetName, XContentParser parser, SearchContext context) { + @Override public FacetCollector parse(String facetName, XContentParser parser, SearchContext context) { XContentIndexQueryParser indexQueryParser = (XContentIndexQueryParser) context.queryParser(); Query facetQuery = indexQueryParser.parse(parser); return new QueryFacetCollector(facetName, facetQuery, context.filterCache()); diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetBuilder.java index 666aa5e90cc..a6b8d63926e 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetBuilder.java @@ -136,8 +136,8 @@ public class RangeFacetBuilder extends AbstractFacetBuilder { /** * An additional filter used to further filter down the set of documents the facet will run on. */ - public RangeFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public RangeFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -179,14 +179,7 @@ public class RangeFacetBuilder extends AbstractFacetBuilder { builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetCollectorParser.java index f6890d0bde0..f71df4c8ef1 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetCollectorParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeFacetCollectorParser.java @@ -42,7 +42,7 @@ public class RangeFacetCollectorParser implements FacetCollectorParser { return new String[]{NAME}; } - @Override public FacetCollector parser(String facetName, XContentParser parser, SearchContext context) throws IOException { + @Override public FacetCollector parse(String facetName, XContentParser parser, SearchContext context) throws IOException { String keyField = null; String valueField = null; String keyScript = null; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeScriptFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeScriptFacetBuilder.java index 50181ac906f..0753580832b 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeScriptFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/range/RangeScriptFacetBuilder.java @@ -99,8 +99,8 @@ public class RangeScriptFacetBuilder extends AbstractFacetBuilder { return this; } - public RangeScriptFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public RangeScriptFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -141,14 +141,7 @@ public class RangeScriptFacetBuilder extends AbstractFacetBuilder { } builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetBuilder.java index cdd0a07a444..779e7f83291 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetBuilder.java @@ -46,8 +46,8 @@ public class StatisticalFacetBuilder extends AbstractFacetBuilder { return this; } - public StatisticalFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public StatisticalFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -61,14 +61,7 @@ public class StatisticalFacetBuilder extends AbstractFacetBuilder { builder.field("field", fieldName); builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetCollectorParser.java index 5ef3ea52586..f462a1261ec 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetCollectorParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalFacetCollectorParser.java @@ -47,7 +47,7 @@ public class StatisticalFacetCollectorParser implements FacetCollectorParser { return new String[]{NAME}; } - @Override public FacetCollector parser(String facetName, XContentParser parser, SearchContext context) throws IOException { + @Override public FacetCollector parse(String facetName, XContentParser parser, SearchContext context) throws IOException { String field = null; String currentFieldName = null; diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalScriptFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalScriptFacetBuilder.java index e736d3728f8..dd7419845ed 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalScriptFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/statistical/StatisticalScriptFacetBuilder.java @@ -44,8 +44,8 @@ public class StatisticalScriptFacetBuilder extends AbstractFacetBuilder { return this; } - public StatisticalScriptFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public StatisticalScriptFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -76,14 +76,7 @@ public class StatisticalScriptFacetBuilder extends AbstractFacetBuilder { } builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetBuilder.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetBuilder.java index a2d33d30bf3..6b2936a29cf 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetBuilder.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetBuilder.java @@ -46,8 +46,8 @@ public class TermsFacetBuilder extends AbstractFacetBuilder { return this; } - public TermsFacetBuilder filter(XContentFilterBuilder filter) { - this.filter = filter; + public TermsFacetBuilder facetFilter(XContentFilterBuilder filter) { + this.facetFilter = filter; return this; } @@ -100,13 +100,7 @@ public class TermsFacetBuilder extends AbstractFacetBuilder { } builder.endObject(); - if (filter != null) { - builder.field("filter"); - filter.toXContent(builder, params); - } - if (global != null) { - builder.field("global", global); - } + addFilterFacetAndGlobal(builder, params); builder.endObject(); } diff --git a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetCollectorParser.java b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetCollectorParser.java index 308f9deb17b..f56e8d08fce 100644 --- a/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetCollectorParser.java +++ b/modules/elasticsearch/src/main/java/org/elasticsearch/search/facets/terms/TermsFacetCollectorParser.java @@ -40,7 +40,7 @@ public class TermsFacetCollectorParser implements FacetCollectorParser { return new String[]{NAME}; } - @Override public FacetCollector parser(String facetName, XContentParser parser, SearchContext context) throws IOException { + @Override public FacetCollector parse(String facetName, XContentParser parser, SearchContext context) throws IOException { String field = null; int size = 10; diff --git a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/facets/SimpleFacetsTests.java b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/facets/SimpleFacetsTests.java index b20781f36a7..5dd94dff036 100644 --- a/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/facets/SimpleFacetsTests.java +++ b/modules/test/integration/src/test/java/org/elasticsearch/test/integration/search/facets/SimpleFacetsTests.java @@ -23,6 +23,7 @@ import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.action.search.ShardSearchFailure; import org.elasticsearch.client.Client; +import org.elasticsearch.search.facets.filter.FilterFacet; import org.elasticsearch.search.facets.histogram.HistogramFacet; import org.elasticsearch.search.facets.range.RangeFacet; import org.elasticsearch.search.facets.statistical.StatisticalFacet; @@ -116,6 +117,40 @@ public class SimpleFacetsTests extends AbstractNodesTests { assertThat(facet.entries().get(0).count(), equalTo(2)); } + @Test public void testFilterFacets() throws Exception { + try { + client.admin().indices().prepareDelete("test").execute().actionGet(); + } catch (Exception e) { + // ignore + } + client.admin().indices().prepareCreate("test").execute().actionGet(); + client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet(); + + client.prepareIndex("test", "type1").setSource(jsonBuilder().startObject() + .field("stag", "111") + .startArray("tag").value("xxx").value("yyy").endArray() + .endObject()).execute().actionGet(); + client.admin().indices().prepareFlush().setRefresh(true).execute().actionGet(); + + client.prepareIndex("test", "type1").setSource(jsonBuilder().startObject() + .field("stag", "111") + .startArray("tag").value("zzz").value("yyy").endArray() + .endObject()).execute().actionGet(); + + client.admin().indices().prepareRefresh().execute().actionGet(); + + SearchResponse searchResponse = client.prepareSearch() + .setQuery(matchAllQuery()) + .addFacet(filterFacet("facet1").filter(termFilter("stag", "111"))) + .addFacet(filterFacet("facet2").filter(termFilter("tag", "xxx"))) + .addFacet(filterFacet("facet3").filter(termFilter("tag", "yyy"))) + .execute().actionGet(); + + FilterFacet facet = searchResponse.facets().facet("facet1"); + assertThat(facet.name(), equalTo("facet1")); + assertThat(facet.count(), equalTo(2l)); + } + @Test public void testTermsFacets() throws Exception { try { client.admin().indices().prepareDelete("test").execute().actionGet(); @@ -158,7 +193,7 @@ public class SimpleFacetsTests extends AbstractNodesTests { searchResponse = client.prepareSearch() .setQuery(matchAllQuery()) - .addFacet(termsFacet("facet1").field("stag").size(10).filter(termFilter("tag", "xxx"))) + .addFacet(termsFacet("facet1").field("stag").size(10).facetFilter(termFilter("tag", "xxx"))) .execute().actionGet(); facet = searchResponse.facets().facet("facet1");