Restrict Document list access in ParseContext (#29463)

Today we expose a mutable list of documents in ParseContext via
ParseContext#docs(). This, on the one hand places knowledge how
to access nested documnts in multiple places and on the other
allows for potential illegal access to nested only docs after
the docs are reversed. This change restricts the access and
streamlines nested / non-root doc access.
This commit is contained in:
Simon Willnauer 2018-04-11 15:09:44 +02:00 committed by GitHub
parent 3a147b442a
commit 45e7e24736
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 64 additions and 67 deletions

View File

@ -76,7 +76,7 @@ final class DocumentParser {
throw new IllegalStateException("found leftover path elements: " + remainingPath); throw new IllegalStateException("found leftover path elements: " + remainingPath);
} }
reverseOrder(context); context.postParse();
return parsedDocument(source, context, createDynamicUpdate(mapping, docMapper, context.getDynamicMappers())); return parsedDocument(source, context, createDynamicUpdate(mapping, docMapper, context.getDynamicMappers()));
} }
@ -141,12 +141,6 @@ final class DocumentParser {
return false; return false;
} }
private static void reverseOrder(ParseContext.InternalParseContext context) {
// reverse the order of docs for nested docs support, parent should be last
if (context.docs().size() > 1) {
Collections.reverse(context.docs());
}
}
private static ParsedDocument parsedDocument(SourceToParse source, ParseContext.InternalParseContext context, Mapping update) { private static ParsedDocument parsedDocument(SourceToParse source, ParseContext.InternalParseContext context, Mapping update) {
return new ParsedDocument( return new ParsedDocument(

View File

@ -263,7 +263,7 @@ public class FieldNamesFieldMapper extends MetadataFieldMapper {
if (fieldType().isEnabled() == false) { if (fieldType().isEnabled() == false) {
return; return;
} }
for (ParseContext.Document document : context.docs()) { for (ParseContext.Document document : context) {
final List<String> paths = new ArrayList<>(document.getFields().size()); final List<String> paths = new ArrayList<>(document.getFields().size());
String previousPath = ""; // used as a sentinel - field names can't be empty String previousPath = ""; // used as a sentinel - field names can't be empty
for (IndexableField field : document.getFields()) { for (IndexableField field : document.getFields()) {

View File

@ -267,9 +267,6 @@ public class IdFieldMapper extends MetadataFieldMapper {
super.parse(context); super.parse(context);
} }
@Override
public void postParse(ParseContext context) throws IOException {}
@Override @Override
protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException { protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
if (fieldType.indexOptions() != IndexOptions.NONE || fieldType.stored()) { if (fieldType.indexOptions() != IndexOptions.NONE || fieldType.stored()) {

View File

@ -172,9 +172,6 @@ public class IndexFieldMapper extends MetadataFieldMapper {
@Override @Override
public void preParse(ParseContext context) throws IOException {} public void preParse(ParseContext context) throws IOException {}
@Override
public void postParse(ParseContext context) throws IOException {}
@Override @Override
protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {} protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {}

View File

@ -64,7 +64,9 @@ public abstract class MetadataFieldMapper extends FieldMapper {
/** /**
* Called after {@link FieldMapper#parse(ParseContext)} on the {@link RootObjectMapper}. * Called after {@link FieldMapper#parse(ParseContext)} on the {@link RootObjectMapper}.
*/ */
public abstract void postParse(ParseContext context) throws IOException; public void postParse(ParseContext context) throws IOException {
// do nothing
}
@Override @Override
public MetadataFieldMapper merge(Mapper mergeWith) { public MetadataFieldMapper merge(Mapper mergeWith) {

View File

@ -29,10 +29,11 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
public abstract class ParseContext { public abstract class ParseContext implements Iterable<ParseContext.Document>{
/** Fork of {@link org.apache.lucene.document.Document} with additional functionality. */ /** Fork of {@link org.apache.lucene.document.Document} with additional functionality. */
public static class Document implements Iterable<IndexableField> { public static class Document implements Iterable<IndexableField> {
@ -171,6 +172,11 @@ public abstract class ParseContext {
this.in = in; this.in = in;
} }
@Override
public Iterable<Document> nonRootDocuments() {
return in.nonRootDocuments();
}
@Override @Override
public DocumentMapperParser docMapperParser() { public DocumentMapperParser docMapperParser() {
return in.docMapperParser(); return in.docMapperParser();
@ -211,11 +217,6 @@ public abstract class ParseContext {
return in.rootDoc(); return in.rootDoc();
} }
@Override
public List<Document> docs() {
return in.docs();
}
@Override @Override
public Document doc() { public Document doc() {
return in.doc(); return in.doc();
@ -280,6 +281,11 @@ public abstract class ParseContext {
public List<Mapper> getDynamicMappers() { public List<Mapper> getDynamicMappers() {
return in.getDynamicMappers(); return in.getDynamicMappers();
} }
@Override
public Iterator<Document> iterator() {
return in.iterator();
}
} }
public static class InternalParseContext extends ParseContext { public static class InternalParseContext extends ParseContext {
@ -309,9 +315,10 @@ public abstract class ParseContext {
private long numNestedDocs; private long numNestedDocs;
private final List<Mapper> dynamicMappers; private final List<Mapper> dynamicMappers;
private boolean docsReversed = false;
public InternalParseContext(@Nullable Settings indexSettings, DocumentMapperParser docMapperParser, DocumentMapper docMapper, public InternalParseContext(@Nullable Settings indexSettings, DocumentMapperParser docMapperParser, DocumentMapper docMapper,
SourceToParse source, XContentParser parser) { SourceToParse source, XContentParser parser) {
this.indexSettings = indexSettings; this.indexSettings = indexSettings;
@ -360,8 +367,7 @@ public abstract class ParseContext {
return documents.get(0); return documents.get(0);
} }
@Override List<Document> docs() {
public List<Document> docs() {
return this.documents; return this.documents;
} }
@ -426,8 +432,35 @@ public abstract class ParseContext {
public List<Mapper> getDynamicMappers() { public List<Mapper> getDynamicMappers() {
return dynamicMappers; return dynamicMappers;
} }
@Override
public Iterable<Document> nonRootDocuments() {
if (docsReversed) {
throw new IllegalStateException("documents are already reversed");
}
return documents.subList(1, documents.size());
}
void postParse() {
// reverse the order of docs for nested docs support, parent should be last
if (documents.size() > 1) {
docsReversed = true;
Collections.reverse(documents);
}
}
@Override
public Iterator<Document> iterator() {
return documents.iterator();
}
} }
/**
* 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<Document> nonRootDocuments();
public abstract DocumentMapperParser docMapperParser(); public abstract DocumentMapperParser docMapperParser();
/** /**
@ -506,8 +539,6 @@ public abstract class ParseContext {
public abstract Document rootDoc(); public abstract Document rootDoc();
public abstract List<Document> docs();
public abstract Document doc(); public abstract Document doc();
protected abstract void addDoc(Document doc); protected abstract void addDoc(Document doc);

View File

@ -157,10 +157,6 @@ public class RoutingFieldMapper extends MetadataFieldMapper {
super.parse(context); super.parse(context);
} }
@Override
public void postParse(ParseContext context) throws IOException {
}
@Override @Override
public Mapper parse(ParseContext context) throws IOException { public Mapper parse(ParseContext context) throws IOException {
// no need ot parse here, we either get the routing in the sourceToParse // no need ot parse here, we either get the routing in the sourceToParse

View File

@ -253,11 +253,9 @@ public class SeqNoFieldMapper extends MetadataFieldMapper {
// we share the parent docs fields to ensure good compression // we share the parent docs fields to ensure good compression
SequenceIDFields seqID = context.seqID(); SequenceIDFields seqID = context.seqID();
assert seqID != null; assert seqID != null;
int numDocs = context.docs().size();
final Version versionCreated = context.mapperService().getIndexSettings().getIndexVersionCreated(); final Version versionCreated = context.mapperService().getIndexSettings().getIndexVersionCreated();
final boolean includePrimaryTerm = versionCreated.before(Version.V_6_1_0); final boolean includePrimaryTerm = versionCreated.before(Version.V_6_1_0);
for (int i = 1; i < numDocs; i++) { for (Document doc : context.nonRootDocuments()) {
final Document doc = context.docs().get(i);
doc.add(seqID.seqNo); doc.add(seqID.seqNo);
doc.add(seqID.seqNoDocValue); doc.add(seqID.seqNoDocValue);
if (includePrimaryTerm) { if (includePrimaryTerm) {

View File

@ -216,10 +216,6 @@ public class SourceFieldMapper extends MetadataFieldMapper {
super.parse(context); super.parse(context);
} }
@Override
public void postParse(ParseContext context) throws IOException {
}
@Override @Override
public Mapper parse(ParseContext context) throws IOException { public Mapper parse(ParseContext context) throws IOException {
// nothing to do here, we will call it in pre parse // nothing to do here, we will call it in pre parse
@ -228,32 +224,23 @@ public class SourceFieldMapper extends MetadataFieldMapper {
@Override @Override
protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException { protected void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException {
if (!enabled) {
return;
}
if (!fieldType().stored()) {
return;
}
BytesReference source = context.sourceToParse().source(); BytesReference source = context.sourceToParse().source();
// Percolate and tv APIs may not set the source and that is ok, because these APIs will not index any data if (enabled && fieldType().stored() && source != null) {
if (source == null) { // Percolate and tv APIs may not set the source and that is ok, because these APIs will not index any data
return; if (filter != null) {
// we don't update the context source if we filter, we want to keep it as is...
Tuple<XContentType, Map<String, Object>> mapTuple =
XContentHelper.convertToMap(source, true, context.sourceToParse().getXContentType());
Map<String, Object> filteredSource = filter.apply(mapTuple.v2());
BytesStreamOutput bStream = new BytesStreamOutput();
XContentType contentType = mapTuple.v1();
XContentBuilder builder = XContentFactory.contentBuilder(contentType, bStream).map(filteredSource);
builder.close();
source = bStream.bytes();
}
BytesRef ref = source.toBytesRef();
fields.add(new StoredField(fieldType().name(), ref.bytes, ref.offset, ref.length));
} }
if (filter != null) {
// we don't update the context source if we filter, we want to keep it as is...
Tuple<XContentType, Map<String, Object>> mapTuple =
XContentHelper.convertToMap(source, true, context.sourceToParse().getXContentType());
Map<String, Object> filteredSource = filter.apply(mapTuple.v2());
BytesStreamOutput bStream = new BytesStreamOutput();
XContentType contentType = mapTuple.v1();
XContentBuilder builder = XContentFactory.contentBuilder(contentType, bStream).map(filteredSource);
builder.close();
source = bStream.bytes();
}
BytesRef ref = source.toBytesRef();
fields.add(new StoredField(fieldType().name(), ref.bytes, ref.offset, ref.length));
} }
@Override @Override

View File

@ -269,10 +269,6 @@ public class TypeFieldMapper extends MetadataFieldMapper {
super.parse(context); super.parse(context);
} }
@Override
public void postParse(ParseContext context) throws IOException {
}
@Override @Override
public Mapper parse(ParseContext context) throws IOException { public Mapper parse(ParseContext context) throws IOException {
// we parse in pre parse // we parse in pre parse

View File

@ -128,8 +128,7 @@ public class VersionFieldMapper extends MetadataFieldMapper {
// that don't have the field. This is consistent with the default value for efficiency. // that don't have the field. This is consistent with the default value for efficiency.
Field version = context.version(); Field version = context.version();
assert version != null; assert version != null;
for (int i = 1; i < context.docs().size(); i++) { for (Document doc : context.nonRootDocuments()) {
final Document doc = context.docs().get(i);
doc.add(version); doc.add(version);
} }
} }