
org.elasticsearch.index.mapper.DocumentParserContext Maven / Gradle / Ivy
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.index.mapper;
import org.apache.lucene.document.Field;
import org.elasticsearch.common.time.DateFormatter;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.analysis.IndexAnalyzers;
import org.elasticsearch.xcontent.XContentParser;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/**
* Context used when parsing incoming documents. Holds everything that is needed to parse a document as well as
* the lucene data structures and mappings to be dynamically created as the outcome of parsing a document.
*/
public abstract class DocumentParserContext {
/**
* Wraps a given context while allowing to override some of its behaviour by re-implementing some of the non final methods
*/
private static class Wrapper extends DocumentParserContext {
private final DocumentParserContext in;
private Wrapper(DocumentParserContext in) {
super(in);
this.in = in;
}
@Override
public Iterable nonRootDocuments() {
return in.nonRootDocuments();
}
@Override
public boolean isWithinCopyTo() {
return in.isWithinCopyTo();
}
@Override
public boolean isWithinMultiFields() {
return in.isWithinMultiFields();
}
@Override
public ContentPath path() {
return in.path();
}
@Override
public XContentParser parser() {
return in.parser();
}
@Override
public LuceneDocument rootDoc() {
return in.rootDoc();
}
@Override
public List docs() {
return in.docs();
}
@Override
public LuceneDocument doc() {
return in.doc();
}
@Override
protected void addDoc(LuceneDocument doc) {
in.addDoc(doc);
}
}
private final IndexSettings indexSettings;
private final IndexAnalyzers indexAnalyzers;
private final MappingLookup mappingLookup;
private final Function parserContextFunction;
private final SourceToParse sourceToParse;
private final Set ignoredFields;
private final List dynamicMappers;
private final Set newFieldsSeen;
private final Map dynamicObjectMappers;
private final List dynamicRuntimeFields;
private Field version;
private SeqNoFieldMapper.SequenceIDFields seqID;
private DocumentParserContext(DocumentParserContext in) {
this.mappingLookup = in.mappingLookup;
this.indexSettings = in.indexSettings;
this.indexAnalyzers = in.indexAnalyzers;
this.parserContextFunction = in.parserContextFunction;
this.sourceToParse = in.sourceToParse;
this.ignoredFields = in.ignoredFields;
this.dynamicMappers = in.dynamicMappers;
this.newFieldsSeen = in.newFieldsSeen;
this.dynamicObjectMappers = in.dynamicObjectMappers;
this.dynamicRuntimeFields = in.dynamicRuntimeFields;
this.version = in.version;
this.seqID = in.seqID;
}
protected DocumentParserContext(
MappingLookup mappingLookup,
IndexSettings indexSettings,
IndexAnalyzers indexAnalyzers,
Function parserContextFunction,
SourceToParse source
) {
this.mappingLookup = mappingLookup;
this.indexSettings = indexSettings;
this.indexAnalyzers = indexAnalyzers;
this.parserContextFunction = parserContextFunction;
this.sourceToParse = source;
this.ignoredFields = new HashSet<>();
this.dynamicMappers = new ArrayList<>();
this.newFieldsSeen = new HashSet<>();
this.dynamicObjectMappers = new HashMap<>();
this.dynamicRuntimeFields = new ArrayList<>();
}
public final IndexSettings indexSettings() {
return indexSettings;
}
public final IndexAnalyzers indexAnalyzers() {
return indexAnalyzers;
}
public final RootObjectMapper root() {
return this.mappingLookup.getMapping().getRoot();
}
public final MappingLookup mappingLookup() {
return mappingLookup;
}
public final MetadataFieldMapper getMetadataMapper(String mapperName) {
return mappingLookup.getMapping().getMetadataMapperByName(mapperName);
}
public final MappingParserContext dynamicTemplateParserContext(DateFormatter dateFormatter) {
return parserContextFunction.apply(dateFormatter);
}
public final SourceToParse sourceToParse() {
return this.sourceToParse;
}
/**
* Add the given {@code field} to the set of ignored fields.
*/
public final void addIgnoredField(String field) {
ignoredFields.add(field);
}
/**
* Return the collection of fields that have been ignored so far.
*/
public final Collection getIgnoredFields() {
return Collections.unmodifiableCollection(ignoredFields);
}
/**
* Add the given {@code field} to the _field_names field
*
* Use this if an exists query run against the field cannot use docvalues
* or norms.
*/
public final void addToFieldNames(String field) {
FieldNamesFieldMapper fieldNamesFieldMapper = (FieldNamesFieldMapper) getMetadataMapper(FieldNamesFieldMapper.NAME);
if (fieldNamesFieldMapper != null) {
fieldNamesFieldMapper.addFieldNames(this, field);
}
}
public final Field version() {
return this.version;
}
public final void version(Field version) {
this.version = version;
}
public final SeqNoFieldMapper.SequenceIDFields seqID() {
return this.seqID;
}
public final void seqID(SeqNoFieldMapper.SequenceIDFields seqID) {
this.seqID = seqID;
}
/**
* Add a new mapper dynamically created while parsing.
*/
public final void addDynamicMapper(Mapper mapper) {
// eagerly check field name limit here to avoid OOM errors
// only check fields that are not already mapped or tracked in order to avoid hitting field limit too early via double-counting
// note that existing fields can also receive dynamic mapping updates (e.g. constant_keyword to fix the value)
if (mappingLookup.getMapper(mapper.name()) == null
&& mappingLookup.objectMappers().containsKey(mapper.name()) == false
&& newFieldsSeen.add(mapper.name())) {
mappingLookup.checkFieldLimit(indexSettings().getMappingTotalFieldsLimit(), newFieldsSeen.size());
}
if (mapper instanceof ObjectMapper) {
dynamicObjectMappers.put(mapper.name(), (ObjectMapper) mapper);
}
dynamicMappers.add(mapper);
}
/**
* Get dynamic mappers created while parsing.
*/
public final List getDynamicMappers() {
return dynamicMappers;
}
public final boolean isShadowed(String field) {
return mappingLookup.isShadowed(field);
}
public final ObjectMapper getObjectMapper(String name) {
return dynamicObjectMappers.get(name);
}
/**
* Add a new runtime field dynamically created while parsing.
*/
public final void addDynamicRuntimeField(RuntimeField runtimeField) {
dynamicRuntimeFields.add(runtimeField);
}
/**
* Get dynamic runtime fields created while parsing.
*/
public final List getDynamicRuntimeFields() {
return Collections.unmodifiableList(dynamicRuntimeFields);
}
/**
* Returns an Iterable over all non-root documents. If there are no non-root documents
* the iterable will return an empty iterator.
*/
public abstract Iterable nonRootDocuments();
/**
* Return a new context that will be within a copy-to operation.
*/
public final DocumentParserContext createCopyToContext() {
return new Wrapper(this) {
@Override
public boolean isWithinCopyTo() {
return true;
}
};
}
public boolean isWithinCopyTo() {
return false;
}
/**
* Return a new context that will be within multi-fields.
*/
public final DocumentParserContext createMultiFieldContext() {
return new Wrapper(this) {
@Override
public boolean isWithinMultiFields() {
return true;
}
};
}
public boolean isWithinMultiFields() {
return false;
}
/**
* Return a new context that will be used within a nested document.
*/
public final DocumentParserContext createNestedContext(String fullPath) {
final LuceneDocument doc = new LuceneDocument(fullPath, doc());
addDoc(doc);
return switchDoc(doc);
}
/**
* Return a new context that has the provided document as the current document.
*/
public final DocumentParserContext switchDoc(final LuceneDocument document) {
return new Wrapper(this) {
@Override
public LuceneDocument doc() {
return document;
}
};
}
/**
* Return a new context that will have the provided path.
*/
public final DocumentParserContext overridePath(final ContentPath path) {
return new Wrapper(this) {
@Override
public ContentPath path() {
return path;
}
};
}
/**
* @deprecated we are actively deprecating and removing the ability to pass
* complex objects to multifields, so try and avoid using this method
*/
@Deprecated
public final DocumentParserContext switchParser(XContentParser parser) {
return new Wrapper(this) {
@Override
public XContentParser parser() {
return parser;
}
};
}
public abstract ContentPath path();
public abstract XContentParser parser();
public abstract LuceneDocument rootDoc();
public abstract List docs();
public abstract LuceneDocument doc();
protected abstract void addDoc(LuceneDocument doc);
/**
* Find a dynamic mapping template for the given field and its matching type
*
* @param fieldName the name of the field
* @param matchType the expecting matchType of the field
* @return the matching template; otherwise returns null
* @throws MapperParsingException if the given field has a dynamic template name specified, but no template matches that name.
*/
public final DynamicTemplate findDynamicTemplate(String fieldName, DynamicTemplate.XContentFieldType matchType) {
final String pathAsString = path().pathAsText(fieldName);
final String matchTemplateName = sourceToParse().dynamicTemplates().get(pathAsString);
for (DynamicTemplate template : root().dynamicTemplates()) {
if (template.match(matchTemplateName, pathAsString, fieldName, matchType)) {
return template;
}
}
if (matchTemplateName != null) {
throw new MapperParsingException(
"Can't find dynamic template for dynamic template name [" + matchTemplateName + "] of field [" + pathAsString + "]"
);
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy