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
|
||||
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 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:
|
||||
private final IndexQueryParserService queryParserService;
|
||||
private final MapperService mapperService;
|
||||
|
@ -85,6 +87,8 @@ public class PercolatorQueriesRegistry extends AbstractIndexShardComponent imple
|
|||
private final PercolateTypeListener percolateTypeListener = new PercolateTypeListener();
|
||||
private final AtomicBoolean realTimePercolatorEnabled = new AtomicBoolean(false);
|
||||
|
||||
private boolean mapUnmappedFieldsAsString;
|
||||
|
||||
private CloseableThreadLocal<QueryParseContext> cache = new CloseableThreadLocal<QueryParseContext>() {
|
||||
@Override
|
||||
protected QueryParseContext initialValue() {
|
||||
|
@ -104,6 +108,7 @@ public class PercolatorQueriesRegistry extends AbstractIndexShardComponent imple
|
|||
this.indexCache = indexCache;
|
||||
this.indexFieldDataService = indexFieldDataService;
|
||||
this.shardPercolateService = shardPercolateService;
|
||||
this.mapUnmappedFieldsAsString = indexSettings.getAsBoolean(MAP_UNMAPPED_FIELDS_AS_STRING, false);
|
||||
|
||||
indicesLifecycle.addListener(shardLifecycleListener);
|
||||
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),
|
||||
// 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
|
||||
//
|
||||
// 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.setMapUnmappedFieldAsString(mapUnmappedFieldsAsString ? true : false);
|
||||
return queryParserService.parseInnerQuery(context);
|
||||
} catch (IOException 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.Queries;
|
||||
import org.elasticsearch.common.lucene.search.ResolvableFilter;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.index.Index;
|
||||
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.mapper.FieldMapper;
|
||||
import org.elasticsearch.index.mapper.FieldMappers;
|
||||
import org.elasticsearch.index.mapper.Mapper;
|
||||
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.similarity.SimilarityService;
|
||||
import org.elasticsearch.script.ScriptService;
|
||||
|
@ -102,6 +107,8 @@ public class QueryParseContext {
|
|||
|
||||
private boolean allowUnmappedFields;
|
||||
|
||||
private boolean mapUnmappedFieldAsString;
|
||||
|
||||
public QueryParseContext(Index index, IndexQueryParserService indexQueryParser) {
|
||||
this(index, indexQueryParser, false);
|
||||
}
|
||||
|
@ -389,10 +396,6 @@ public class QueryParseContext {
|
|||
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) {
|
||||
return indexQueryParser.mapperService.smartNameObjectMapper(name, getTypes());
|
||||
}
|
||||
|
@ -401,9 +404,17 @@ public class QueryParseContext {
|
|||
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) {
|
||||
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 {
|
||||
Version indexCreatedVersion = indexQueryParser.getIndexCreatedVersion();
|
||||
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.Requests;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings;
|
||||
import org.elasticsearch.common.settings.ImmutableSettings.Builder;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
@ -2034,5 +2035,25 @@ public class PercolatorTests extends ElasticsearchIntegrationTest {
|
|||
.get();
|
||||
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