Introduce index option named 'index.percolator.map_unmapped_fields_as_string', that handles unmapped fields in percolator queries as type string.
Closes #9053 Closes #9054
This commit is contained in:
parent
a4c92eb67e
commit
eaa1674d6d
|
@ -472,3 +472,14 @@ achieve the same result (with way less memory being used).
|
||||||
|
|
||||||
The delete-by-query api doesn't work to unregister a query, it only deletes the percolate documents from disk. In order
|
The delete-by-query api doesn't work to unregister a query, it only deletes the percolate documents from disk. In order
|
||||||
to update the registered queries in memory the index needs be closed and opened.
|
to update the registered queries in memory the index needs be closed and opened.
|
||||||
|
|
||||||
|
[float]
|
||||||
|
=== Forcing unmapped fields to be handled as string
|
||||||
|
|
||||||
|
In certain cases it is unknown what kind of percolator queries do get registered and if no field mapping exist for fields
|
||||||
|
that are referred by percolator queries then adding a percolator query fails. This means the mapping needs to be updated
|
||||||
|
to have the field with the appropriate settings and then the percolator query can be added. But sometimes it is sufficient
|
||||||
|
if all unmapped fields are handled as if these were default string fields. In those cases one can configure the
|
||||||
|
`index.percolator.map_unmapped_fields_as_string` setting to `true` (default to `false`) and then if a field referred in
|
||||||
|
a percolator query does not exist, it will be handled as a default string field, so adding the percolator query doesn't
|
||||||
|
fail.
|
|
@ -69,6 +69,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
*/
|
*/
|
||||||
public class PercolatorQueriesRegistry extends AbstractIndexShardComponent implements Closeable{
|
public class PercolatorQueriesRegistry extends AbstractIndexShardComponent implements Closeable{
|
||||||
|
|
||||||
|
public final String MAP_UNMAPPED_FIELDS_AS_STRING = "index.percolator.map_unmapped_fields_as_string";
|
||||||
|
|
||||||
// This is a shard level service, but these below are index level service:
|
// This is a shard level service, but these below are index level service:
|
||||||
private final IndexQueryParserService queryParserService;
|
private final IndexQueryParserService queryParserService;
|
||||||
private final MapperService mapperService;
|
private final MapperService mapperService;
|
||||||
|
@ -85,6 +87,8 @@ public class PercolatorQueriesRegistry extends AbstractIndexShardComponent imple
|
||||||
private final PercolateTypeListener percolateTypeListener = new PercolateTypeListener();
|
private final PercolateTypeListener percolateTypeListener = new PercolateTypeListener();
|
||||||
private final AtomicBoolean realTimePercolatorEnabled = new AtomicBoolean(false);
|
private final AtomicBoolean realTimePercolatorEnabled = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
private boolean mapUnmappedFieldsAsString;
|
||||||
|
|
||||||
private CloseableThreadLocal<QueryParseContext> cache = new CloseableThreadLocal<QueryParseContext>() {
|
private CloseableThreadLocal<QueryParseContext> cache = new CloseableThreadLocal<QueryParseContext>() {
|
||||||
@Override
|
@Override
|
||||||
protected QueryParseContext initialValue() {
|
protected QueryParseContext initialValue() {
|
||||||
|
@ -104,6 +108,7 @@ public class PercolatorQueriesRegistry extends AbstractIndexShardComponent imple
|
||||||
this.indexCache = indexCache;
|
this.indexCache = indexCache;
|
||||||
this.indexFieldDataService = indexFieldDataService;
|
this.indexFieldDataService = indexFieldDataService;
|
||||||
this.shardPercolateService = shardPercolateService;
|
this.shardPercolateService = shardPercolateService;
|
||||||
|
this.mapUnmappedFieldsAsString = indexSettings.getAsBoolean(MAP_UNMAPPED_FIELDS_AS_STRING, false);
|
||||||
|
|
||||||
indicesLifecycle.addListener(shardLifecycleListener);
|
indicesLifecycle.addListener(shardLifecycleListener);
|
||||||
mapperService.addTypeListener(percolateTypeListener);
|
mapperService.addTypeListener(percolateTypeListener);
|
||||||
|
@ -209,7 +214,11 @@ public class PercolatorQueriesRegistry extends AbstractIndexShardComponent imple
|
||||||
// Query parsing can't introduce new fields in mappings (which happens when registering a percolator query),
|
// Query parsing can't introduce new fields in mappings (which happens when registering a percolator query),
|
||||||
// because field type can't be inferred from queries (like document do) so the best option here is to disallow
|
// because field type can't be inferred from queries (like document do) so the best option here is to disallow
|
||||||
// the usage of unmapped fields in percolator queries to avoid unexpected behaviour
|
// the usage of unmapped fields in percolator queries to avoid unexpected behaviour
|
||||||
|
//
|
||||||
|
// if index.percolator.map_unmapped_fields_as_string is set to true, query can contain unmapped fields which will be mapped
|
||||||
|
// as an analyzed string.
|
||||||
context.setAllowUnmappedFields(false);
|
context.setAllowUnmappedFields(false);
|
||||||
|
context.setMapUnmappedFieldAsString(mapUnmappedFieldsAsString ? true : false);
|
||||||
return queryParserService.parseInnerQuery(context);
|
return queryParserService.parseInnerQuery(context);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new QueryParsingException(queryParserService.index(), "Failed to parse", e);
|
throw new QueryParsingException(queryParserService.index(), "Failed to parse", e);
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.elasticsearch.common.lucene.search.NoCacheFilter;
|
||||||
import org.elasticsearch.common.lucene.search.NoCacheQuery;
|
import org.elasticsearch.common.lucene.search.NoCacheQuery;
|
||||||
import org.elasticsearch.common.lucene.search.Queries;
|
import org.elasticsearch.common.lucene.search.Queries;
|
||||||
import org.elasticsearch.common.lucene.search.ResolvableFilter;
|
import org.elasticsearch.common.lucene.search.ResolvableFilter;
|
||||||
|
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.index.Index;
|
import org.elasticsearch.index.Index;
|
||||||
import org.elasticsearch.index.analysis.AnalysisService;
|
import org.elasticsearch.index.analysis.AnalysisService;
|
||||||
|
@ -46,7 +47,11 @@ import org.elasticsearch.index.cache.query.parser.QueryParserCache;
|
||||||
import org.elasticsearch.index.fielddata.IndexFieldData;
|
import org.elasticsearch.index.fielddata.IndexFieldData;
|
||||||
import org.elasticsearch.index.mapper.FieldMapper;
|
import org.elasticsearch.index.mapper.FieldMapper;
|
||||||
import org.elasticsearch.index.mapper.FieldMappers;
|
import org.elasticsearch.index.mapper.FieldMappers;
|
||||||
|
import org.elasticsearch.index.mapper.Mapper;
|
||||||
import org.elasticsearch.index.mapper.MapperService;
|
import org.elasticsearch.index.mapper.MapperService;
|
||||||
|
import org.elasticsearch.index.mapper.MapperBuilders;
|
||||||
|
import org.elasticsearch.index.mapper.ContentPath;
|
||||||
|
import org.elasticsearch.index.mapper.core.StringFieldMapper;
|
||||||
import org.elasticsearch.index.search.child.CustomQueryWrappingFilter;
|
import org.elasticsearch.index.search.child.CustomQueryWrappingFilter;
|
||||||
import org.elasticsearch.index.similarity.SimilarityService;
|
import org.elasticsearch.index.similarity.SimilarityService;
|
||||||
import org.elasticsearch.script.ScriptService;
|
import org.elasticsearch.script.ScriptService;
|
||||||
|
@ -102,6 +107,8 @@ public class QueryParseContext {
|
||||||
|
|
||||||
private boolean allowUnmappedFields;
|
private boolean allowUnmappedFields;
|
||||||
|
|
||||||
|
private boolean mapUnmappedFieldAsString;
|
||||||
|
|
||||||
public QueryParseContext(Index index, IndexQueryParserService indexQueryParser) {
|
public QueryParseContext(Index index, IndexQueryParserService indexQueryParser) {
|
||||||
this(index, indexQueryParser, false);
|
this(index, indexQueryParser, false);
|
||||||
}
|
}
|
||||||
|
@ -389,10 +396,6 @@ public class QueryParseContext {
|
||||||
return failIfFieldMappingNotFound(name, indexQueryParser.mapperService.smartName(name, getTypes()));
|
return failIfFieldMappingNotFound(name, indexQueryParser.mapperService.smartName(name, getTypes()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldMapper smartNameFieldMapper(String name) {
|
|
||||||
return failIfFieldMappingNotFound(name, indexQueryParser.mapperService.smartNameFieldMapper(name, getTypes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapperService.SmartNameObjectMapper smartObjectMapper(String name) {
|
public MapperService.SmartNameObjectMapper smartObjectMapper(String name) {
|
||||||
return indexQueryParser.mapperService.smartNameObjectMapper(name, getTypes());
|
return indexQueryParser.mapperService.smartNameObjectMapper(name, getTypes());
|
||||||
}
|
}
|
||||||
|
@ -401,9 +404,17 @@ public class QueryParseContext {
|
||||||
this.allowUnmappedFields = allowUnmappedFields;
|
this.allowUnmappedFields = allowUnmappedFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T failIfFieldMappingNotFound(String name, T fieldMapping) {
|
public void setMapUnmappedFieldAsString(boolean mapUnmappedFieldAsString) {
|
||||||
|
this.mapUnmappedFieldAsString = mapUnmappedFieldAsString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MapperService.SmartNameFieldMappers failIfFieldMappingNotFound(String name, MapperService.SmartNameFieldMappers fieldMapping) {
|
||||||
if (allowUnmappedFields) {
|
if (allowUnmappedFields) {
|
||||||
return fieldMapping;
|
return fieldMapping;
|
||||||
|
} else if (mapUnmappedFieldAsString){
|
||||||
|
StringFieldMapper.Builder builder = MapperBuilders.stringField(name);
|
||||||
|
StringFieldMapper stringFieldMapper = builder.build(new Mapper.BuilderContext(ImmutableSettings.EMPTY, new ContentPath(1)));
|
||||||
|
return new MapperService.SmartNameFieldMappers(mapperService(), new FieldMappers(stringFieldMapper), null, false);
|
||||||
} else {
|
} else {
|
||||||
Version indexCreatedVersion = indexQueryParser.getIndexCreatedVersion();
|
Version indexCreatedVersion = indexQueryParser.getIndexCreatedVersion();
|
||||||
if (fieldMapping == null && indexCreatedVersion.onOrAfter(Version.V_1_4_0_Beta1)) {
|
if (fieldMapping == null && indexCreatedVersion.onOrAfter(Version.V_1_4_0_Beta1)) {
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.client.Client;
|
import org.elasticsearch.client.Client;
|
||||||
import org.elasticsearch.client.Requests;
|
import org.elasticsearch.client.Requests;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
|
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||||
import org.elasticsearch.common.settings.ImmutableSettings.Builder;
|
import org.elasticsearch.common.settings.ImmutableSettings.Builder;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
@ -2034,5 +2035,25 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
|
||||||
.get();
|
.get();
|
||||||
assertMatchCount(response, 3l);
|
assertMatchCount(response, 3l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMapUnmappedFieldAsString() throws IOException{
|
||||||
|
// If index.percolator.map_unmapped_fields_as_string is set to true, unmapped field is mapped as an analyzed string.
|
||||||
|
ImmutableSettings.Builder settings = ImmutableSettings.settingsBuilder()
|
||||||
|
.put(indexSettings())
|
||||||
|
.put("index.percolator.map_unmapped_fields_as_string", true);
|
||||||
|
assertAcked(prepareCreate("test")
|
||||||
|
.setSettings(settings));
|
||||||
|
client().prepareIndex("test", PercolatorService.TYPE_NAME)
|
||||||
|
.setSource(jsonBuilder().startObject().field("query", matchQuery("field1", "value")).endObject()).get();
|
||||||
|
logger.info("--> Percolate doc with field1=value");
|
||||||
|
PercolateResponse response1 = client().preparePercolate()
|
||||||
|
.setIndices("test").setDocumentType("type")
|
||||||
|
.setPercolateDoc(docBuilder().setDoc(jsonBuilder().startObject().field("field1", "value").endObject()))
|
||||||
|
.execute().actionGet();
|
||||||
|
assertMatchCount(response1, 1l);
|
||||||
|
assertThat(response1.getMatches(), arrayWithSize(1));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue