Support for term facets on unmapped fields

Added support for unmapped & partially mapped fields (partially mapped fields may occur when searching across multiple indices where the faceted field is mapped on some and unmapped on others). If a shard doesn't have mappings for a field, the matching documents count on that shard will be added to the missing count for that facet.
This commit is contained in:
uboness 2013-05-14 11:28:38 +02:00
parent 906f278896
commit d06a15ec3e
12 changed files with 573 additions and 45 deletions

View File

@ -85,7 +85,7 @@ public class FacetParseElement implements SearchParseElement {
if ("facet_filter".equals(fieldName) || "facetFilter".equals(fieldName)) {
filter = context.queryParserService().parseInnerFilter(parser);
} else {
FacetParser facetParser = facetParsers.processor(fieldName);
FacetParser facetParser = facetParsers.parser(fieldName);
if (facetParser == null) {
throw new SearchParseException(context, "No facet type found for [" + fieldName + "]");
}
@ -114,6 +114,7 @@ public class FacetParseElement implements SearchParseElement {
}
}
}
if (filter != null) {
if (cacheFilter) {
filter = context.filterCache().cache(filter);

View File

@ -30,20 +30,20 @@ import java.util.Set;
*/
public class FacetParsers {
private final ImmutableMap<String, FacetParser> processors;
private final ImmutableMap<String, FacetParser> parsers;
@Inject
public FacetParsers(Set<FacetParser> processors) {
public FacetParsers(Set<FacetParser> parsers) {
MapBuilder<String, FacetParser> builder = MapBuilder.newMapBuilder();
for (FacetParser processor : processors) {
for (String type : processor.types()) {
builder.put(type, processor);
for (FacetParser parser : parsers) {
for (String type : parser.types()) {
builder.put(type, parser);
}
}
this.processors = builder.immutableMap();
this.parsers = builder.immutableMap();
}
public FacetParser processor(String type) {
return processors.get(type);
public FacetParser parser(String type) {
return parsers.get(type);
}
}

View File

@ -88,7 +88,7 @@ public class HistogramFacetParser extends AbstractComponent implements FacetPars
valueField = parser.text();
} else if ("interval".equals(fieldName)) {
interval = parser.longValue();
} else if ("time_interval".equals(fieldName)) {
} else if ("time_interval".equals(fieldName) || "timeInterval".equals(fieldName)) {
interval = TimeValue.parseTimeValue(parser.text(), null).millis();
} else if ("key_script".equals(fieldName) || "keyScript".equals(fieldName)) {
keyScript = parser.text();

View File

@ -158,7 +158,7 @@ public class RangeFacetParser extends AbstractComponent implements FacetParser {
} else {
FieldMapper valueFieldMapper = context.smartNameFieldMapper(valueField);
if (valueFieldMapper == null) {
throw new FacetPhaseExecutionException(facetName, "No mapping found for value_field [" + keyField + "]");
throw new FacetPhaseExecutionException(facetName, "No mapping found for value_field [" + valueField + "]");
}
IndexNumericFieldData valueIndexFieldData = context.fieldData().getForField(valueFieldMapper);
// we have a value field, and its different than the key

View File

@ -33,7 +33,6 @@ import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.facet.FacetExecutor;
import org.elasticsearch.search.facet.FacetParser;
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
import org.elasticsearch.search.facet.terms.doubles.TermsDoubleFacetExecutor;
import org.elasticsearch.search.facet.terms.index.IndexNameFacetExecutor;
import org.elasticsearch.search.facet.terms.longs.TermsLongFacetExecutor;
@ -41,9 +40,11 @@ import org.elasticsearch.search.facet.terms.strings.FieldsTermsStringFacetExecut
import org.elasticsearch.search.facet.terms.strings.ScriptTermsStringFieldFacetExecutor;
import org.elasticsearch.search.facet.terms.strings.TermsStringFacetExecutor;
import org.elasticsearch.search.facet.terms.strings.TermsStringOrdinalsFacetExecutor;
import org.elasticsearch.search.facet.terms.unmapped.UnmappedFieldExecutor;
import org.elasticsearch.search.internal.SearchContext;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
@ -119,7 +120,7 @@ public class TermsFacetParser extends AbstractComponent implements FacetParser {
} else if (token.isValue()) {
if ("field".equals(currentFieldName)) {
field = parser.text();
} else if ("script_field".equals(currentFieldName)) {
} else if ("script_field".equals(currentFieldName) || "scriptField".equals(currentFieldName)) {
script = parser.text();
} else if ("size".equals(currentFieldName)) {
size = parser.intValue();
@ -161,7 +162,20 @@ public class TermsFacetParser extends AbstractComponent implements FacetParser {
}
if (fieldsNames != null) {
return new FieldsTermsStringFacetExecutor(facetName, fieldsNames, size, comparatorType, allTerms, context, excluded, pattern, searchScript);
// in case of multi files, we only collect the fields that are mapped and facet on them.
ArrayList<FieldMapper> mappers = new ArrayList<FieldMapper>(fieldsNames.length);
for (int i = 0; i < fieldsNames.length; i++) {
FieldMapper mapper = context.smartNameFieldMapper(fieldsNames[i]);
if (mapper != null) {
mappers.add(mapper);
}
}
if (mappers.isEmpty()) {
// non of the fields is mapped
return new UnmappedFieldExecutor(size, comparatorType);
}
return new FieldsTermsStringFacetExecutor(facetName, mappers.toArray(new FieldMapper[mappers.size()]), size, comparatorType, allTerms, context, excluded, pattern, searchScript);
}
if (field == null && fieldsNames == null && script != null) {
return new ScriptTermsStringFieldFacetExecutor(size, comparatorType, context, excluded, pattern, scriptLang, script, params);
@ -169,7 +183,7 @@ public class TermsFacetParser extends AbstractComponent implements FacetParser {
FieldMapper fieldMapper = context.smartNameFieldMapper(field);
if (fieldMapper == null) {
throw new FacetPhaseExecutionException(facetName, "failed to find mapping for [" + field + "]");
return new UnmappedFieldExecutor(size, comparatorType);
}
IndexFieldData indexFieldData = context.fieldData().getForField(fieldMapper);

View File

@ -162,16 +162,22 @@ public class InternalDoubleTermsFacet extends InternalTermsFacet {
if (facets.size() == 1) {
return facets.get(0);
}
InternalDoubleTermsFacet first = (InternalDoubleTermsFacet) facets.get(0);
InternalDoubleTermsFacet first = null;
TDoubleIntHashMap aggregated = CacheRecycler.popDoubleIntMap();
long missing = 0;
long total = 0;
for (Facet facet : facets) {
InternalDoubleTermsFacet mFacet = (InternalDoubleTermsFacet) facet;
missing += mFacet.getMissingCount();
total += mFacet.getTotalCount();
for (DoubleEntry entry : mFacet.entries) {
aggregated.adjustOrPutValue(entry.term, entry.getCount(), entry.getCount());
TermsFacet termsFacet = (TermsFacet) facet;
// termsFacet could be of type InternalStringTermsFacet representing unmapped fields
if (first == null && termsFacet instanceof InternalDoubleTermsFacet) {
first = (InternalDoubleTermsFacet) termsFacet;
}
missing += termsFacet.getMissingCount();
total += termsFacet.getTotalCount();
for (Entry entry : termsFacet.getEntries()) {
aggregated.adjustOrPutValue(((DoubleEntry)entry).term, entry.getCount(), entry.getCount());
}
}

View File

@ -163,16 +163,22 @@ public class InternalLongTermsFacet extends InternalTermsFacet {
if (facets.size() == 1) {
return facets.get(0);
}
InternalLongTermsFacet first = (InternalLongTermsFacet) facets.get(0);
InternalLongTermsFacet first = null;
TLongIntHashMap aggregated = CacheRecycler.popLongIntMap();
long missing = 0;
long total = 0;
for (Facet facet : facets) {
InternalLongTermsFacet mFacet = (InternalLongTermsFacet) facet;
missing += mFacet.getMissingCount();
total += mFacet.getTotalCount();
for (LongEntry entry : mFacet.entries) {
aggregated.adjustOrPutValue(entry.term, entry.getCount(), entry.getCount());
TermsFacet termsFacet = (TermsFacet) facet;
// termsFacet could be of type InternalStringTermsFacet representing unmapped fields
if (first == null && termsFacet instanceof InternalLongTermsFacet) {
first = (InternalLongTermsFacet) termsFacet;
}
missing += termsFacet.getMissingCount();
total += termsFacet.getTotalCount();
for (Entry entry : termsFacet.getEntries()) {
aggregated.adjustOrPutValue(((LongEntry) entry).term, entry.getCount(), entry.getCount());
}
}

View File

@ -28,7 +28,6 @@ import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.script.SearchScript;
import org.elasticsearch.search.facet.FacetExecutor;
import org.elasticsearch.search.facet.FacetPhaseExecutionException;
import org.elasticsearch.search.facet.InternalFacet;
import org.elasticsearch.search.internal.SearchContext;
@ -48,17 +47,14 @@ public class FieldsTermsStringFacetExecutor extends FacetExecutor {
long missing;
long total;
public FieldsTermsStringFacetExecutor(String facetName, String[] fieldsNames, int size, InternalStringTermsFacet.ComparatorType comparatorType, boolean allTerms, SearchContext context,
public FieldsTermsStringFacetExecutor(String facetName, FieldMapper[] fieldMappers, int size, InternalStringTermsFacet.ComparatorType comparatorType, boolean allTerms, SearchContext context,
ImmutableSet<BytesRef> excluded, Pattern pattern, SearchScript script) {
this.size = size;
this.comparatorType = comparatorType;
this.script = script;
this.indexFieldDatas = new IndexFieldData[fieldsNames.length];
for (int i = 0; i < fieldsNames.length; i++) {
FieldMapper mapper = context.smartNameFieldMapper(fieldsNames[i]);
if (mapper == null) {
throw new FacetPhaseExecutionException(facetName, "failed to find mapping for [" + fieldsNames[i] + "]");
}
this.indexFieldDatas = new IndexFieldData[fieldMappers.length];
for (int i = 0; i < fieldMappers.length; i++) {
FieldMapper mapper = fieldMappers[i];
indexFieldDatas[i] = context.fieldData().getForField(mapper);
}
if (excluded.isEmpty() && pattern == null && script == null) {
@ -68,7 +64,7 @@ public class FieldsTermsStringFacetExecutor extends FacetExecutor {
}
if (allTerms) {
for (int i = 0; i < fieldsNames.length; i++) {
for (int i = 0; i < fieldMappers.length; i++) {
TermsStringFacetExecutor.loadAllTerms(context, indexFieldDatas[i], aggregator);
}
}

View File

@ -172,15 +172,29 @@ public class InternalStringTermsFacet extends InternalTermsFacet {
if (facets.size() == 1) {
return facets.get(0);
}
InternalStringTermsFacet first = (InternalStringTermsFacet) facets.get(0);
InternalStringTermsFacet first = null;
TObjectIntHashMap<Text> aggregated = CacheRecycler.popObjectIntMap();
long missing = 0;
long total = 0;
for (Facet facet : facets) {
InternalStringTermsFacet mFacet = (InternalStringTermsFacet) facet;
missing += mFacet.getMissingCount();
total += mFacet.getTotalCount();
for (TermEntry entry : mFacet.entries) {
InternalTermsFacet termsFacet = (InternalTermsFacet) facet;
missing += termsFacet.getMissingCount();
total += termsFacet.getTotalCount();
if (!(termsFacet instanceof InternalStringTermsFacet)) {
// the assumption is that if one of the facets is of different type, it should do the
// reduction (all the facets we iterated so far most likely represent unmapped fields, if not
// class cast exception will be thrown)
return termsFacet.reduce(facets);
}
if (first == null) {
first = (InternalStringTermsFacet) termsFacet;
}
for (Entry entry : termsFacet.getEntries()) {
aggregated.adjustOrPutValue(entry.getTerm(), entry.getCount(), entry.getCount());
}
}

View File

@ -0,0 +1,77 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch 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.facet.terms.unmapped;
import com.google.common.collect.ImmutableList;
import org.apache.lucene.index.AtomicReaderContext;
import org.elasticsearch.search.facet.FacetExecutor;
import org.elasticsearch.search.facet.InternalFacet;
import org.elasticsearch.search.facet.terms.TermsFacet;
import org.elasticsearch.search.facet.terms.strings.InternalStringTermsFacet;
import java.io.IOException;
import java.util.Collection;
/**
* A facet executor that only aggregates the missing count for unmapped fields and builds a terms facet over it
*/
public class UnmappedFieldExecutor extends FacetExecutor {
final int size;
final TermsFacet.ComparatorType comparatorType;
long missing;
public UnmappedFieldExecutor(int size, TermsFacet.ComparatorType comparatorType) {
this.size = size;
this.comparatorType = comparatorType;
}
@Override
public InternalFacet buildFacet(String facetName) {
Collection<InternalStringTermsFacet.TermEntry> entries = ImmutableList.of();
return new InternalStringTermsFacet(facetName, comparatorType, size, entries, missing, 0);
}
@Override
public Collector collector() {
return new Collector();
}
final class Collector extends FacetExecutor.Collector {
private long missing;
@Override
public void postCollection() {
UnmappedFieldExecutor.this.missing = missing;
}
@Override
public void collect(int doc) throws IOException {
missing++;
}
@Override
public void setNextReader(AtomicReaderContext context) throws IOException {
}
}
}

View File

@ -86,9 +86,9 @@ public class TermsStatsFacetParser extends AbstractComponent implements FacetPar
keyField = parser.text();
} else if ("value_field".equals(currentFieldName) || "valueField".equals(currentFieldName)) {
valueField = parser.text();
} else if ("script_field".equals(currentFieldName)) {
} else if ("script_field".equals(currentFieldName) || "scriptField".equals(currentFieldName)) {
script = parser.text();
} else if ("value_script".equals(currentFieldName)) {
} else if ("value_script".equals(currentFieldName) || "valueScript".equals(currentFieldName)) {
script = parser.text();
} else if ("size".equals(currentFieldName)) {
size = parser.intValue();
@ -98,8 +98,6 @@ public class TermsStatsFacetParser extends AbstractComponent implements FacetPar
}
} else if ("order".equals(currentFieldName) || "comparator".equals(currentFieldName)) {
comparatorType = TermsStatsFacet.ComparatorType.fromString(parser.text());
} else if ("value_script".equals(currentFieldName)) {
script = parser.text();
} else if ("lang".equals(currentFieldName)) {
scriptLang = parser.text();
}

View File

@ -0,0 +1,416 @@
/*
* Licensed to ElasticSearch and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. ElasticSearch 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.test.integration.search.facet.terms;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.Priority;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.search.facet.terms.TermsFacet;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.search.facet.FacetBuilders.termsFacet;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
/**
*
*/
public class UnmappedFieldsTermsFacetsTests extends AbstractNodesTests {
private Client client;
@BeforeClass
public void createNodes() throws Exception {
Settings settings = ImmutableSettings.settingsBuilder()
.put("index.number_of_shards", numberOfShards())
.put("index.number_of_replicas", 0)
.build();
for (int i = 0; i < numberOfNodes(); i++) {
startNode("node" + i, settings);
}
client = getClient();
}
protected int numberOfShards() {
return 5;
}
protected int numberOfNodes() {
return 1;
}
@AfterClass
public void closeNodes() {
client.close();
closeAllNodes();
}
@AfterMethod
public void cleanupTest() {
client.admin().indices().prepareDelete("_all").execute().actionGet();
}
protected Client getClient() {
return client("node0");
}
/**
* Tests the terms facet when faceting on unmapped field
*/
@Test
public void testUnmappedField() throws Exception {
client.admin().indices().prepareCreate("idx").execute().actionGet();
client.admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
for (int i = 0; i < 10; i++) {
client.prepareIndex("idx", "type", ""+i).setSource(jsonBuilder().startObject()
.field("mapped", ""+i)
.endObject()).execute().actionGet();
}
client.admin().indices().prepareFlush().setRefresh(true).execute().actionGet();
SearchResponse searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.addFacet(termsFacet("mapped").field("mapped").size(10))
.addFacet(termsFacet("unmapped_bool").field("unmapped_bool").size(10))
.addFacet(termsFacet("unmapped_str").field("unmapped_str").size(10))
.addFacet(termsFacet("unmapped_byte").field("unmapped_byte").size(10))
.addFacet(termsFacet("unmapped_short").field("unmapped_short").size(10))
.addFacet(termsFacet("unmapped_int").field("unmapped_int").size(10))
.addFacet(termsFacet("unmapped_long").field("unmapped_long").size(10))
.addFacet(termsFacet("unmapped_float").field("unmapped_float").size(10))
.addFacet(termsFacet("unmapped_double").field("unmapped_double").size(10))
.execute().actionGet();
assertThat(searchResponse.getHits().getTotalHits(), equalTo(10l));
// all values should be returned for the mapped field
TermsFacet facet = searchResponse.getFacets().facet("mapped");
assertThat(facet.getName(), equalTo("mapped"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getMissingCount(), is(0l));
// no values should be returned for the unmapped field (all docs are missing)
facet = searchResponse.getFacets().facet("unmapped_str");
assertThat(facet.getName(), equalTo("unmapped_str"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("unmapped_bool");
assertThat(facet.getName(), equalTo("unmapped_bool"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("unmapped_byte");
assertThat(facet.getName(), equalTo("unmapped_byte"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("unmapped_short");
assertThat(facet.getName(), equalTo("unmapped_short"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("unmapped_int");
assertThat(facet.getName(), equalTo("unmapped_int"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("unmapped_long");
assertThat(facet.getName(), equalTo("unmapped_long"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("unmapped_float");
assertThat(facet.getName(), equalTo("unmapped_float"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("unmapped_double");
assertThat(facet.getName(), equalTo("unmapped_double"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
}
/**
* Tests the terms facet when faceting on partially unmapped field. An example for this scenario is when searching
* across indices, where the field is mapped in some indices and unmapped in others.
*/
@Test
public void testPartiallyUnmappedField() throws ElasticSearchException, IOException {
client.admin().indices().prepareCreate("mapped_idx")
.addMapping("type", jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("partially_mapped_byte").field("type", "byte").endObject()
.startObject("partially_mapped_short").field("type", "short").endObject()
.startObject("partially_mapped_int").field("type", "integer").endObject()
.startObject("partially_mapped_long").field("type", "long").endObject()
.startObject("partially_mapped_float").field("type", "float").endObject()
.startObject("partially_mapped_double").field("type", "double").endObject()
.endObject().endObject().endObject())
.execute().actionGet();
client.admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
client.admin().indices().prepareCreate("unmapped_idx").execute().actionGet();
client.admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
for (int i = 0; i < 10; i++) {
client.prepareIndex("mapped_idx", "type", ""+i).setSource(jsonBuilder().startObject()
.field("mapped", "" + i)
.field("partially_mapped_str", ""+i)
.field("partially_mapped_bool", i%2 == 0)
.field("partially_mapped_byte", i)
.field("partially_mapped_short", i)
.field("partially_mapped_int", i)
.field("partially_mapped_long", i)
.field("partially_mapped_float", i)
.field("partially_mapped_double", i)
.endObject()).execute().actionGet();
}
for (int i = 10; i < 20; i++) {
client.prepareIndex("unmapped_idx", "type", ""+i).setSource(jsonBuilder().startObject()
.field("mapped", ""+i)
.endObject()).execute().actionGet();
}
client.admin().indices().prepareFlush().setRefresh(true).execute().actionGet();
SearchResponse searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.addFacet(termsFacet("mapped").field("mapped").size(10))
.addFacet(termsFacet("partially_mapped_str").field("partially_mapped_str").size(10))
.addFacet(termsFacet("partially_mapped_bool").field("partially_mapped_bool").size(10))
.addFacet(termsFacet("partially_mapped_byte").field("partially_mapped_byte").size(10))
.addFacet(termsFacet("partially_mapped_short").field("partially_mapped_short").size(10))
.addFacet(termsFacet("partially_mapped_int").field("partially_mapped_int").size(10))
.addFacet(termsFacet("partially_mapped_long").field("partially_mapped_long").size(10))
.addFacet(termsFacet("partially_mapped_float").field("partially_mapped_float").size(10))
.addFacet(termsFacet("partially_mapped_double").field("partially_mapped_double").size(10))
.execute().actionGet();
assertThat(searchResponse.getHits().getTotalHits(), equalTo(20l));
// all values should be returned for the mapped field
TermsFacet facet = searchResponse.getFacets().facet("mapped");
assertThat(facet.getName(), equalTo("mapped"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(20l));
assertThat(facet.getOtherCount(), is(10l));
assertThat(facet.getMissingCount(), is(0l));
// only the values of the mapped index should be returned for the partially mapped field (all docs of
// the unmapped index should be missing)
facet = searchResponse.getFacets().facet("partially_mapped_str");
assertThat(facet.getName(), equalTo("partially_mapped_str"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("partially_mapped_bool");
assertThat(facet.getName(), equalTo("partially_mapped_bool"));
assertThat(facet.getEntries().size(), is(2));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("partially_mapped_byte");
assertThat(facet.getName(), equalTo("partially_mapped_byte"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("partially_mapped_short");
assertThat(facet.getName(), equalTo("partially_mapped_short"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("partially_mapped_int");
assertThat(facet.getName(), equalTo("partially_mapped_int"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("partially_mapped_long");
assertThat(facet.getName(), equalTo("partially_mapped_long"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("partially_mapped_float");
assertThat(facet.getName(), equalTo("partially_mapped_float"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("partially_mapped_float");
assertThat(facet.getName(), equalTo("partially_mapped_float"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getOtherCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
}
@Test
public void testMappedYetMissingField() throws IOException {
client.admin().indices().prepareCreate("idx")
.addMapping("type", jsonBuilder().startObject()
.field("type").startObject()
.field("properties").startObject()
.field("string").startObject().field("type", "string").endObject()
.field("long").startObject().field("type", "long").endObject()
.field("double").startObject().field("type", "double").endObject()
.endObject()
.endObject())
.execute().actionGet();
client.admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
for (int i = 0; i < 10; i++) {
client.prepareIndex("idx", "type", ""+i).setSource(jsonBuilder().startObject()
.field("foo", "bar")
.endObject()).execute().actionGet();
}
client.admin().indices().prepareFlush().setRefresh(true).execute().actionGet();
SearchResponse searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.addFacet(termsFacet("string").field("string").size(10))
.addFacet(termsFacet("long").field("long").size(10))
.addFacet(termsFacet("double").field("double").size(10))
.execute().actionGet();
TermsFacet facet = searchResponse.getFacets().facet("string");
assertThat(facet.getName(), equalTo("string"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("long");
assertThat(facet.getName(), equalTo("long"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
facet = searchResponse.getFacets().facet("double");
assertThat(facet.getName(), equalTo("double"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
}
/**
* Tests the terms facet when faceting on multiple fields
* case 1: some but not all the fields are mapped
* case 2: all the fields are unmapped
*/
@Test
public void testMultiFields() throws Exception {
client.admin().indices().prepareCreate("idx").execute().actionGet();
client.admin().cluster().prepareHealth().setWaitForEvents(Priority.LANGUID).setWaitForGreenStatus().execute().actionGet();
for (int i = 0; i < 10; i++) {
client.prepareIndex("idx", "type", ""+i).setSource(jsonBuilder().startObject()
.field("mapped_str", ""+i)
.field("mapped_long", i)
.field("mapped_double", i)
.endObject()).execute().actionGet();
}
client.admin().indices().prepareFlush().setRefresh(true).execute().actionGet();
SearchResponse searchResponse = client.prepareSearch()
.setQuery(matchAllQuery())
.addFacet(termsFacet("string").fields("mapped_str", "unmapped").size(10))
.addFacet(termsFacet("long").fields("mapped_long", "unmapped").size(10))
.addFacet(termsFacet("double").fields("mapped_double", "unmapped").size(10))
.addFacet(termsFacet("all_unmapped").fields("unmapped", "unmapped_1").size(10))
.execute().actionGet();
assertThat(searchResponse.getHits().getTotalHits(), equalTo(10l));
TermsFacet facet = searchResponse.getFacets().facet("string");
assertThat(facet.getName(), equalTo("string"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getMissingCount(), is(0l));
facet = searchResponse.getFacets().facet("long");
assertThat(facet.getName(), equalTo("long"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getMissingCount(), is(0l));
facet = searchResponse.getFacets().facet("double");
assertThat(facet.getName(), equalTo("double"));
assertThat(facet.getEntries().size(), is(10));
assertThat(facet.getTotalCount(), is(10l));
assertThat(facet.getMissingCount(), is(0l));
facet = searchResponse.getFacets().facet("all_unmapped");
assertThat(facet.getName(), equalTo("all_unmapped"));
assertThat(facet.getEntries().size(), is(0));
assertThat(facet.getTotalCount(), is(0l));
assertThat(facet.getMissingCount(), is(10l));
}
}