-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Remove] Type from nested fields using new metadata field mapper #3004
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* | ||
* 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.Field; | ||
import org.apache.lucene.document.FieldType; | ||
import org.apache.lucene.index.IndexOptions; | ||
import org.apache.lucene.index.Term; | ||
import org.apache.lucene.search.Query; | ||
import org.apache.lucene.search.TermQuery; | ||
import org.apache.lucene.util.BytesRef; | ||
import org.opensearch.Version; | ||
import org.opensearch.index.query.QueryShardContext; | ||
import org.opensearch.search.lookup.SearchLookup; | ||
|
||
import java.util.Collections; | ||
|
||
public class NestedPathFieldMapper extends MetadataFieldMapper { | ||
// OpenSearch version 2.0 removed types; this name is used for bwc | ||
public static final String LEGACY_NAME = "_type"; | ||
public static final String NAME = "_nested_path"; | ||
|
||
public static class Defaults { | ||
public static final FieldType FIELD_TYPE = new FieldType(); | ||
static { | ||
FIELD_TYPE.setIndexOptions(IndexOptions.DOCS); | ||
FIELD_TYPE.setTokenized(false); | ||
FIELD_TYPE.setStored(false); | ||
FIELD_TYPE.setOmitNorms(true); | ||
FIELD_TYPE.freeze(); | ||
} | ||
} | ||
|
||
/** private ctor; using SINGLETON to control BWC */ | ||
private NestedPathFieldMapper(String name) { | ||
super(new NestedPathFieldType(name)); | ||
} | ||
|
||
/** returns the field name */ | ||
public static String name(Version version) { | ||
if (version.before(Version.V_2_0_0)) { | ||
return LEGACY_NAME; | ||
} | ||
return NAME; | ||
} | ||
|
||
@Override | ||
protected String contentType() { | ||
return NAME; | ||
} | ||
|
||
private static final NestedPathFieldMapper LEGACY_INSTANCE = new NestedPathFieldMapper(LEGACY_NAME); | ||
private static final NestedPathFieldMapper INSTANCE = new NestedPathFieldMapper(NAME); | ||
|
||
public static final TypeParser PARSER = new FixedTypeParser( | ||
c -> c.indexVersionCreated().before(Version.V_2_0_0) ? LEGACY_INSTANCE : INSTANCE | ||
); | ||
|
||
/** helper method to create a lucene field based on the opensearch version */ | ||
public static Field field(Version version, String path) { | ||
return new Field(name(version), path, Defaults.FIELD_TYPE); | ||
} | ||
|
||
/** helper method to create a query based on the opensearch version */ | ||
public static Query filter(Version version, String path) { | ||
return new TermQuery(new Term(name(version), new BytesRef(path))); | ||
} | ||
|
||
/** field type for the NestPath field */ | ||
public static final class NestedPathFieldType extends StringFieldType { | ||
private NestedPathFieldType(String name) { | ||
super(name, true, false, false, TextSearchInfo.SIMPLE_MATCH_ONLY, Collections.emptyMap()); | ||
} | ||
|
||
@Override | ||
public String typeName() { | ||
return NAME; | ||
} | ||
|
||
@Override | ||
public Query existsQuery(QueryShardContext context) { | ||
throw new UnsupportedOperationException("Cannot run exists() query against the nested field path"); | ||
} | ||
|
||
@Override | ||
public ValueFetcher valueFetcher(QueryShardContext context, SearchLookup searchLookup, String format) { | ||
throw new UnsupportedOperationException("Cannot fetch values for internal field [" + name() + "]."); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,11 +32,9 @@ | |
|
||
package org.opensearch.index.mapper; | ||
|
||
import org.apache.lucene.index.Term; | ||
import org.apache.lucene.search.Query; | ||
import org.apache.lucene.search.TermQuery; | ||
import org.apache.lucene.util.BytesRef; | ||
import org.opensearch.OpenSearchParseException; | ||
import org.opensearch.Version; | ||
import org.opensearch.common.Explicit; | ||
import org.opensearch.common.Nullable; | ||
import org.opensearch.common.collect.CopyOnWriteHashMap; | ||
|
@@ -388,8 +386,7 @@ protected static void parseProperties(ObjectMapper.Builder objBuilder, Map<Strin | |
|
||
private final Nested nested; | ||
|
||
private final String nestedTypePathAsString; | ||
private final BytesRef nestedTypePathAsBytes; | ||
private final String nestedTypePath; | ||
|
||
private final Query nestedTypeFilter; | ||
|
||
|
@@ -420,9 +417,13 @@ protected static void parseProperties(ObjectMapper.Builder objBuilder, Map<Strin | |
} else { | ||
this.mappers = CopyOnWriteHashMap.copyOf(mappers); | ||
} | ||
this.nestedTypePathAsString = "__" + fullPath; | ||
this.nestedTypePathAsBytes = new BytesRef(nestedTypePathAsString); | ||
this.nestedTypeFilter = new TermQuery(new Term(TypeFieldMapper.NAME, nestedTypePathAsBytes)); | ||
Version version = Version.indexCreated(settings); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit risky call: when fixing one of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Woah! Did you find this to be true in production or could it have just been a test that didn't set the Version? I don't think we can have a case where an index is created without a version, otherwise rolling upgrades would fail? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is this one https://github.com/opensearch-project/OpenSearch/pull/2094/files, to clarify, it is not caused by absence of the index created but prefabricated index settings which may not include it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just getting back to this. The testDefaultConfig I had in there would've failed since it uses prefabricated empty settings but it was updating w/ different mappings so I added an explicit test based on #1921 to update with the same mappings and Empty settings. There are no issues; we may revisit if this rears it's head again. |
||
if (version.before(Version.V_2_0_0)) { | ||
this.nestedTypePath = "__" + fullPath; | ||
} else { | ||
this.nestedTypePath = fullPath; | ||
} | ||
this.nestedTypeFilter = NestedPathFieldMapper.filter(version, nestedTypePath); | ||
} | ||
|
||
@Override | ||
|
@@ -486,8 +487,8 @@ public String fullPath() { | |
return this.fullPath; | ||
} | ||
|
||
public String nestedTypePathAsString() { | ||
return nestedTypePathAsString; | ||
public String nestedTypePath() { | ||
return nestedTypePath; | ||
} | ||
|
||
public final Dynamic dynamic() { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* 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.opensearch.common.bytes.BytesArray; | ||
import org.opensearch.common.compress.CompressedXContent; | ||
import org.opensearch.common.settings.Settings; | ||
import org.opensearch.common.xcontent.XContentType; | ||
import org.opensearch.test.OpenSearchSingleNodeTestCase; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
import java.util.Collections; | ||
|
||
/** tests for {@link org.opensearch.index.mapper.NestedPathFieldMapper} */ | ||
public class NestedPathFieldMapperTests extends OpenSearchSingleNodeTestCase { | ||
|
||
public void testDefaultConfig() throws IOException { | ||
Settings indexSettings = Settings.EMPTY; | ||
MapperService mapperService = createIndex("test", indexSettings).mapperService(); | ||
DocumentMapper mapper = mapperService.merge( | ||
MapperService.SINGLE_MAPPING_NAME, | ||
new CompressedXContent("{\"" + MapperService.SINGLE_MAPPING_NAME + "\":{}}"), | ||
MapperService.MergeReason.MAPPING_UPDATE | ||
); | ||
ParsedDocument document = mapper.parse(new SourceToParse("index", "id", new BytesArray("{}"), XContentType.JSON)); | ||
assertEquals(Collections.<IndexableField>emptyList(), Arrays.asList(document.rootDoc().getFields(NestedPathFieldMapper.NAME))); | ||
} | ||
|
||
public void testUpdatesWithSameMappings() throws IOException { | ||
Settings indexSettings = Settings.EMPTY; | ||
MapperService mapperService = createIndex("test", indexSettings).mapperService(); | ||
DocumentMapper mapper = mapperService.merge( | ||
MapperService.SINGLE_MAPPING_NAME, | ||
new CompressedXContent("{\"" + MapperService.SINGLE_MAPPING_NAME + "\":{}}"), | ||
MapperService.MergeReason.MAPPING_UPDATE | ||
); | ||
mapper.merge(mapper.mapping(), MapperService.MergeReason.MAPPING_UPDATE); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an opinion,
_nested_path
looks too verbose, may be just_path
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up sticking with
_nested_path
because it's more descriptive when analyzing lucene segments (which has no object->field relationship). The fieldType is easily identifiable as the path for a nested field when just reading the field name.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍