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:
parent
906f278896
commit
d06a15ec3e
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue