diff --git a/server/src/main/java/org/opensearch/index/mapper/DerivedFieldMapper.java b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldMapper.java index 5885d08ec9ce4..c02b618bfccd5 100644 --- a/server/src/main/java/org/opensearch/index/mapper/DerivedFieldMapper.java +++ b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldMapper.java @@ -8,34 +8,15 @@ package org.opensearch.index.mapper; -import org.apache.lucene.document.DoubleField; -import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; -import org.apache.lucene.document.KeywordField; -import org.apache.lucene.document.LongField; import org.apache.lucene.index.IndexOptions; import org.apache.lucene.index.IndexableField; -import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.MultiTermQuery; -import org.apache.lucene.search.Query; -import org.opensearch.common.Nullable; -import org.opensearch.common.geo.ShapeRelation; -import org.opensearch.common.lucene.Lucene; -import org.opensearch.common.time.DateMathParser; import org.opensearch.core.xcontent.XContentBuilder; -import org.opensearch.index.query.DerivedFieldQuery; -import org.opensearch.index.query.DerivedFieldScript; -import org.opensearch.index.query.QueryShardContext; import org.opensearch.script.Script; -import org.opensearch.search.lookup.SearchLookup; -import org.opensearch.search.lookup.SourceLookup; import java.io.IOException; -import java.time.ZoneId; import java.util.Arrays; -import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.function.Function; /** @@ -94,7 +75,6 @@ public Builder(String name) { super(name); } - @Override protected List> getParameters() { return Arrays.asList(type, script); @@ -102,208 +82,16 @@ protected List> getParameters() { @Override public DerivedFieldMapper build(BuilderContext context) { - FieldMapper typeFieldMapper; - Function fieldFunction; - switch (type.getValue()) { - // TODO: add logic all supported type in derived fields - // TODO: should we support mapping settings exposed by a given field type from derived fields too? - // for example, support `format` for date type? - case KeywordFieldMapper.CONTENT_TYPE: - FieldType dummyFieldType = new FieldType(); - dummyFieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS); - KeywordFieldMapper.Builder keywordBuilder = new KeywordFieldMapper.Builder(name()); - KeywordFieldMapper.KeywordFieldType keywordFieldType = keywordBuilder.buildFieldType(context, dummyFieldType); - keywordFieldType.setIndexAnalyzer(Lucene.KEYWORD_ANALYZER); - typeFieldMapper = new KeywordFieldMapper( - name(), dummyFieldType, keywordFieldType, - keywordBuilder.multiFieldsBuilder.build(this, context), - keywordBuilder.copyTo.build(), - keywordBuilder - ); - fieldFunction = o -> new KeywordField(name(), (String) o, Field.Store.NO); - break; - case "long": - // ignoreMalformed? - NumberFieldMapper.Builder longBuilder = new NumberFieldMapper.Builder(name, NumberFieldMapper.NumberType.LONG, false, false); - typeFieldMapper = longBuilder.build(context); - fieldFunction = o -> new LongField(name(), Long.parseLong(o.toString()), Field.Store.NO); - break; - case "double": - // ignoreMalformed? - NumberFieldMapper.Builder doubleBuilder = new NumberFieldMapper.Builder(name, NumberFieldMapper.NumberType.DOUBLE, false, false); - typeFieldMapper = doubleBuilder.build(context); - fieldFunction = o -> new DoubleField(name(), Double.parseDouble(o.toString()), Field.Store.NO); - break; - default: - throw new IllegalArgumentException("Field [" + name() + "] of type [" + type + "] isn't supported " + - "in Derived field context."); - - } - MappedFieldType ft = new DerivedFieldType(buildFullName(context), type.getValue(), script.getValue(), typeFieldMapper, fieldFunction); + FieldMapper fieldMapper = DerivedFieldSupportedTypes.getFieldMapperFromType(type.getValue(), name, context); + Function fieldFunction = + DerivedFieldSupportedTypes.getIndexableFieldGeneratorType(type.getValue(), name); + DerivedFieldType ft = new DerivedFieldType(buildFullName(context), type.getValue(), script.getValue(), fieldMapper, fieldFunction); return new DerivedFieldMapper(name, ft, multiFieldsBuilder.build(this, context), copyTo.build(), this); } } - public static final TypeParser PARSER = new TypeParser((n, c) -> new Builder(n)); - - /** - * Field type for derived field mapper - * - * @opensearch.internal - */ - public static final class DerivedFieldType extends MappedFieldType { - private final String type; - - private final Script script; - - FieldMapper typeFieldMapper; - - private final Function fieldFunction; - - public DerivedFieldType( - String name, - String type, - Script script, - boolean isIndexed, - boolean isStored, - boolean hasDocValues, - Map meta, - FieldMapper typeFieldMapper, - Function fieldFunction - ) { - super(name, isIndexed, isStored, hasDocValues, typeFieldMapper.fieldType().getTextSearchInfo(), meta); - this.type = type; - this.script = script; - this.typeFieldMapper = typeFieldMapper; - this.fieldFunction = fieldFunction; - } - - public DerivedFieldType(String name, String type, Script script, FieldMapper typeFieldMapper, Function fieldFunction) { - this(name, type, script, false, false, false, Collections.emptyMap(), typeFieldMapper, fieldFunction); - } - - @Override - public String typeName() { - return CONTENT_TYPE; - } - - @Override - public ValueFetcher valueFetcher(QueryShardContext context, SearchLookup searchLookup, String format) { - if (format != null) { - throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] doesn't support formats."); - } - DerivedFieldScript.Factory factory = context.compile(script, DerivedFieldScript.CONTEXT); - DerivedFieldScript.LeafFactory derivedFieldScriptFactory = factory.newFactory(script.getParams(), context.lookup()); - - return new SourceValueFetcher(name(), context) { - DerivedFieldScript derivedFieldScript; - - @Override - public List fetchValues(SourceLookup lookup) { - derivedFieldScript.setDocument(lookup.docId()); - // TODO: remove List.of() when derivedFieldScript.execute() returns list of objects. - return List.of(derivedFieldScript.execute()); - } - - @Override - protected Object parseSourceValue(Object value) { - return value; - } - - public void setNextReader(LeafReaderContext context) { - try { - derivedFieldScript = derivedFieldScriptFactory.newInstance(context); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - }; - } - - @Override - public Query termQuery(Object value, @Nullable QueryShardContext context) { - Query query = typeFieldMapper.mappedFieldType.termQuery(value, context); - DerivedFieldScript.Factory factory = context.compile(script, DerivedFieldScript.CONTEXT); - DerivedFieldScript.LeafFactory derivedFieldScript = factory.newFactory(script.getParams(), context.lookup()); - return new DerivedFieldQuery(query, derivedFieldScript, typeFieldMapper.mappedFieldType.indexAnalyzer(), fieldFunction, context); - } - - @Override - public Query prefixQuery( - String value, - @Nullable MultiTermQuery.RewriteMethod method, - boolean caseInsensitive, - QueryShardContext context - ) { - Query query = typeFieldMapper.mappedFieldType.prefixQuery(value, method, caseInsensitive, context); - DerivedFieldScript.Factory factory = context.compile(script, DerivedFieldScript.CONTEXT); - DerivedFieldScript.LeafFactory derivedFieldScript = factory.newFactory(script.getParams(), context.lookup()); - return new DerivedFieldQuery(query, derivedFieldScript, typeFieldMapper.mappedFieldType.indexAnalyzer(), fieldFunction, context); - } - - @Override - public Query wildcardQuery( - String value, - @Nullable MultiTermQuery.RewriteMethod method, - boolean caseInsensitive, - QueryShardContext context - ) { - Query query = typeFieldMapper.mappedFieldType.wildcardQuery(value, method, caseInsensitive, context); - DerivedFieldScript.Factory factory = context.compile(script, DerivedFieldScript.CONTEXT); - DerivedFieldScript.LeafFactory derivedFieldScript = factory.newFactory(script.getParams(), context.lookup()); - return new DerivedFieldQuery(query, derivedFieldScript, typeFieldMapper.mappedFieldType.indexAnalyzer(), fieldFunction, context); - } - - @Override - public Query regexpQuery( - String value, - int syntaxFlags, - int matchFlags, - int maxDeterminizedStates, - @Nullable MultiTermQuery.RewriteMethod method, - QueryShardContext context - ) { - Query query = typeFieldMapper.mappedFieldType.regexpQuery(value, syntaxFlags, matchFlags, - maxDeterminizedStates, method, context); - DerivedFieldScript.Factory factory = context.compile(script, DerivedFieldScript.CONTEXT); - DerivedFieldScript.LeafFactory derivedFieldScript = factory.newFactory(script.getParams(), context.lookup()); - return new DerivedFieldQuery(query, derivedFieldScript, typeFieldMapper.mappedFieldType.indexAnalyzer(), fieldFunction, context); - } - - // TODO: Override all types of queries which can be supported by all types supported within derived fields. - @Override - public Query rangeQuery( - Object lowerTerm, - Object upperTerm, - boolean includeLower, - boolean includeUpper, - ShapeRelation relation, - ZoneId timeZone, - DateMathParser parser, - QueryShardContext context - ) { - Query query = typeFieldMapper.mappedFieldType.rangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, - relation, timeZone, parser, context); - DerivedFieldScript.Factory factory = context.compile(script, DerivedFieldScript.CONTEXT); - DerivedFieldScript.LeafFactory derivedFieldScript = factory.newFactory(script.getParams(), context.lookup()); - return new DerivedFieldQuery(query, derivedFieldScript, typeFieldMapper.mappedFieldType.indexAnalyzer(), fieldFunction, context); - } - - @Override - public Query existsQuery(QueryShardContext context) { - throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] does not support exist queries"); - } - - @Override - public boolean isAggregatable() { - return false; - } - } - - private final String type; - private final Script script; protected DerivedFieldMapper( diff --git a/server/src/main/java/org/opensearch/index/mapper/DerivedFieldSupportedTypes.java b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldSupportedTypes.java new file mode 100644 index 0000000000000..c70a79441cbf3 --- /dev/null +++ b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldSupportedTypes.java @@ -0,0 +1,101 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.index.mapper; + +import org.apache.lucene.document.DoubleField; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.FieldType; +import org.apache.lucene.document.KeywordField; +import org.apache.lucene.document.LongField; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.IndexableField; +import org.opensearch.common.lucene.Lucene; + +import java.util.Arrays; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +public enum DerivedFieldSupportedTypes { + KEYWORD ( + "keyword", + (name, context) -> { + FieldType dummyFieldType = new FieldType(); + dummyFieldType.setIndexOptions(IndexOptions.DOCS_AND_FREQS); + KeywordFieldMapper.Builder keywordBuilder = new KeywordFieldMapper.Builder(name); + KeywordFieldMapper.KeywordFieldType keywordFieldType = keywordBuilder.buildFieldType(context, dummyFieldType); + keywordFieldType.setIndexAnalyzer(Lucene.KEYWORD_ANALYZER); + return new KeywordFieldMapper( + name, dummyFieldType, keywordFieldType, + keywordBuilder.multiFieldsBuilder.build(keywordBuilder, context), + keywordBuilder.copyTo.build(), + keywordBuilder + ); + }, + name -> o -> new KeywordField(name, (String) o, Field.Store.NO) + ), + LONG ( + "long", + (name, context) -> { + NumberFieldMapper.Builder longBuilder = new NumberFieldMapper.Builder(name, NumberFieldMapper.NumberType.LONG, false, false); + return longBuilder.build(context); + }, + name -> o -> new LongField(name, Long.parseLong(o.toString()), Field.Store.NO) + ), + DOUBLE ( + "double", + (name, context) -> { + NumberFieldMapper.Builder doubleBuilder = new NumberFieldMapper.Builder(name, NumberFieldMapper.NumberType.DOUBLE, false, false); + return doubleBuilder.build(context); + }, + name -> o -> new DoubleField(name, Double.parseDouble(o.toString()), Field.Store.NO) + ); + // TODO: add logic all supported type in derived fields + // TODO: should we support mapping settings exposed by a given field type from derived fields too? + // for example, support `format` for date type? + final String name; + private final BiFunction builder; + + private final Function> indexableFieldBuilder; + + DerivedFieldSupportedTypes(String name, BiFunction builder, + Function> indexableFieldBuilder){ + this.name = name; + this.builder = builder; + this.indexableFieldBuilder = indexableFieldBuilder; + } + + public String getName() { + return name; + } + private FieldMapper getFieldMapper(String name, Mapper.BuilderContext context) { + return builder.apply(name, context); + } + + private Function getIndexableFieldGenerator(String name) { + return indexableFieldBuilder.apply(name); + } + private static final Map enumMap = Arrays.stream(DerivedFieldSupportedTypes.values()) + .collect(Collectors.toMap(DerivedFieldSupportedTypes::getName, enumValue -> enumValue)); + + public static FieldMapper getFieldMapperFromType(String type, String name, Mapper.BuilderContext context) { + if (!enumMap.containsKey(type)) { + throw new IllegalArgumentException("Type [" + type + "] isn't supported in Derived field context."); + } + return enumMap.get(type).getFieldMapper(name, context); + } + + public static Function getIndexableFieldGeneratorType(String type, String name) { + if (!enumMap.containsKey(type)) { + throw new IllegalArgumentException("Type [" + type + "] isn't supported in Derived field context."); + } + return enumMap.get(type).getIndexableFieldGenerator(name); + } +} diff --git a/server/src/main/java/org/opensearch/index/mapper/DerivedFieldType.java b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldType.java new file mode 100644 index 0000000000000..53887e85bd0cd --- /dev/null +++ b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldType.java @@ -0,0 +1,155 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.index.mapper; + +import org.apache.lucene.index.IndexableField; +import org.apache.lucene.search.MultiTermQuery; +import org.apache.lucene.search.Query; +import org.opensearch.common.Nullable; +import org.opensearch.common.geo.ShapeRelation; +import org.opensearch.common.time.DateMathParser; +import org.opensearch.index.query.DerivedFieldQuery; +import org.opensearch.index.query.QueryShardContext; +import org.opensearch.script.Script; +import org.opensearch.search.lookup.SearchLookup; + +import java.time.ZoneId; +import java.util.Collections; +import java.util.Map; +import java.util.function.Function; + + +/** + * MappedFieldType for {@link DerivedFieldMapper} + * Contains logic to different type of queries on derived fields + * @opensearch.internal + */ +public final class DerivedFieldType extends MappedFieldType { + private final String type; + + private final Script script; + + FieldMapper typeFieldMapper; + + private final Function indexableFieldGenerator; + + public DerivedFieldType( + String name, + String type, + Script script, + boolean isIndexed, + boolean isStored, + boolean hasDocValues, + Map meta, + FieldMapper typeFieldMapper, + Function fieldFunction + ) { + super(name, isIndexed, isStored, hasDocValues, typeFieldMapper.fieldType().getTextSearchInfo(), meta); + this.type = type; + this.script = script; + this.typeFieldMapper = typeFieldMapper; + this.indexableFieldGenerator = fieldFunction; + } + + public DerivedFieldType(String name, String type, Script script, FieldMapper typeFieldMapper, Function fieldFunction) { + this(name, type, script, false, false, false, Collections.emptyMap(), typeFieldMapper, fieldFunction); + } + + @Override + public String typeName() { + return DerivedFieldMapper.CONTENT_TYPE; + } + + public String getType() { + return type; + } + + @Override + public DerivedFieldValueFetcher valueFetcher(QueryShardContext context, SearchLookup searchLookup, String format) { + if (format != null) { + throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] doesn't support formats."); + } + return new DerivedFieldValueFetcher(context, script); + } + + @Override + public Query termQuery(Object value, @Nullable QueryShardContext context) { + Query query = typeFieldMapper.mappedFieldType.termQuery(value, context); + DerivedFieldValueFetcher valueFetcher = new DerivedFieldValueFetcher(context, script); + return new DerivedFieldQuery(query, valueFetcher, context.lookup(), indexableFieldGenerator, typeFieldMapper.mappedFieldType.indexAnalyzer()); + } + + @Override + public Query prefixQuery( + String value, + @Nullable MultiTermQuery.RewriteMethod method, + boolean caseInsensitive, + QueryShardContext context + ) { + Query query = typeFieldMapper.mappedFieldType.prefixQuery(value, method, caseInsensitive, context); + DerivedFieldValueFetcher valueFetcher = new DerivedFieldValueFetcher(context, script); + return new DerivedFieldQuery(query, valueFetcher, context.lookup(), indexableFieldGenerator, typeFieldMapper.mappedFieldType.indexAnalyzer()); + } + + @Override + public Query wildcardQuery( + String value, + @Nullable MultiTermQuery.RewriteMethod method, + boolean caseInsensitive, + QueryShardContext context + ) { + Query query = typeFieldMapper.mappedFieldType.wildcardQuery(value, method, caseInsensitive, context); + DerivedFieldValueFetcher valueFetcher = new DerivedFieldValueFetcher(context, script); + return new DerivedFieldQuery(query, valueFetcher, context.lookup(), indexableFieldGenerator, typeFieldMapper.mappedFieldType.indexAnalyzer()); + } + + @Override + public Query regexpQuery( + String value, + int syntaxFlags, + int matchFlags, + int maxDeterminizedStates, + @Nullable MultiTermQuery.RewriteMethod method, + QueryShardContext context + ) { + Query query = typeFieldMapper.mappedFieldType.regexpQuery(value, syntaxFlags, matchFlags, + maxDeterminizedStates, method, context); + DerivedFieldValueFetcher valueFetcher = new DerivedFieldValueFetcher(context, script); + return new DerivedFieldQuery(query, valueFetcher, context.lookup(), indexableFieldGenerator, typeFieldMapper.mappedFieldType.indexAnalyzer()); + } + + // TODO: Override all types of queries which can be supported by all types supported within derived fields. + @Override + public Query rangeQuery( + Object lowerTerm, + Object upperTerm, + boolean includeLower, + boolean includeUpper, + ShapeRelation relation, + ZoneId timeZone, + DateMathParser parser, + QueryShardContext context + ) { + Query query = typeFieldMapper.mappedFieldType.rangeQuery(lowerTerm, upperTerm, includeLower, includeUpper, + relation, timeZone, parser, context); + DerivedFieldValueFetcher valueFetcher = new DerivedFieldValueFetcher(context, script); + return new DerivedFieldQuery(query, valueFetcher, context.lookup(), indexableFieldGenerator, typeFieldMapper.mappedFieldType.indexAnalyzer()); + } + + @Override + public Query existsQuery(QueryShardContext context) { + throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] does not support exist queries"); + } + + @Override + public boolean isAggregatable() { + return false; + } +} + diff --git a/server/src/main/java/org/opensearch/index/mapper/DerivedFieldValueFetcher.java b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldValueFetcher.java new file mode 100644 index 0000000000000..dd11b2ddd8f5f --- /dev/null +++ b/server/src/main/java/org/opensearch/index/mapper/DerivedFieldValueFetcher.java @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.index.mapper; + +import org.apache.lucene.index.LeafReaderContext; +import org.opensearch.index.query.DerivedFieldScript; +import org.opensearch.index.query.QueryShardContext; +import org.opensearch.script.Script; +import org.opensearch.search.lookup.SourceLookup; + +import java.io.IOException; +import java.util.List; + +/** + * ValueFetcher used by Derived Fields. + */ +public final class DerivedFieldValueFetcher implements ValueFetcher { + private DerivedFieldScript derivedFieldScript; + private final DerivedFieldScript.LeafFactory derivedFieldScriptFactory; + + DerivedFieldValueFetcher(QueryShardContext context, Script script) { + if (!context.documentMapper("").sourceMapper().enabled()) { + throw new IllegalArgumentException( + "DerivedFieldQuery error: unable to fetch fields from _source field: _source is disabled in the mappings " + + "for index [" + + context.index().getName() + + "]" + ); + } + DerivedFieldScript.Factory factory = context.compile(script, DerivedFieldScript.CONTEXT); + derivedFieldScriptFactory = factory.newFactory(script.getParams(), context.lookup()); + } + + @Override + public List fetchValues(SourceLookup lookup) { + derivedFieldScript.setDocument(lookup.docId()); + // TODO: remove List.of() when derivedFieldScript.execute() returns list of objects. + return List.of(derivedFieldScript.execute()); + } + + public void setNextReader(LeafReaderContext context) { + try { + derivedFieldScript = derivedFieldScriptFactory.newInstance(context); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/server/src/main/java/org/opensearch/index/query/DerivedFieldQuery.java b/server/src/main/java/org/opensearch/index/query/DerivedFieldQuery.java index db98aaee90fc4..948f26be6db90 100644 --- a/server/src/main/java/org/opensearch/index/query/DerivedFieldQuery.java +++ b/server/src/main/java/org/opensearch/index/query/DerivedFieldQuery.java @@ -22,44 +22,39 @@ import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.search.Weight; +import org.opensearch.index.mapper.DerivedFieldValueFetcher; +import org.opensearch.search.lookup.LeafSearchLookup; +import org.opensearch.search.lookup.SearchLookup; import java.io.IOException; +import java.util.List; import java.util.Objects; import java.util.function.Function; /** * DerivedFieldQuery used for querying derived fields. */ -public class DerivedFieldQuery extends Query { - private final Query filter; - private final DerivedFieldScript.LeafFactory derivedFieldScriptFactory; +public final class DerivedFieldQuery extends Query { + private final Query query; + private final DerivedFieldValueFetcher valueFetcher; + private final SearchLookup searchLookup; + private final Function indexableFieldGenerator; private final Analyzer indexAnalyzer; - private final Function fieldFunction; - private final QueryShardContext context; /** - * @param query query to execute against memory index - * @param derivedFieldScriptFactory derived field script's LeafFactory instance - * @param indexAnalyzer index analyzer to use while building memory index - * @param fieldFunction IndexableField generator - * @param context QueryShardContext + * + * @param query + * @param valueFetcher + * @param searchLookup + * @param indexableFieldGenerator */ - public DerivedFieldQuery(Query query, DerivedFieldScript.LeafFactory derivedFieldScriptFactory, - Analyzer indexAnalyzer, Function fieldFunction, - QueryShardContext context) { - this.filter = query; - this.derivedFieldScriptFactory = derivedFieldScriptFactory; + public DerivedFieldQuery(Query query, DerivedFieldValueFetcher valueFetcher, SearchLookup searchLookup, + Function indexableFieldGenerator, Analyzer indexAnalyzer) { + this.query = query; + this.valueFetcher = valueFetcher; + this.searchLookup = searchLookup; + this.indexableFieldGenerator = indexableFieldGenerator; this.indexAnalyzer = indexAnalyzer; - this.context = context; - this.fieldFunction = fieldFunction; - if (!context.documentMapper("").sourceMapper().enabled()) { - throw new IllegalArgumentException( - "DerivedFieldQuery error: unable to fetch fields from _source field: _source is disabled in the mappings " - + "for index [" - + context.index().getName() - + "]" - ); - } } @Override @@ -69,31 +64,34 @@ public void visit(QueryVisitor visitor) { @Override public Query rewrite(IndexSearcher indexSearcher) throws IOException { - Query rewritten = indexSearcher.rewrite(filter); - if (rewritten == filter) { + Query rewritten = indexSearcher.rewrite(query); + if (rewritten == query) { return this; } - return new DerivedFieldQuery(rewritten, derivedFieldScriptFactory, indexAnalyzer, fieldFunction, context); + return new DerivedFieldQuery(rewritten, valueFetcher, searchLookup, indexableFieldGenerator, indexAnalyzer); } @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { + return new ConstantScoreWeight(this, boost) { @Override public Scorer scorer(LeafReaderContext context) throws IOException { DocIdSetIterator approximation = DocIdSetIterator.all(context.reader().maxDoc()); - DerivedFieldScript derivedFieldScript = derivedFieldScriptFactory.newInstance(context); + valueFetcher.setNextReader(context); + LeafSearchLookup leafSearchLookup = searchLookup.getLeafSearchLookup(context); TwoPhaseIterator twoPhase = new TwoPhaseIterator(approximation) { @Override public boolean matches() { - derivedFieldScript.setDocument(approximation.docID()); - Object value = derivedFieldScript.execute(); + leafSearchLookup.source().setSegmentAndDocument(context, approximation.docID()); + List values = valueFetcher.fetchValues(leafSearchLookup.source()); // TODO: in case of errors from script, should it be ignored and treated as missing field // by using a configurable setting? MemoryIndex memoryIndex = new MemoryIndex(); - // TODO add support for multi-field with use of emit() once available - memoryIndex.addField(fieldFunction.apply(value), indexAnalyzer); - float score = memoryIndex.search(filter); + for (Object value : values) { + memoryIndex.addField(indexableFieldGenerator.apply(value), indexAnalyzer); + } + float score = memoryIndex.search(query); return score > 0.0f; } @@ -125,20 +123,20 @@ public boolean equals(Object o) { return false; } DerivedFieldQuery other = (DerivedFieldQuery) o; - return Objects.equals(this.filter, other.filter) - && Objects.equals(this.derivedFieldScriptFactory, other.derivedFieldScriptFactory) - && Objects.equals(this.fieldFunction, other.fieldFunction) - && Objects.equals(this.indexAnalyzer, other.indexAnalyzer) - && Objects.equals(this.context, other.context); + return Objects.equals(this.query, other.query) + && Objects.equals(this.valueFetcher, other.valueFetcher) + && Objects.equals(this.searchLookup, other.searchLookup) + && Objects.equals(this.indexableFieldGenerator, other.indexableFieldGenerator) + && Objects.equals(this.indexAnalyzer, other.indexAnalyzer); } @Override public int hashCode() { - return Objects.hash(classHash(), filter, derivedFieldScriptFactory, fieldFunction, indexAnalyzer, context); + return Objects.hash(classHash(), query, valueFetcher, searchLookup, indexableFieldGenerator, indexableFieldGenerator); } @Override public String toString(String f) { - return "DerivedFieldQuery (filter query: [ " + filter.toString(f) + "])"; + return "DerivedFieldQuery (Query: [ " + query.toString(f) + "])"; } }