[GitHub] moshebla closed pull request #501: SOLR-5211

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[GitHub] moshebla closed pull request #501: SOLR-5211

GitBox
moshebla closed pull request #501: SOLR-5211
URL: https://github.com/apache/lucene-solr/pull/501
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java b/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
index cfa937e7cb3..b644f73f548 100644
--- a/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
+++ b/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
@@ -95,11 +95,14 @@ public SolrInputDocument getSolrInputDocument() {
    * Nested documents, if found, will cause an exception to be thrown.  Call {@link #getLuceneDocsIfNested()} for that.
    * Any changes made to the returned Document will not be reflected in the SolrInputDocument, or future calls to this
    * method.
-   * Note that the behavior of this is sensitive to {@link #isInPlaceUpdate()}.
-   */
+   * Note that the behavior of this is sensitive to {@link #isInPlaceUpdate()}.*/
    public Document getLuceneDocument() {
      final boolean ignoreNestedDocs = false; // throw an exception if found
-     return DocumentBuilder.toDocument(getSolrInputDocument(), req.getSchema(), isInPlaceUpdate(), ignoreNestedDocs);
+     SolrInputDocument solrInputDocument = getSolrInputDocument();
+     if (!isInPlaceUpdate() && getReq().getSchema().isUsableForChildDocs()) {
+       addRootField(solrInputDocument, getHashableId());
+     }
+     return DocumentBuilder.toDocument(solrInputDocument, req.getSchema(), isInPlaceUpdate(), ignoreNestedDocs);
    }
 
   /** Returns the indexed ID for this document.  The returned BytesRef is retained across multiple calls, and should not be modified. */
@@ -194,13 +197,14 @@ public String getHashableId() {
       return null; // caller should call getLuceneDocument() instead
     }
 
-    String rootId = getHashableId();
-
-    boolean isVersion = version != 0;
+    final String rootId = getHashableId();
+    final SolrInputField versionSif = solrDoc.get(CommonParams.VERSION_FIELD);
 
     for (SolrInputDocument sdoc : all) {
-      sdoc.setField(IndexSchema.ROOT_FIELD_NAME, rootId);
-      if(isVersion) sdoc.setField(CommonParams.VERSION_FIELD, version);
+      addRootField(sdoc, rootId);
+      if (versionSif != null) {
+        addVersionField(sdoc, versionSif);
+      }
       // TODO: if possible concurrent modification exception (if SolrInputDocument not cloned and is being forwarded to replicas)
       // then we could add this field to the generated lucene document instead.
     }
@@ -208,6 +212,17 @@ public String getHashableId() {
     return () -> all.stream().map(sdoc -> DocumentBuilder.toDocument(sdoc, req.getSchema())).iterator();
   }
 
+  private void addRootField(SolrInputDocument sdoc, String rootId) {
+    sdoc.setField(IndexSchema.ROOT_FIELD_NAME, rootId);
+  }
+
+  private void addVersionField(SolrInputDocument sdoc, SolrInputField versionSif) {
+    // Reordered delete-by-query assumes all documents have a version, see SOLR-10114
+    // all docs in hierarchy should have the same version.
+    // Either fetch the version from the root doc or compute it and propagate it.
+    sdoc.put(CommonParams.VERSION_FIELD, versionSif);
+  }
+
   private List<SolrInputDocument> flatten(SolrInputDocument root) {
     List<SolrInputDocument> unwrappedDocs = new ArrayList<>();
     flattenAnonymous(unwrappedDocs, root, true);
diff --git a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
index e64ee8a3c4b..660df064b30 100644
--- a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
+++ b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
@@ -42,6 +42,7 @@
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TermQuery;
 import org.apache.lucene.store.AlreadyClosedException;
+import org.apache.lucene.util.BytesRef;
 import org.apache.lucene.util.BytesRefHash;
 import org.apache.solr.cloud.ZkController;
 import org.apache.solr.common.SolrException;
@@ -319,9 +320,9 @@ private void allowDuplicateUpdate(AddUpdateCommand cmd) throws IOException {
     RefCounted<IndexWriter> iw = solrCoreState.getIndexWriter(core);
     try {
       IndexWriter writer = iw.get();
-      Iterable<Document> blockDocs = cmd.getLuceneDocsIfNested();
-      if (blockDocs != null) {
-        writer.addDocuments(blockDocs);
+      Iterable<Document> nestedDocs = cmd.getLuceneDocsIfNested();
+      if (nestedDocs != null) {
+        writer.addDocuments(nestedDocs);
       } else {
         writer.addDocument(cmd.getLuceneDocument());
       }
@@ -425,7 +426,7 @@ public void delete(DeleteUpdateCommand cmd) throws IOException {
       return;
     }
 
-    Term deleteTerm = new Term(idField.getName(), cmd.getIndexedId());
+    Term deleteTerm = getIdTerm(cmd.getIndexedId(), false);
     // SolrCore.verbose("deleteDocuments",deleteTerm,writer);
     RefCounted<IndexWriter> iw = solrCoreState.getIndexWriter(core);
     try {
@@ -951,13 +952,13 @@ private void updateDocOrDocValues(AddUpdateCommand cmd, IndexWriter writer) thro
 
     } else { // more normal path
 
-      Iterable<Document> blockDocs = cmd.getLuceneDocsIfNested();
-      boolean isBlock = blockDocs != null; // AKA nested child docs
-      Term idTerm = new Term(isBlock ? IndexSchema.ROOT_FIELD_NAME : idField.getName(), cmd.getIndexedId());
+      Iterable<Document> nestedDocs = cmd.getLuceneDocsIfNested();
+      boolean isNested = nestedDocs != null; // AKA nested child docs
+      Term idTerm = getIdTerm(cmd.getIndexedId(), isNested);
       Term updateTerm = hasUpdateTerm ? cmd.updateTerm : idTerm;
-      if (isBlock) {
+      if (isNested) {
         log.debug("updateDocuments({})", cmd);
-        writer.updateDocuments(updateTerm, blockDocs);
+        writer.updateDocuments(updateTerm, nestedDocs);
       } else {
         Document luceneDocument = cmd.getLuceneDocument();
         log.debug("updateDocument({})", cmd);
@@ -975,6 +976,10 @@ private void updateDocOrDocValues(AddUpdateCommand cmd, IndexWriter writer) thro
     }
   }
 
+  private Term getIdTerm(BytesRef indexedId, boolean isNested) {
+    boolean useRootId = isNested || core.getLatestSchema().isUsableForChildDocs();
+    return new Term(useRootId ? IndexSchema.ROOT_FIELD_NAME : idField.getName(), indexedId);
+  }
 
   /////////////////////////////////////////////////////////////////////
   // SolrInfoBean stuff: Statistics and Module Info
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestCloudPseudoReturnFields.java b/solr/core/src/test/org/apache/solr/cloud/TestCloudPseudoReturnFields.java
index f7e6756e955..31d69cf24cb 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestCloudPseudoReturnFields.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestCloudPseudoReturnFields.java
@@ -194,7 +194,7 @@ public void testAllRealFields() throws Exception {
       SolrDocumentList docs = assertSearch(params("q", "*:*", "rows", "10", "fl",fl));
       // shouldn't matter what doc we pick...
       for (SolrDocument doc : docs) {
-        assertEquals(fl + " => " + doc, 4, doc.size());
+        assertEquals(fl + " => " + doc, 5, doc.size());
         assertTrue(fl + " => " + doc, doc.getFieldValue("id") instanceof String);
         assertTrue(fl + " => " + doc, doc.getFieldValue("val_i") instanceof Integer);
         assertTrue(fl + " => " + doc, doc.getFieldValue("subject") instanceof String);
@@ -208,7 +208,7 @@ public void testAllRealFieldsRTG() throws Exception {
     for (String fl : TestPseudoReturnFields.ALL_REAL_FIELDS) {
       for (int i : Arrays.asList(42, 43, 44, 45, 46, 99)) {
         SolrDocument doc = getRandClient(random()).getById(""+i, params("fl",fl));
-        assertEquals(fl + " => " + doc, 4, doc.size());
+        assertEquals(fl + " => " + doc, 5, doc.size());
         assertTrue(fl + " => " + doc, doc.getFieldValue("id") instanceof String);
         assertTrue(fl + " => " + doc, doc.getFieldValue("val_i") instanceof Integer);
         assertTrue(fl + " => " + doc, doc.getFieldValue("subject") instanceof String);
@@ -238,7 +238,7 @@ public void testScoreAndAllRealFields() throws Exception {
       SolrDocumentList docs = assertSearch(params("q", "*:*", "rows", "10", "fl",fl));
       // shouldn't matter what doc we pick...
       for (SolrDocument doc : docs) {
-        assertEquals(fl + " => " + doc, 5, doc.size());
+        assertEquals(fl + " => " + doc, 6, doc.size());
         assertTrue(fl + " => " + doc, doc.getFieldValue("id") instanceof String);
         assertTrue(fl + " => " + doc, doc.getFieldValue("score") instanceof Float);
         assertTrue(fl + " => " + doc, doc.getFieldValue("val_i") instanceof Integer);
@@ -253,7 +253,7 @@ public void testScoreAndAllRealFieldsRTG() throws Exception {
     for (String fl : TestPseudoReturnFields.SCORE_AND_REAL_FIELDS) {
       for (int i : Arrays.asList(42, 43, 44, 45, 46, 99)) {
         SolrDocument doc = getRandClient(random()).getById(""+i, params("fl",fl));
-        assertEquals(fl + " => " + doc, 4, doc.size());
+        assertEquals(fl + " => " + doc, 5, doc.size());
         assertTrue(fl + " => " + doc, doc.getFieldValue("id") instanceof String);
         assertTrue(fl + " => " + doc, doc.getFieldValue("val_i") instanceof Integer);
         assertTrue(fl + " => " + doc, doc.getFieldValue("subject") instanceof String);
diff --git a/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java b/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java
index 5a9db8f9360..69698837c20 100644
--- a/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java
+++ b/solr/core/src/test/org/apache/solr/cloud/TestRandomFlRTGCloud.java
@@ -70,6 +70,9 @@
 
   /** Always included in fl so we can vet what doc we're looking at */
   private static final FlValidator ID_VALIDATOR = new SimpleFieldValueValidator("id");
+
+  /** Since nested documents are not tested, when _root_ is declared in schema, it is always the same as id */
+  private static final FlValidator ROOT_VALIDATOR = new RenameFieldValueValidator("id" , "_root_");
   
   /**
    * Types of things we will randomly ask for in fl param, and validate in response docs.
@@ -352,6 +355,7 @@ private void assertRTG(final SolrInputDocument[] knownDocs, final int[] docIds)
     
     final Set<FlValidator> validators = new LinkedHashSet<>();
     validators.add(ID_VALIDATOR); // always include id so we can be confident which doc we're looking at
+    validators.add(ROOT_VALIDATOR); // always added in a nested schema, with the same value as id
     addRandomFlValidators(random(), validators);
     FlValidator.addParams(validators, params);
 
diff --git a/solr/core/src/test/org/apache/solr/handler/tagger/TaggerTest.java b/solr/core/src/test/org/apache/solr/handler/tagger/TaggerTest.java
index 93b11b50a28..7a67b95d4a0 100644
--- a/solr/core/src/test/org/apache/solr/handler/tagger/TaggerTest.java
+++ b/solr/core/src/test/org/apache/solr/handler/tagger/TaggerTest.java
@@ -83,7 +83,8 @@ public void testFormat() throws Exception {
         "<result name=\"response\" numFound=\"1\" start=\"0\">\n" +
         "  <doc>\n" +
         "    <str name=\"id\">1</str>\n" +
-        "    <str name=\"name\">London Business School</str></doc>\n" +
+        "    <str name=\"name\">London Business School</str>\n" +
+        "    <str name=\"_root_\">1</str></doc>\n" +
         "</result>\n" +
         "</response>\n";
     assertEquals(expected, rspStr);
@@ -111,7 +112,8 @@ public void testFormatMatchText() throws Exception {
         "<result name=\"response\" numFound=\"1\" start=\"0\">\n" +
         "  <doc>\n" +
         "    <str name=\"id\">1</str>\n" +
-        "    <str name=\"name\">London Business School</str></doc>\n" +
+        "    <str name=\"name\">London Business School</str>\n" +
+        "    <str name=\"_root_\">1</str></doc>\n" +
         "</result>\n" +
         "</response>\n";
     assertEquals(expected, rspStr);
diff --git a/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java b/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java
index 0a987345ed2..10803548c69 100644
--- a/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java
+++ b/solr/core/src/test/org/apache/solr/search/TestPseudoReturnFields.java
@@ -126,7 +126,7 @@ public void testAllRealFields() throws Exception {
               ,"//result/doc/str[@name='ssto']"
               ,"//result/doc/str[@name='subject']"
               
-              ,"//result/doc[count(*)=4]"
+              ,"//result/doc[count(*)=5]"
               );
     }
   }
@@ -142,7 +142,7 @@ public void testAllRealFieldsRTG() throws Exception {
                 ,"//doc/int[@name='val_i']"
                 ,"//doc/str[@name='ssto']"
                 ,"//doc/str[@name='subject']"
-                ,"//doc[count(*)=4]"
+                ,"//doc[count(*)=5]"
                 );
       }
     }
@@ -172,8 +172,7 @@ public void testScoreAndAllRealFields() throws Exception {
               ,"//result/doc/str[@name='ssto']"
               ,"//result/doc/str[@name='subject']"
               ,"//result/doc/float[@name='score']"
-              
-              ,"//result/doc[count(*)=5]"
+              ,"//result/doc[count(*)=6]"
               );
     }
   }
@@ -190,7 +189,7 @@ public void testScoreAndAllRealFieldsRTG() throws Exception {
                 ,"//doc/int[@name='val_i']"
                 ,"//doc/str[@name='ssto']"
                 ,"//doc/str[@name='subject']"
-                ,"//doc[count(*)=4]"
+                ,"//doc[count(*)=5]"
                 );
       }
     }
diff --git a/solr/core/src/test/org/apache/solr/search/TestReload.java b/solr/core/src/test/org/apache/solr/search/TestReload.java
index 872abecda15..13d78fcc178 100644
--- a/solr/core/src/test/org/apache/solr/search/TestReload.java
+++ b/solr/core/src/test/org/apache/solr/search/TestReload.java
@@ -36,13 +36,13 @@ public void testGetRealtimeReload() throws Exception {
 
     assertU(commit("softCommit","true"));   // should cause a RTG searcher to be opened
 
-    assertJQ(req("qt","/get","id","1")
+    assertJQ(req("qt","/get","id","1", "fl", "id,_version_")
         ,"=={'doc':{'id':'1','_version_':" + version + "}}"
     );
 
     h.reload();
 
-    assertJQ(req("qt","/get","id","1")
+    assertJQ(req("qt","/get","id","1", "fl", "id,_version_")
         ,"=={'doc':{'id':'1','_version_':" + version + "}}"
     );
 
@@ -76,7 +76,7 @@ public void testGetRealtimeReload() throws Exception {
       if (rand.nextBoolean()) {
         // RTG should always be able to see the last version
         // System.out.println("!!! rtg");
-        assertJQ(req("qt","/get","id","1")
+        assertJQ(req("qt","/get","id","1", "fl", "id,_version_")
             ,"=={'doc':{'id':'1','_version_':" + version + "}}"
         );
       }
diff --git a/solr/core/src/test/org/apache/solr/update/RootFieldTest.java b/solr/core/src/test/org/apache/solr/update/RootFieldTest.java
new file mode 100644
index 00000000000..fe570b9636c
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/update/RootFieldTest.java
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.update;
+
+import java.util.List;
+
+import org.apache.solr.SolrJettyTestBase;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrQuery;
+import org.apache.solr.common.SolrDocument;
+import org.apache.solr.common.SolrDocumentList;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.CommonParams;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import static org.hamcrest.CoreMatchers.is;
+
+public class RootFieldTest extends SolrJettyTestBase {
+  private static boolean useRootSchema;
+  private static final String MESSAGE = "Update handler should create and process _root_ field " +
+      "unless there is no such a field in schema";
+
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
+
+  private static boolean expectRoot() {
+    return useRootSchema;
+  }
+
+  @BeforeClass
+  public static void beforeTest() throws Exception {
+    useRootSchema = random().nextBoolean();
+    // schema.xml declares _root_ field while schema15.xml does not.
+    String schema = useRootSchema ? "schema.xml" : "schema15.xml";
+    initCore("solrconfig.xml", schema);
+  }
+
+  @Test
+  public void testLegacyBlockProcessing() throws Exception
+  {
+    SolrClient client = getSolrClient();
+    client.deleteByQuery("*:*");// delete everything!
+
+    // Add child free doc
+    SolrInputDocument docToUpdate = new SolrInputDocument();
+    String docId = "11";
+    docToUpdate.addField( "id", docId);
+    docToUpdate.addField( "name", "child free doc" );
+    client.add(docToUpdate);
+    client.commit();
+
+    SolrQuery query = new SolrQuery();
+    query.setQuery( "*:*" );
+    query.set( CommonParams.FL, "id,name,_root_" );
+
+    SolrDocumentList results = client.query(query).getResults();
+    assertThat(results.getNumFound(), is(1L));
+    SolrDocument foundDoc = results.get( 0 );
+
+    // Check retrieved field values
+    assertThat(foundDoc.getFieldValue( "id" ), is(docId));
+    assertThat( ((List)foundDoc.getFieldValue( "name" )).get(0), is("child free doc"));
+
+    String expectedRootValue = expectRoot() ? docId : null;
+    assertThat(MESSAGE, foundDoc.getFieldValue( "_root_" ), is(expectedRootValue));
+
+    // Update the doc
+    docToUpdate.setField( "name", "updated doc" );
+    client.add(docToUpdate);
+    client.commit();
+
+    results = client.query(query).getResults();
+    assertEquals( 1, results.getNumFound() );
+    foundDoc = results.get( 0 );
+
+    // Check updated field values
+    assertThat(foundDoc.getFieldValue( "id" ), is(docId));
+    assertThat( ((List)foundDoc.getFieldValue( "name" )).get(0), is("updated doc"));
+    assertThat(MESSAGE, foundDoc.getFieldValue( "_root_" ), is(expectedRootValue));
+  }
+
+  @Test
+  public void testUpdateWithChildDocs() throws Exception {
+    SolrClient client = getSolrClient();
+    client.deleteByQuery("*:*");// delete everything!
+
+    // Add child free doc
+    SolrInputDocument docToUpdate = new SolrInputDocument();
+    String docId = "11";
+    docToUpdate.addField( "id", docId);
+    docToUpdate.addField( "name", "parent doc with a child" );
+    SolrInputDocument child = new SolrInputDocument();
+    child.addField("id", "111");
+    child.addField("name", "child doc");
+    docToUpdate.addChildDocument(child);
+    if (!useRootSchema) {
+      thrown.expect(SolrException.class);
+      thrown.expectMessage("Unable to index docs with children:" +
+          " the schema must include definitions for both a uniqueKey field" +
+          " and the '_root_' field, using the exact same fieldType");
+    }
+    client.add(docToUpdate);
+    client.commit();
+  }
+
+}
diff --git a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
index 43a84e6bb69..f72fd6702a5 100644
--- a/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
+++ b/solr/core/src/test/org/apache/solr/update/processor/AtomicUpdatesTest.java
@@ -1215,7 +1215,7 @@ public void testFieldsWithDefaultValuesWhenAtomicUpdatesAgainstTlog() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=7"
+              , "count(//doc/*)=8"
               );
 
       // do atomic update
@@ -1230,7 +1230,7 @@ public void testFieldsWithDefaultValuesWhenAtomicUpdatesAgainstTlog() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=7"
+              , "count(//doc/*)=8"
               );
 
       assertU(commit());
@@ -1244,7 +1244,7 @@ public void testFieldsWithDefaultValuesWhenAtomicUpdatesAgainstTlog() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=7"
+              , "count(//doc/*)=8"
               );
     }
     
@@ -1267,7 +1267,7 @@ public void testAtomicUpdateOfFieldsWithDefaultValue() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=6"
+              , "count(//doc/*)=7"
               );
       // do atomic update
       assertU(adoc(sdoc("id", "7", fieldToUpdate, ImmutableMap.of("inc", -555))));
@@ -1281,7 +1281,7 @@ public void testAtomicUpdateOfFieldsWithDefaultValue() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=6"
+              , "count(//doc/*)=7"
               );
 
       // diff doc where we check that we can overwrite the default value
@@ -1296,7 +1296,7 @@ public void testAtomicUpdateOfFieldsWithDefaultValue() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=6"
+              , "count(//doc/*)=7"
               );
       // do atomic update
       assertU(adoc(sdoc("id", "8", fieldToUpdate, ImmutableMap.of("inc", -555))));
@@ -1310,7 +1310,7 @@ public void testAtomicUpdateOfFieldsWithDefaultValue() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=6"
+              , "count(//doc/*)=7"
               );
       
       assertU(commit());
@@ -1325,7 +1325,7 @@ public void testAtomicUpdateOfFieldsWithDefaultValue() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=6"
+              , "count(//doc/*)=7"
               );
       assertQ(fieldToUpdate + ": doc8 post commit RTG"
               , req("qt", "/get", "id", "8")
@@ -1337,7 +1337,7 @@ public void testAtomicUpdateOfFieldsWithDefaultValue() {
               , "//doc/long[@name='_version_']"
               , "//doc/date[@name='timestamp']"
               , "//doc/arr[@name='multiDefault']/str[.='muLti-Default']"
-              , "count(//doc/*)=6"
+              , "count(//doc/*)=7"
               );
     }
     
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java
index b83be8300fa..807757ce77c 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/SolrExampleTests.java
@@ -69,6 +69,7 @@
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.Pair;
+import org.junit.Before;
 import org.junit.Test;
 import org.noggit.JSONParser;
 import org.slf4j.Logger;
@@ -76,6 +77,7 @@
 
 import static org.apache.solr.common.params.UpdateParams.ASSUME_CONTENT_TYPE;
 import static org.junit.internal.matchers.StringContains.containsString;
+import static org.hamcrest.CoreMatchers.is;
 
 /**
  * This should include tests against the example solr config
@@ -93,6 +95,15 @@
   static {
     ignoreException("uniqueKey");
   }
+
+  @Before
+  public void emptyCollection() throws Exception {
+    SolrClient client = getSolrClient();
+    // delete everything!
+    client.deleteByQuery("*:*");
+    client.commit();
+  }
+
   /**
    * query the example
    */
@@ -2179,4 +2190,84 @@ private SolrInputDocument genNestedDocuments(Map<String,SolrInputDocument> allDo
     }
     return sdoc;
   }
+
+  @Test
+  public void testAddChildToChildFreeDoc() throws IOException, SolrServerException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
+    SolrClient client = getSolrClient();
+    client.deleteByQuery("*:*");
+
+    SolrInputDocument docToUpdate = new SolrInputDocument();
+    docToUpdate.addField("id", "p0");
+    docToUpdate.addField("title_s", "i am a child free doc");
+    client.add(docToUpdate);
+    client.commit();
+
+    SolrQuery q = new SolrQuery("*:*");
+    q.set( CommonParams.FL, "id,title_s" );
+    q.addSort("id", SolrQuery.ORDER.desc);
+
+    SolrDocumentList results = client.query(q).getResults();
+    assertThat(results.getNumFound(), is(1L));
+    SolrDocument foundDoc = results.get(0);
+    assertThat(foundDoc.getFieldValue("title_s"), is("i am a child free doc"));
+
+    // Rewrite child free doc
+    docToUpdate.setField("title_s", "i am a parent");
+
+    SolrInputDocument child = new SolrInputDocument();
+    child.addField("id", "c0");
+    child.addField("title_s", "i am a child");
+
+    docToUpdate.addChildDocument(child);
+
+    client.add(docToUpdate);
+    client.commit();
+
+    results = client.query(q).getResults();
+
+    assertThat(results.getNumFound(), is(2L));
+    foundDoc = results.get(0);
+    assertThat(foundDoc.getFieldValue("title_s"), is("i am a parent"));
+    foundDoc = results.get(1);
+    assertThat(foundDoc.getFieldValue("title_s"), is("i am a child"));
+  }
+
+  @Test
+  public void testDeleteParentDoc() throws IOException, SolrServerException, IllegalArgumentException, IllegalAccessException, SecurityException, NoSuchFieldException {
+    SolrClient client = getSolrClient();
+    client.deleteByQuery("*:*");
+
+    SolrInputDocument docToDelete = new SolrInputDocument();
+    docToDelete.addField("id", "p0");
+    docToDelete.addField("title_s", "parent doc");
+
+    SolrInputDocument child = new SolrInputDocument();
+    child.addField("id", "c0");
+    child.addField("title_s", "i am a child 0");
+    docToDelete.addChildDocument(child);
+
+    child = new SolrInputDocument();
+    child.addField("id", "c1");
+    child.addField("title_s", "i am a child 1");
+    docToDelete.addChildDocument(child);
+
+    child = new SolrInputDocument();
+    child.addField("id", "c2");
+    child.addField("title_s", "i am a child 2");
+    docToDelete.addChildDocument(child);
+
+    client.add(docToDelete);
+    client.commit();
+
+    SolrQuery q = new SolrQuery("*:*");
+    SolrDocumentList results = client.query(q).getResults();
+    assertThat(results.getNumFound(), is(4L));
+
+    client.deleteById("p0");
+    client.commit();
+
+    results = client.query(q).getResults();
+    assertThat("All the children are expected to be deleted together with parent",
+        results.getNumFound(), is(0L));
+  }
 }


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[hidden email]


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]