From 20e7a64d518e7d97ce67a14fa28cf07855891384 Mon Sep 17 00:00:00 2001 From: Jim Ferenczi Date: Wed, 3 Oct 2018 11:55:30 +0200 Subject: [PATCH] Preserve the order of nested documents in the Lucene index (#34225) Today we reverse the initial order of the nested documents when we index them in order to ensure that parents documents appear after their children. This means that a query will always match nested documents in the reverse order of their offsets in the source document. Reversing all documents is not needed so this change ensures that parents documents appear after their children without modifying the initial order in each nested level. This allows to match children in the order of their appearance in the source document which is a requirement to efficiently implement #33587. Old indices created before this change will continue to reverse the order of nested documents to ensure backwark compatibility. --- .../index/mapper/ParseContext.java | 33 ++++- .../search/fetch/FetchPhase.java | 33 ++++- .../index/mapper/CopyToMapperTests.java | 12 +- .../index/mapper/NestedObjectMapperTests.java | 129 +++++++++++++----- .../aggregations/metrics/TopHitsIT.java | 8 +- .../search/fetch/subphase/InnerHitsIT.java | 4 +- .../DocumentLevelSecurityTests.java | 6 + 7 files changed, 172 insertions(+), 53 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/mapper/ParseContext.java b/server/src/main/java/org/elasticsearch/index/mapper/ParseContext.java index 693dae6033af8..3552a215af95b 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/ParseContext.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/ParseContext.java @@ -26,6 +26,7 @@ import org.apache.lucene.index.IndexableField; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.all.AllEntries; +import org.elasticsearch.Version; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.IndexSettings; @@ -34,6 +35,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -456,11 +458,38 @@ public Iterable nonRootDocuments() { } void postParse() { - // reverse the order of docs for nested docs support, parent should be last if (documents.size() > 1) { docsReversed = true; - Collections.reverse(documents); + if (indexSettings.getIndexVersionCreated().onOrAfter(Version.V_6_5_0)) { + /** + * For indices created on or after {@link Version#V_6_5_0} we preserve the order + * of the children while ensuring that parents appear after them. + */ + List newDocs = reorderParent(documents); + documents.clear(); + documents.addAll(newDocs); + } else { + // reverse the order of docs for nested docs support, parent should be last + Collections.reverse(documents); + } + } + } + + /** + * Returns a copy of the provided {@link List} where parent documents appear + * after their children. + */ + private List reorderParent(List docs) { + List newDocs = new ArrayList<>(docs.size()); + LinkedList parents = new LinkedList<>(); + for (Document doc : docs) { + while (parents.peek() != doc.getParent()){ + newDocs.add(parents.poll()); + } + parents.add(0, doc); } + newDocs.addAll(parents); + return newDocs; } @Override diff --git a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java index 64ed5f4479514..0f957cd8d578b 100644 --- a/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java +++ b/server/src/main/java/org/elasticsearch/search/fetch/FetchPhase.java @@ -27,6 +27,7 @@ import org.apache.lucene.search.Weight; import org.apache.lucene.util.BitSet; import org.elasticsearch.ExceptionsHelper; +import org.elasticsearch.Version; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.collect.Tuple; import org.elasticsearch.common.document.DocumentField; @@ -35,6 +36,7 @@ import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.support.XContentMapValues; +import org.elasticsearch.index.IndexSettings; import org.elasticsearch.index.fieldvisitor.CustomFieldsVisitor; import org.elasticsearch.index.fieldvisitor.FieldsVisitor; import org.elasticsearch.index.mapper.DocumentMapper; @@ -344,6 +346,7 @@ private SearchHit.NestedIdentity getInternalNestedIdentity(SearchContext context ObjectMapper current = nestedObjectMapper; String originalName = nestedObjectMapper.name(); SearchHit.NestedIdentity nestedIdentity = null; + final IndexSettings indexSettings = context.getQueryShardContext().getIndexSettings(); do { Query parentFilter; nestedParentObjectMapper = current.getParentObjectMapper(mapperService); @@ -373,12 +376,32 @@ private SearchHit.NestedIdentity getInternalNestedIdentity(SearchContext context BitSet parentBits = context.bitsetFilterCache().getBitSetProducer(parentFilter).getBitSet(subReaderContext); int offset = 0; - int nextParent = parentBits.nextSetBit(currentParent); - for (int docId = childIter.advance(currentParent + 1); docId < nextParent && docId != DocIdSetIterator.NO_MORE_DOCS; - docId = childIter.nextDoc()) { - offset++; + if (indexSettings.getIndexVersionCreated().onOrAfter(Version.V_6_5_0)) { + /** + * Starts from the previous parent and finds the offset of the + * nestedSubDocID within the nested children. Nested documents + * are indexed in the same order than in the source array so the offset + * of the nested child is the number of nested document with the same parent + * that appear before him. + */ + int previousParent = parentBits.prevSetBit(currentParent); + for (int docId = childIter.advance(previousParent + 1); docId < nestedSubDocId && docId != DocIdSetIterator.NO_MORE_DOCS; + docId = childIter.nextDoc()) { + offset++; + } + currentParent = nestedSubDocId; + } else { + /** + * Nested documents are in reverse order in this version so we start from the current nested document + * and find the number of documents with the same parent that appear after it. + */ + int nextParent = parentBits.nextSetBit(currentParent); + for (int docId = childIter.advance(currentParent + 1); docId < nextParent && docId != DocIdSetIterator.NO_MORE_DOCS; + docId = childIter.nextDoc()) { + offset++; + } + currentParent = nextParent; } - currentParent = nextParent; current = nestedObjectMapper = nestedParentObjectMapper; int currentPrefix = current == null ? 0 : current.name().length() + 1; nestedIdentity = new SearchHit.NestedIdentity(originalName.substring(currentPrefix), offset, nestedIdentity); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java index e3d5839ab11fa..a59dceacfedc9 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/CopyToMapperTests.java @@ -389,28 +389,28 @@ public void testCopyToNestedField() throws Exception { assertEquals(6, doc.docs().size()); Document nested = doc.docs().get(0); - assertFieldValue(nested, "n1.n2.target", 7L); + assertFieldValue(nested, "n1.n2.target", 3L); assertFieldValue(nested, "n1.target"); assertFieldValue(nested, "target"); - nested = doc.docs().get(2); + nested = doc.docs().get(1); assertFieldValue(nested, "n1.n2.target", 5L); assertFieldValue(nested, "n1.target"); assertFieldValue(nested, "target"); nested = doc.docs().get(3); - assertFieldValue(nested, "n1.n2.target", 3L); + assertFieldValue(nested, "n1.n2.target", 7L); assertFieldValue(nested, "n1.target"); assertFieldValue(nested, "target"); - Document parent = doc.docs().get(1); + Document parent = doc.docs().get(2); assertFieldValue(parent, "target"); - assertFieldValue(parent, "n1.target", 7L); + assertFieldValue(parent, "n1.target", 3L, 5L); assertFieldValue(parent, "n1.n2.target"); parent = doc.docs().get(4); assertFieldValue(parent, "target"); - assertFieldValue(parent, "n1.target", 3L, 5L); + assertFieldValue(parent, "n1.target", 7L); assertFieldValue(parent, "n1.n2.target"); Document root = doc.docs().get(5); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java index a6120ac29fa6c..d590709d729a0 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java @@ -20,6 +20,8 @@ package org.elasticsearch.index.mapper; import org.apache.lucene.index.IndexableField; +import org.elasticsearch.Version; +import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.Strings; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.compress.CompressedXContent; @@ -29,6 +31,7 @@ import org.elasticsearch.index.mapper.MapperService.MergeReason; import org.elasticsearch.index.mapper.ObjectMapper.Dynamic; import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.test.VersionUtils; import java.io.IOException; import java.io.UncheckedIOException; @@ -111,11 +114,11 @@ public void testSingleNested() throws Exception { assertThat(doc.docs().size(), equalTo(3)); assertThat(doc.docs().get(0).get(TypeFieldMapper.NAME), equalTo(nested1Mapper.nestedTypePathAsString())); - assertThat(doc.docs().get(0).get("nested1.field1"), equalTo("3")); - assertThat(doc.docs().get(0).get("nested1.field2"), equalTo("4")); + assertThat(doc.docs().get(0).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(0).get("nested1.field2"), equalTo("2")); assertThat(doc.docs().get(1).get(TypeFieldMapper.NAME), equalTo(nested1Mapper.nestedTypePathAsString())); - assertThat(doc.docs().get(1).get("nested1.field1"), equalTo("1")); - assertThat(doc.docs().get(1).get("nested1.field2"), equalTo("2")); + assertThat(doc.docs().get(1).get("nested1.field1"), equalTo("3")); + assertThat(doc.docs().get(1).get("nested1.field2"), equalTo("4")); assertThat(doc.docs().get(2).get("field"), equalTo("value")); } @@ -151,20 +154,20 @@ public void testMultiNested() throws Exception { XContentType.JSON)); assertThat(doc.docs().size(), equalTo(7)); - assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("6")); + assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("2")); assertThat(doc.docs().get(0).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(0).get("field"), nullValue()); - assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("5")); + assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("3")); assertThat(doc.docs().get(1).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(1).get("field"), nullValue()); - assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("4")); + assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("1")); assertThat(doc.docs().get(2).get("nested1.nested2.field2"), nullValue()); assertThat(doc.docs().get(2).get("field"), nullValue()); - assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("3")); + assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("5")); assertThat(doc.docs().get(3).get("field"), nullValue()); - assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("2")); + assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("6")); assertThat(doc.docs().get(4).get("field"), nullValue()); - assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("4")); assertThat(doc.docs().get(5).get("nested1.nested2.field2"), nullValue()); assertThat(doc.docs().get(5).get("field"), nullValue()); assertThat(doc.docs().get(6).get("field"), equalTo("value")); @@ -203,21 +206,21 @@ public void testMultiObjectAndNested1() throws Exception { XContentType.JSON)); assertThat(doc.docs().size(), equalTo(7)); - assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("6")); + assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("2")); assertThat(doc.docs().get(0).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(0).get("field"), nullValue()); - assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("5")); + assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("3")); assertThat(doc.docs().get(1).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(1).get("field"), nullValue()); - assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("4")); - assertThat(doc.docs().get(2).get("nested1.nested2.field2"), equalTo("5")); + assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(2).get("nested1.nested2.field2"), equalTo("2")); assertThat(doc.docs().get(2).get("field"), nullValue()); - assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("3")); + assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("5")); assertThat(doc.docs().get(3).get("field"), nullValue()); - assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("2")); + assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("6")); assertThat(doc.docs().get(4).get("field"), nullValue()); - assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("1")); - assertThat(doc.docs().get(5).get("nested1.nested2.field2"), equalTo("2")); + assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("4")); + assertThat(doc.docs().get(5).get("nested1.nested2.field2"), equalTo("5")); assertThat(doc.docs().get(5).get("field"), nullValue()); assertThat(doc.docs().get(6).get("field"), equalTo("value")); assertThat(doc.docs().get(6).get("nested1.field1"), nullValue()); @@ -255,21 +258,21 @@ public void testMultiObjectAndNested2() throws Exception { XContentType.JSON)); assertThat(doc.docs().size(), equalTo(7)); - assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("6")); + assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("2")); assertThat(doc.docs().get(0).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(0).get("field"), nullValue()); - assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("5")); + assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("3")); assertThat(doc.docs().get(1).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(1).get("field"), nullValue()); - assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("4")); - assertThat(doc.docs().get(2).get("nested1.nested2.field2"), equalTo("5")); + assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(2).get("nested1.nested2.field2"), equalTo("2")); assertThat(doc.docs().get(2).get("field"), nullValue()); - assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("3")); + assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("5")); assertThat(doc.docs().get(3).get("field"), nullValue()); - assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("2")); + assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("6")); assertThat(doc.docs().get(4).get("field"), nullValue()); - assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("1")); - assertThat(doc.docs().get(5).get("nested1.nested2.field2"), equalTo("2")); + assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("4")); + assertThat(doc.docs().get(5).get("nested1.nested2.field2"), equalTo("5")); assertThat(doc.docs().get(5).get("field"), nullValue()); assertThat(doc.docs().get(6).get("field"), equalTo("value")); assertThat(doc.docs().get(6).getFields("nested1.field1").length, equalTo(2)); @@ -307,20 +310,20 @@ public void testMultiRootAndNested1() throws Exception { XContentType.JSON)); assertThat(doc.docs().size(), equalTo(7)); - assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("6")); + assertThat(doc.docs().get(0).get("nested1.nested2.field2"), equalTo("2")); assertThat(doc.docs().get(0).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(0).get("field"), nullValue()); - assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("5")); + assertThat(doc.docs().get(1).get("nested1.nested2.field2"), equalTo("3")); assertThat(doc.docs().get(1).get("nested1.field1"), nullValue()); assertThat(doc.docs().get(1).get("field"), nullValue()); - assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("4")); + assertThat(doc.docs().get(2).get("nested1.field1"), equalTo("1")); assertThat(doc.docs().get(2).get("nested1.nested2.field2"), nullValue()); assertThat(doc.docs().get(2).get("field"), nullValue()); - assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("3")); + assertThat(doc.docs().get(3).get("nested1.nested2.field2"), equalTo("5")); assertThat(doc.docs().get(3).get("field"), nullValue()); - assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("2")); + assertThat(doc.docs().get(4).get("nested1.nested2.field2"), equalTo("6")); assertThat(doc.docs().get(4).get("field"), nullValue()); - assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(5).get("nested1.field1"), equalTo("4")); assertThat(doc.docs().get(5).get("nested1.nested2.field2"), nullValue()); assertThat(doc.docs().get(5).get("field"), nullValue()); assertThat(doc.docs().get(6).get("field"), equalTo("value")); @@ -415,9 +418,9 @@ public void testNestedArrayStrict() throws Exception { XContentType.JSON)); assertThat(doc.docs().size(), equalTo(3)); - assertThat(doc.docs().get(0).get("nested1.field1"), equalTo("4")); + assertThat(doc.docs().get(0).get("nested1.field1"), equalTo("1")); assertThat(doc.docs().get(0).get("field"), nullValue()); - assertThat(doc.docs().get(1).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(1).get("nested1.field1"), equalTo("4")); assertThat(doc.docs().get(1).get("field"), nullValue()); assertThat(doc.docs().get(2).get("field"), equalTo("value")); } @@ -485,4 +488,62 @@ public void testParentObjectMapperAreNested() throws Exception { assertFalse(objectMapper.parentObjectMapperAreNested(mapperService)); } + @Override + protected boolean forbidPrivateIndexSettings() { + /** + * This is needed to force the index version with {@link IndexMetaData.SETTING_INDEX_VERSION_CREATED}. + */ + return false; + } + + public void testReorderParentBWC() throws IOException { + String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties") + .startObject("nested1").field("type", "nested").endObject() + .endObject().endObject().endObject()); + + Version bwcVersion = VersionUtils.randomVersionBetween(random(), Version.V_6_0_0, Version.V_6_4_0); + for (Version version : new Version[] {Version.V_6_5_0, bwcVersion}) { + DocumentMapper docMapper = createIndex("test-" + version, + Settings.builder().put(IndexMetaData.SETTING_INDEX_VERSION_CREATED.getKey(), version).build()) + .mapperService().documentMapperParser().parse("type", new CompressedXContent(mapping)); + + assertThat(docMapper.hasNestedObjects(), equalTo(true)); + ObjectMapper nested1Mapper = docMapper.objectMappers().get("nested1"); + assertThat(nested1Mapper.nested().isNested(), equalTo(true)); + + ParsedDocument doc = docMapper.parse(SourceToParse.source("test", "type", "1", BytesReference + .bytes(XContentFactory.jsonBuilder() + .startObject() + .field("field", "value") + .startArray("nested1") + .startObject() + .field("field1", "1") + .field("field2", "2") + .endObject() + .startObject() + .field("field1", "3") + .field("field2", "4") + .endObject() + .endArray() + .endObject()), + XContentType.JSON)); + + assertThat(doc.docs().size(), equalTo(3)); + if (version.onOrAfter(Version.V_6_5_0)) { + assertThat(doc.docs().get(0).get(TypeFieldMapper.NAME), equalTo(nested1Mapper.nestedTypePathAsString())); + assertThat(doc.docs().get(0).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(0).get("nested1.field2"), equalTo("2")); + assertThat(doc.docs().get(1).get("nested1.field1"), equalTo("3")); + assertThat(doc.docs().get(1).get("nested1.field2"), equalTo("4")); + assertThat(doc.docs().get(2).get("field"), equalTo("value")); + } else { + assertThat(doc.docs().get(0).get(TypeFieldMapper.NAME), equalTo(nested1Mapper.nestedTypePathAsString())); + assertThat(doc.docs().get(0).get("nested1.field1"), equalTo("3")); + assertThat(doc.docs().get(0).get("nested1.field2"), equalTo("4")); + assertThat(doc.docs().get(1).get("nested1.field1"), equalTo("1")); + assertThat(doc.docs().get(1).get("nested1.field2"), equalTo("2")); + assertThat(doc.docs().get(2).get("field"), equalTo("value")); + } + } + } } diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java index 952eb22848e1a..66b84a445ff6d 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/metrics/TopHitsIT.java @@ -819,16 +819,16 @@ public void testTopHitsInSecondLayerNested() throws Exception { assertThat(topReviewers.getHits().getAt(2).getId(), equalTo("1")); assertThat(extractValue("name", topReviewers.getHits().getAt(2).getSourceAsMap()), equalTo("user c")); assertThat(topReviewers.getHits().getAt(2).getNestedIdentity().getField().string(), equalTo("comments")); - assertThat(topReviewers.getHits().getAt(2).getNestedIdentity().getOffset(), equalTo(0)); + assertThat(topReviewers.getHits().getAt(2).getNestedIdentity().getOffset(), equalTo(1)); assertThat(topReviewers.getHits().getAt(2).getNestedIdentity().getChild().getField().string(), equalTo("reviewers")); - assertThat(topReviewers.getHits().getAt(2).getNestedIdentity().getChild().getOffset(), equalTo(2)); + assertThat(topReviewers.getHits().getAt(2).getNestedIdentity().getChild().getOffset(), equalTo(0)); assertThat(topReviewers.getHits().getAt(3).getId(), equalTo("1")); assertThat(extractValue("name", topReviewers.getHits().getAt(3).getSourceAsMap()), equalTo("user c")); assertThat(topReviewers.getHits().getAt(3).getNestedIdentity().getField().string(), equalTo("comments")); - assertThat(topReviewers.getHits().getAt(3).getNestedIdentity().getOffset(), equalTo(1)); + assertThat(topReviewers.getHits().getAt(3).getNestedIdentity().getOffset(), equalTo(0)); assertThat(topReviewers.getHits().getAt(3).getNestedIdentity().getChild().getField().string(), equalTo("reviewers")); - assertThat(topReviewers.getHits().getAt(3).getNestedIdentity().getChild().getOffset(), equalTo(0)); + assertThat(topReviewers.getHits().getAt(3).getNestedIdentity().getChild().getOffset(), equalTo(2)); assertThat(topReviewers.getHits().getAt(4).getId(), equalTo("1")); assertThat(extractValue("name", topReviewers.getHits().getAt(4).getSourceAsMap()), equalTo("user d")); diff --git a/server/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java b/server/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java index 2354f6c5a6aed..7675315408429 100644 --- a/server/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java +++ b/server/src/test/java/org/elasticsearch/search/fetch/subphase/InnerHitsIT.java @@ -205,9 +205,9 @@ public void testRandomNested() throws Exception { int size = randomIntBetween(0, numDocs); BoolQueryBuilder boolQuery = new BoolQueryBuilder(); boolQuery.should(nestedQuery("field1", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder("a").setSize(size) - .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)))); + .addSort(new FieldSortBuilder("_doc").order(SortOrder.ASC)))); boolQuery.should(nestedQuery("field2", matchAllQuery(), ScoreMode.Avg).innerHit(new InnerHitBuilder("b") - .addSort(new FieldSortBuilder("_doc").order(SortOrder.DESC)).setSize(size))); + .addSort(new FieldSortBuilder("_doc").order(SortOrder.ASC)).setSize(size))); SearchResponse searchResponse = client().prepareSearch("idx") .setQuery(boolQuery) .setSize(numDocs) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java index c131607b3b342..009971470e07c 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/integration/DocumentLevelSecurityTests.java @@ -862,6 +862,9 @@ public void testNestedInnerHits() throws Exception { .startObject() .field("field2", "value2") .endObject() + .startObject() + .array("field2", "value2", "value3") + .endObject() .endArray() .endObject()) .get(); @@ -889,6 +892,9 @@ public void testNestedInnerHits() throws Exception { assertThat(response.getHits().getAt(0).getInnerHits().get("nested_field").getAt(0).getNestedIdentity().getOffset(), equalTo(0)); assertThat(response.getHits().getAt(0).getInnerHits().get("nested_field").getAt(0).getSourceAsString(), equalTo("{\"field2\":\"value2\"}")); + assertThat(response.getHits().getAt(0).getInnerHits().get("nested_field").getAt(1).getNestedIdentity().getOffset(), equalTo(1)); + assertThat(response.getHits().getAt(0).getInnerHits().get("nested_field").getAt(1).getSourceAsString(), + equalTo("{\"field2\":[\"value2\",\"value3\"]}")); } public void testSuggesters() throws Exception {