Search API: Query Facet - Add global flag to control if the facet is bounded to the search query or not, closes #50.

This commit is contained in:
kimchy 2010-03-05 16:05:08 +02:00
parent 40b0dfddec
commit f4f26d2118
5 changed files with 99 additions and 41 deletions

View File

@ -58,7 +58,22 @@ public class SearchSourceFacetsBuilder implements ToJson {
if (queryFacets == null) {
queryFacets = newArrayListWithCapacity(2);
}
queryFacets.add(new FacetQuery(name, query));
queryFacets.add(new FacetQuery(name, query, null));
return this;
}
/**
* 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
*/
public SearchSourceFacetsBuilder facet(String name, JsonQueryBuilder query, boolean global) {
if (queryFacets == null) {
queryFacets = newArrayListWithCapacity(2);
}
queryFacets.add(new FacetQuery(name, query, global));
return this;
}
@ -78,6 +93,9 @@ public class SearchSourceFacetsBuilder implements ToJson {
builder.startObject(facetQuery.name());
builder.field("query");
facetQuery.queryBuilder().toJson(builder, params);
if (facetQuery.global() != null) {
builder.field("global", facetQuery.global());
}
builder.endObject();
}
}
@ -88,10 +106,12 @@ public class SearchSourceFacetsBuilder implements ToJson {
private static class FacetQuery {
private final String name;
private final JsonQueryBuilder queryBuilder;
private final Boolean global;
private FacetQuery(String name, JsonQueryBuilder queryBuilder) {
private FacetQuery(String name, JsonQueryBuilder queryBuilder, Boolean global) {
this.name = name;
this.queryBuilder = queryBuilder;
this.global = global;
}
public String name() {
@ -101,5 +121,9 @@ public class SearchSourceFacetsBuilder implements ToJson {
public JsonQueryBuilder queryBuilder() {
return queryBuilder;
}
public Boolean global() {
return this.global;
}
}
}

View File

@ -36,7 +36,8 @@ import java.util.List;
* facets : {
* queryExecution : "collect|idset",
* facet1: {
* query : { ... }
* query : { ... },
* global : false
* }
* }
* </pre>
@ -49,12 +50,12 @@ public class FacetsParseElement implements SearchParseElement {
JsonToken token;
SearchContextFacets.QueryExecutionType queryExecutionType = SearchContextFacets.QueryExecutionType.COLLECT;
List<SearchContextFacets.QueryFacet> queryFacets = null;
String topLevelFieldName = null;
while ((token = jp.nextToken()) != JsonToken.END_OBJECT) {
if (token == JsonToken.FIELD_NAME) {
String topLevelFieldName = jp.getCurrentName();
topLevelFieldName = jp.getCurrentName();
} else if (token == JsonToken.VALUE_STRING) {
if ("queryExecution".equals(topLevelFieldName)) {
jp.nextToken(); // move to value
String text = jp.getText();
if ("collect".equals(text)) {
queryExecutionType = SearchContextFacets.QueryExecutionType.COLLECT;
@ -63,26 +64,36 @@ public class FacetsParseElement implements SearchParseElement {
} else {
throw new SearchParseException(context, "Unsupported query type [" + text + "]");
}
} else {
jp.nextToken(); // move to START_OBJECT
jp.nextToken(); // move to FIELD_NAME
String facetType = jp.getCurrentName();
if ("query".equals(facetType)) {
JsonIndexQueryParser indexQueryParser = (JsonIndexQueryParser) context.queryParser();
Query facetQuery = indexQueryParser.parse(jp);
if (queryFacets == null) {
queryFacets = Lists.newArrayListWithCapacity(2);
}
queryFacets.add(new SearchContextFacets.QueryFacet(topLevelFieldName, facetQuery));
} else {
throw new SearchParseException(context, "Unsupported facet type [" + facetType + "] for facet name [" + topLevelFieldName + "]");
}
jp.nextToken();
}
} else if (token == JsonToken.START_OBJECT) {
SearchContextFacets.Facet facet = null;
boolean global = false;
String facetFieldName = null;
while ((token = jp.nextToken()) != JsonToken.END_OBJECT) {
if (token == JsonToken.FIELD_NAME) {
facetFieldName = jp.getCurrentName();
} else if (token == JsonToken.START_OBJECT) {
if ("query".equals(facetFieldName)) {
JsonIndexQueryParser indexQueryParser = (JsonIndexQueryParser) context.queryParser();
Query facetQuery = indexQueryParser.parse(jp);
facet = new SearchContextFacets.QueryFacet(topLevelFieldName, facetQuery);
if (queryFacets == null) {
queryFacets = Lists.newArrayListWithCapacity(2);
}
queryFacets.add((SearchContextFacets.QueryFacet) facet);
}
} else if (token == JsonToken.VALUE_TRUE) {
if ("global".equals(facetFieldName)) {
global = true;
}
} else if (token == JsonToken.VALUE_NUMBER_INT) {
global = jp.getIntValue() != 0;
}
}
if (facet == null) {
throw new SearchParseException(context, "No facet type found for [" + topLevelFieldName + "]");
}
facet.global(global);
}
}

View File

@ -21,9 +21,7 @@ package org.elasticsearch.search.facets;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.*;
import org.apache.lucene.util.OpenBitSet;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchIllegalStateException;
@ -38,7 +36,7 @@ import java.util.List;
import java.util.Map;
/**
* @author kimchy (Shay Banon)
* @author kimchy (shay.banon)
*/
public class FacetsPhase implements SearchPhase {
@ -63,17 +61,27 @@ public class FacetsPhase implements SearchPhase {
List<Facet> facets = Lists.newArrayListWithCapacity(2);
if (contextFacets.queryFacets() != null) {
for (SearchContextFacets.QueryFacet queryFacet : contextFacets.queryFacets()) {
Filter facetFilter = new QueryWrapperFilter(queryFacet.query());
facetFilter = context.filterCache().cache(facetFilter);
long count;
if (contextFacets.queryType() == SearchContextFacets.QueryExecutionType.COLLECT) {
count = executeQueryCollectorCount(context, queryFacet, facetFilter);
} else if (contextFacets.queryType() == SearchContextFacets.QueryExecutionType.IDSET) {
count = executeQueryIdSetCount(context, queryFacet, facetFilter);
if (queryFacet.global()) {
try {
Query globalQuery = new ConstantScoreQuery(context.filterCache().cache(new QueryWrapperFilter(queryFacet.query())));
long count = Lucene.count(context.searcher(), globalQuery, -1.0f);
facets.add(new CountFacet(queryFacet.name(), count));
} catch (Exception e) {
throw new FacetPhaseExecutionException(queryFacet.name(), "Failed to execute global facet [" + queryFacet.query() + "]", e);
}
} else {
throw new ElasticSearchIllegalStateException("No matching for type [" + contextFacets.queryType() + "]");
Filter facetFilter = new QueryWrapperFilter(queryFacet.query());
facetFilter = context.filterCache().cache(facetFilter);
long count;
if (contextFacets.queryType() == SearchContextFacets.QueryExecutionType.COLLECT) {
count = executeQueryCollectorCount(context, queryFacet, facetFilter);
} else if (contextFacets.queryType() == SearchContextFacets.QueryExecutionType.IDSET) {
count = executeQueryIdSetCount(context, queryFacet, facetFilter);
} else {
throw new ElasticSearchIllegalStateException("No matching for type [" + contextFacets.queryType() + "]");
}
facets.add(new CountFacet(queryFacet.name(), count));
}
facets.add(new CountFacet(queryFacet.name(), count));
}
}

View File

@ -24,7 +24,7 @@ import org.apache.lucene.search.Query;
import java.util.List;
/**
* @author kimchy (Shay Banon)
* @author kimchy (shay.banon)
*/
public class SearchContextFacets {
@ -50,7 +50,22 @@ public class SearchContextFacets {
return queryFacets;
}
public static class QueryFacet {
public static abstract class Facet {
private boolean global;
protected Facet() {
}
public boolean global() {
return global;
}
public void global(boolean global) {
this.global = global;
}
}
public static class QueryFacet extends Facet {
private final String name;
private final Query query;

View File

@ -222,7 +222,7 @@ public class TransportTwoServersSearchTests extends AbstractServersTests {
SearchSourceBuilder sourceBuilder = searchSource()
.query(termQuery("multi", "test"))
.from(0).size(20).explain(true)
.facets(facets().facet("all", termQuery("multi", "test")).facet("test1", termQuery("name", "test1")));
.facets(facets().facet("all", termQuery("multi", "test"), true).facet("test1", termQuery("name", "test1")));
SearchResponse searchResponse = client.search(searchRequest("test").source(sourceBuilder)).actionGet();
assertThat(searchResponse.hits().totalHits(), equalTo(100l));