This is an automated email from the ASF dual-hosted git repository.

dsmiley pushed a commit to branch branch_9_0
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/branch_9_0 by this push:
     new 0c236d8  SOLR-15064: nested docs:  _route_ shouldn't be a fallback for 
a blank _root_ (#629)
0c236d8 is described below

commit 0c236d8281ab649403b558780a07606a4c5fdef8
Author: David Smiley <[email protected]>
AuthorDate: Sat Feb 19 00:24:08 2022 -0500

    SOLR-15064: nested docs:  _route_ shouldn't be a fallback for a blank 
_root_ (#629)
    
    Doing atomic updates for nested documents should not use/assume that the 
_route_ parameter is a substitute for _root_ if that's not present.  Thus 
insist the user specify the _root_ field so as to leave no doubt that the 
atomic update is a child and not itself a root document.
    
    * Rename getChildIdStr -> getSelfOrNestedDocIdStr
---
 solr/CHANGES.txt                                   |  4 ++
 .../org/apache/solr/update/AddUpdateCommand.java   | 61 +++++--------------
 .../apache/solr/update/DirectUpdateHandler2.java   |  2 +-
 .../src/java/org/apache/solr/update/UpdateLog.java |  8 ---
 .../processor/AtomicUpdateDocumentMerger.java      |  2 +-
 .../processor/DistributedUpdateProcessor.java      |  2 +-
 .../solr/cloud/NestedShardedAtomicUpdateTest.java  | 69 +++++++++++-----------
 .../update/processor/NestedAtomicUpdateTest.java   | 67 ++++++++++-----------
 .../pages/partial-document-updates.adoc            |  8 +--
 .../pages/major-changes-in-solr-9.adoc             |  2 +
 10 files changed, 94 insertions(+), 131 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 537101a..7d1d186 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -534,6 +534,10 @@ and each individual module's jar will be included in its 
directory's lib/ folder
 
 * SOLR-15991: analysis-extras module tests shouldn't rely on log4j dependency 
(Kevin Risden)
 
+* SOLR-15064: Atomic/partial updates to nested documents now _require_ the 
`\_root_` field to
+  clearly show the document isn't a root document.  Solr 8 would fallback on 
the `\_route_` param but no longer.
+  (David Smiley)
+
 * SOLR-15949: Docker: the official image now uses Java 17 provided by Eclipse 
Temurin.  Formerly it was Java 11 from OpenJDK.
   (janhoy, David Smiley)
 
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 2de9e6b..068ba52 100644
--- a/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
+++ b/solr/core/src/java/org/apache/solr/update/AddUpdateCommand.java
@@ -28,10 +28,7 @@ import org.apache.lucene.util.BytesRef;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.SolrInputField;
-import org.apache.solr.common.cloud.DocRouter;
-import org.apache.solr.common.cloud.ImplicitDocRouter;
 import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.params.ShardParams;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
@@ -42,9 +39,6 @@ import org.apache.solr.schema.SchemaField;
  */
 public class AddUpdateCommand extends UpdateCommand {
 
-  /** In some limited circumstances of child docs, this holds the _route_ 
param. */
-  final String useRouteAsRoot; // lets hope this goes away in SOLR-15064
-
   /**
    * Higher level SolrInputDocument, normally used to construct the Lucene 
Document(s)
    * to index.
@@ -70,35 +64,12 @@ public class AddUpdateCommand extends UpdateCommand {
 
   public boolean isLastDocInBatch = false;
 
-  // optional id in "internal" indexed form... if it is needed and not 
supplied,
-  // it will be obtained from the doc.
   private BytesRef indexedId;
   private String indexedIdStr;
-  private String childDocIdStr;
+  private String selfOrNestedDocIdStr;
 
   public AddUpdateCommand(SolrQueryRequest req) {
     super(req);
-
-    // Populate useRouteParamAsIndexedId.
-    // This ought to be deprecated functionality that we remove in 9.0. 
SOLR-15064
-    String route = null;
-    if (req != null) { // some tests use no req
-      route = req.getParams().get(ShardParams._ROUTE_);
-      if (route == null || !req.getSchema().isUsableForChildDocs()) {
-        route = null;
-      } else {
-        // use route but there's one last exclusion: It's incompatible with 
SolrCloud implicit router.
-        String collectionName = 
req.getCore().getCoreDescriptor().getCollectionName();
-        if (collectionName != null) {
-          DocRouter router = 
req.getCore().getCoreContainer().getZkController().getClusterState()
-              .getCollection(collectionName).getRouter();
-          if (router instanceof ImplicitDocRouter) {
-            route = null;
-          }
-        }
-      }
-    }
-    useRouteAsRoot = route;
   }
 
   @Override
@@ -111,7 +82,7 @@ public class AddUpdateCommand extends UpdateCommand {
      solrDoc = null;
      indexedId = null;
      indexedIdStr = null;
-     childDocIdStr = null;
+     selfOrNestedDocIdStr = null;
      updateTerm = null;
      isLastDocInBatch = false;
      version = 0;
@@ -140,7 +111,8 @@ public class AddUpdateCommand extends UpdateCommand {
    }
 
   /**
-   * Returns the indexed ID for this document, or the root ID for nested 
documents.
+   * Returns the indexed ID that we route this document on.  Typically, this is
+   * for the unique key of the document but for a nested document, it's the ID 
of the root.
    *
    * @return possibly null if there's no uniqueKey field
    */
@@ -150,8 +122,10 @@ public class AddUpdateCommand extends UpdateCommand {
   }
 
   /**
-   * Returns the indexed ID for this document, or the root ID for nested 
documents. The returned
-   * BytesRef should be treated as immutable. It will not be re-used/modified 
for additional docs.
+   * Returns the indexed ID that we route this document on.  Typically, this is
+   * for the unique key of the document but for a nested document, it's the ID 
of the root.
+   *
+   * BytesRef should be treated as immutable.  It will not be re-used/modified 
for additional docs.
    *
    * @return possibly null if there's no uniqueKey field
    */
@@ -166,9 +140,9 @@ public class AddUpdateCommand extends UpdateCommand {
    *
    * @return possibly null if there's no uniqueKey field
    */
-  public String getChildDocIdStr() {
+  public String getSelfOrNestedDocIdStr() {
     extractIdsIfNeeded();
-    return childDocIdStr;
+    return selfOrNestedDocIdStr;
   }
 
   /** The ID for logging purposes. */
@@ -179,10 +153,10 @@ public class AddUpdateCommand extends UpdateCommand {
     extractIdsIfNeeded();
     if (indexedIdStr == null) {
       return "(null)";
-    } else if (indexedIdStr.equals(childDocIdStr)) {
+    } else if (indexedIdStr.equals(selfOrNestedDocIdStr)) {
       return indexedIdStr;
     } else {
-      return childDocIdStr + " (root=" + indexedIdStr + ")";
+      return selfOrNestedDocIdStr + " (root=" + indexedIdStr + ")";
     }
   }
 
@@ -204,14 +178,11 @@ public class AddUpdateCommand extends UpdateCommand {
         } else if (count  > 1) {
           throw new SolrException( 
SolrException.ErrorCode.BAD_REQUEST,"Document contains multiple values for 
uniqueKey field: " + field);
         } else {
-          this.childDocIdStr = field.getFirstValue().toString();
-          // the root might be in _root_ field or _route_ param.  If neither, 
then uniqueKeyField.
+          this.selfOrNestedDocIdStr = field.getFirstValue().toString();
+          // the root might be in _root_ field; if not then uniqueKeyField.
           this.indexedIdStr = (String) 
solrDoc.getFieldValue(IndexSchema.ROOT_FIELD_NAME); // or here
           if (this.indexedIdStr == null) {
-            this.indexedIdStr = useRouteAsRoot;
-            if (this.indexedIdStr == null) {
-              this.indexedIdStr = childDocIdStr;
-            }
+            this.indexedIdStr = selfOrNestedDocIdStr;
           }
           indexedId = schema.indexableUniqueKey(indexedIdStr);
         }
@@ -223,7 +194,7 @@ public class AddUpdateCommand extends UpdateCommand {
   public void setIndexedId(BytesRef indexedId) {
     this.indexedId = indexedId;
     this.indexedIdStr = indexedId.utf8ToString();
-    this.childDocIdStr = indexedIdStr;
+    this.selfOrNestedDocIdStr = indexedIdStr;
   }
 
   /**
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 94b1c4a..7b1dd17 100644
--- a/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
+++ b/solr/core/src/java/org/apache/solr/update/DirectUpdateHandler2.java
@@ -937,7 +937,7 @@ public class DirectUpdateHandler2 extends UpdateHandler 
implements SolrCoreState
 
       // can't use cmd.getIndexedId because it will be a root doc if this doc 
is a child
       Term updateTerm = new Term(idField.getName(),
-          core.getLatestSchema().indexableUniqueKey(cmd.getChildDocIdStr()));
+          
core.getLatestSchema().indexableUniqueKey(cmd.getSelfOrNestedDocIdStr()));
       List<IndexableField> fields = 
cmd.makeLuceneDocForInPlaceUpdate().getFields(); // skips uniqueKey and _root_
       log.debug("updateDocValues({})", cmd);
       writer.updateDocValues(updateTerm, fields.toArray(new 
Field[fields.size()]));
diff --git a/solr/core/src/java/org/apache/solr/update/UpdateLog.java 
b/solr/core/src/java/org/apache/solr/update/UpdateLog.java
index e27a166..ea8bf27 100644
--- a/solr/core/src/java/org/apache/solr/update/UpdateLog.java
+++ b/solr/core/src/java/org/apache/solr/update/UpdateLog.java
@@ -69,7 +69,6 @@ import org.apache.solr.request.LocalSolrQueryRequest;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.request.SolrRequestInfo;
 import org.apache.solr.response.SolrQueryResponse;
-import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.search.SolrIndexSearcher;
 import org.apache.solr.update.processor.DistributedUpdateProcessor;
 import org.apache.solr.update.processor.UpdateRequestProcessor;
@@ -561,13 +560,6 @@ public class UpdateLog implements PluginInfoInitialized, 
SolrMetricProducer {
     // TODO: we currently need to log to maintain correct versioning, rtg, etc
     // if ((cmd.getFlags() & UpdateCommand.REPLAY) != 0) return;
 
-    // This hack could be removed after SOLR-15064 when we insist updates to 
child docs include _root_.
-    // Until then, if we're in a buffering mode, then the solrDoc won't have 
the _root_ field.
-    // Otherwise, it should already be there, placed by the client.
-    if (usableForChildDocs && cmd.useRouteAsRoot != null && 
cmd.solrDoc.getField(IndexSchema.ROOT_FIELD_NAME) == null) {
-      cmd.solrDoc.setField(IndexSchema.ROOT_FIELD_NAME, cmd.getIndexedIdStr());
-    }
-
     synchronized (this) {
       if ((cmd.getFlags() & UpdateCommand.BUFFERING) != 0) {
         ensureBufferTlog();
diff --git 
a/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
 
b/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
index f2ac4cc..620a647 100644
--- 
a/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
+++ 
b/solr/core/src/java/org/apache/solr/update/processor/AtomicUpdateDocumentMerger.java
@@ -370,7 +370,7 @@ public class AtomicUpdateDocumentMerger {
   public boolean doInPlaceUpdateMerge(AddUpdateCommand cmd, Set<String> 
updatedFields) throws IOException {
     SolrInputDocument inputDoc = cmd.getSolrInputDocument();
     BytesRef rootIdBytes = cmd.getIndexedId();
-    BytesRef idBytes = schema.indexableUniqueKey(cmd.getChildDocIdStr());
+    BytesRef idBytes = 
schema.indexableUniqueKey(cmd.getSelfOrNestedDocIdStr());
 
     updatedFields.add(CommonParams.VERSION_FIELD); // add the version field so 
that it is fetched too
     SolrInputDocument oldDocument = RealTimeGetComponent.getInputDocument
diff --git 
a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
 
b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
index 4802134..72908fd 100644
--- 
a/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
+++ 
b/solr/core/src/java/org/apache/solr/update/processor/DistributedUpdateProcessor.java
@@ -714,7 +714,7 @@ public class DistributedUpdateProcessor extends 
UpdateRequestProcessor {
     SolrInputDocument mergedDoc;
     if (oldRootDocWithChildren == null) {
       if (versionOnUpdate > 0
-          || !rootDocIdString.equals(cmd.getChildDocIdStr())) {
+          || !rootDocIdString.equals(cmd.getSelfOrNestedDocIdStr())) {
         // could just let the optimistic locking throw the error
         throw new SolrException(ErrorCode.CONFLICT, "Document not found for 
update.  id=" + rootDocIdString);
       }
diff --git 
a/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java 
b/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java
index 7e2ed1b..7b7e089 100644
--- 
a/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java
+++ 
b/solr/core/src/test/org/apache/solr/cloud/NestedShardedAtomicUpdateTest.java
@@ -30,7 +30,6 @@ import 
org.apache.solr.client.solrj.request.CollectionAdminRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
 import org.apache.solr.client.solrj.response.QueryResponse;
 import org.apache.solr.common.SolrDocument;
-import org.apache.solr.common.SolrException;
 import org.apache.solr.common.SolrInputDocument;
 import org.apache.solr.common.cloud.ClusterState;
 import org.apache.solr.common.cloud.Replica;
@@ -81,28 +80,26 @@ public class NestedShardedAtomicUpdateTest extends 
SolrCloudTestCase { // used t
     // for now,  we know how ranges will be distributed to shards.
     // may have to look it up in clusterstate if that assumption changes.
 
-    SolrInputDocument doc = sdoc("id", "1", "level_s", "root");
+    SolrInputDocument doc = sdoc("id", "1", "_root_", "1", "level_s", "root");
 
-    final SolrParams params = params("wt", "json", "_route_", "1");
-
-    int which = (params.get("_route_").hashCode() & 0x7fffffff) % 
clients.size();
+    int which = (doc.get("_root_").hashCode() & 0x7fffffff) % clients.size();
     SolrClient aClient = clients.get(which);
 
-    indexDoc(aClient, params, doc);
+    indexDoc(aClient, null, doc);
 
-    doc = sdoc("id", "1", "children", map("add", sdocs(sdoc("id", "2", 
"level_s", "child"))));
+    doc = sdoc("id", "1", "_root_", "1", "children", map("add", 
sdocs(sdoc("id", "2", "level_s", "child"))));
 
-    indexDoc(aClient, params, doc);
+    indexDoc(aClient, null, doc);
 
     for(int idIndex = 0; idIndex < ids.length; ++idIndex) {
 
-      doc = sdoc("id", "2", "grandChildren", map("add", sdocs(sdoc("id", 
ids[idIndex], "level_s", "grand_child"))));
+      doc = sdoc("id", "2", "_root_", "1", "grandChildren", map("add", 
sdocs(sdoc("id", ids[idIndex], "level_s", "grand_child"))));
 
-      indexDocAndRandomlyCommit(getRandomSolrClient(), params, doc);
+      indexDocAndRandomlyCommit(getRandomSolrClient(), null, doc);
 
-      doc = sdoc("id", "3", "inplace_updatable_int", map("inc", "1"));
+      doc = sdoc("id", "3", "_root_", "1", "inplace_updatable_int", map("inc", 
"1"));
 
-      indexDocAndRandomlyCommit(getRandomSolrClient(), params, doc);
+      indexDocAndRandomlyCommit(getRandomSolrClient(), null, doc);
 
       // assert RTG request respects _route_ param
       QueryResponse routeRsp = getRandomSolrClient().query(params("qt","/get", 
"id","2", "_route_", "1"));
@@ -141,22 +138,20 @@ public class NestedShardedAtomicUpdateTest extends 
SolrCloudTestCase { // used t
     // for now,  we know how ranges will be distributed to shards.
     // may have to look it up in clusterstate if that assumption changes.
 
-    SolrInputDocument doc = sdoc("id", "1", "level_s", "root");
-
-    final SolrParams params = params("wt", "json", "_route_", "1");
+    SolrInputDocument doc = sdoc("id", "1", "_root_", "1", "level_s", "root");
 
-    int which = (params.get("_route_").hashCode() & 0x7fffffff) % 
clients.size();
+    int which = (doc.get("_root_").hashCode() & 0x7fffffff) % clients.size();
     SolrClient aClient = clients.get(which);
 
-    indexDocAndRandomlyCommit(aClient, params, doc);
+    indexDocAndRandomlyCommit(aClient, null, doc);
 
-    doc = sdoc("id", "1", "children", map("add", sdocs(sdoc("id", "2", 
"level_s", "child"))));
+    doc = sdoc("id", "1", "_root_", "1", "children", map("add", 
sdocs(sdoc("id", "2", "level_s", "child"))));
 
-    indexDocAndRandomlyCommit(aClient, params, doc);
+    indexDocAndRandomlyCommit(aClient, null, doc);
 
-    doc = sdoc("id", "2", "grandChildren", map("add", sdocs(sdoc("id", ids[0], 
"level_s", "grand_child"))));
+    doc = sdoc("id", "2", "_root_", "1", "grandChildren", map("add", 
sdocs(sdoc("id", ids[0], "level_s", "grand_child"))));
 
-    indexDocAndRandomlyCommit(aClient, params, doc);
+    indexDocAndRandomlyCommit(aClient, null, doc);
 
     int id1InPlaceCounter = 0;
     int id2InPlaceCounter = 0;
@@ -167,21 +162,21 @@ public class NestedShardedAtomicUpdateTest extends 
SolrCloudTestCase { // used t
         id1InPlaceCounter++;
         indexDoc(
             getRandomSolrClient(),
-            params,
-            sdoc("id", "1", "inplace_updatable_int", map("inc", "1")));
+            null,
+            sdoc("id", "1", "_root_", "1", "inplace_updatable_int", map("inc", 
"1")));
       }
       if (random().nextBoolean()) {
         id2InPlaceCounter++;
         indexDoc(
             getRandomSolrClient(),
-            params,
-            sdoc("id", "2", "inplace_updatable_int", map("inc", "1")));
+            null,
+            sdoc("id", "2", "_root_", "1", "inplace_updatable_int", map("inc", 
"1")));
       }
-      if (random().nextBoolean()) {
+      if (random().nextBoolean()) { // TODO consider removing this now-useless 
block?
         id3InPlaceCounter++;
         indexDoc(
             getRandomSolrClient(),
-            params, // add root merely to show it doesn't interfere
+            null,
             sdoc("id", "3", "_root_", "1", "inplace_updatable_int", map("inc", 
"1")));
       }
       if (random().nextBoolean()) {
@@ -240,30 +235,32 @@ public class NestedShardedAtomicUpdateTest extends 
SolrCloudTestCase { // used t
 
     SolrInputDocument doc = sdoc("id", rootId, "level_s", "root");
 
-    final SolrParams wrongRootParams = params("wt", "json", "_route_", "c");
-    final SolrParams rightParams = params("wt", "json", "_route_", rootId);
+    final SolrParams wrongRouteParams = params("_route_", "c");
+    final SolrParams rightParams = params("_route_", rootId);
 
     int which = (rootId.hashCode() & 0x7fffffff) % clients.size();
     SolrClient aClient = clients.get(which);
 
-    indexDocAndRandomlyCommit(aClient, params("wt", "json", "_route_", 
rootId), doc);
+    indexDocAndRandomlyCommit(aClient, null, doc);
 
     final SolrInputDocument childDoc = sdoc("id", rootId, "children", 
map("add", sdocs(sdoc("id", "2", "level_s", "child"))));
 
     indexDocAndRandomlyCommit(aClient, rightParams, childDoc);
 
-    final SolrInputDocument grandChildDoc = sdoc("id", "2", "grandChildren",
+    final SolrInputDocument grandChildDoc = sdoc("id", "2", "_root_", rootId,
+        "grandChildren",
         map("add", sdocs(
             sdoc("id", "3", "level_s", "grandChild")
             )
         )
     );
 
-    SolrException e = expectThrows(SolrException.class,
-        "wrong \"_route_\" param should throw an exception",
-        () -> indexDocAndRandomlyCommit(aClient, wrongRootParams, 
grandChildDoc)
-    );
-    assertTrue(e.toString(), e.getMessage().contains("Document not found for 
update"));
+    // despite the wrong param, it'll be routed correctly; we can find the doc 
after.
+    //   An error would have been okay too but routing correctly is also fine.
+    indexDoc(aClient, wrongRouteParams, grandChildDoc);
+    aClient.commit();
+
+    assertEquals(1, aClient.query(params("_route_", rootId, "q", 
"id:3")).getResults().getNumFound());
   }
 
   private void indexDocAndRandomlyCommit(SolrClient client, SolrParams params, 
SolrInputDocument sdoc) throws IOException, SolrServerException {
diff --git 
a/solr/core/src/test/org/apache/solr/update/processor/NestedAtomicUpdateTest.java
 
b/solr/core/src/test/org/apache/solr/update/processor/NestedAtomicUpdateTest.java
index 4efed72..65d6bde 100644
--- 
a/solr/core/src/test/org/apache/solr/update/processor/NestedAtomicUpdateTest.java
+++ 
b/solr/core/src/test/org/apache/solr/update/processor/NestedAtomicUpdateTest.java
@@ -182,8 +182,8 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
 
     List<SolrInputDocument> docs = IntStream.range(10, 20).mapToObj(x -> 
sdoc("id", String.valueOf(x), "string_s",
         "child", "inplace_updatable_int", "0")).collect(Collectors.toList());
-    doc = sdoc("id", "1", "children", Collections.singletonMap("add", docs));
-    addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+    doc = sdoc("id", "1", "_root_", "1", "children", 
Collections.singletonMap("add", docs));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -198,28 +198,28 @@ public class NestedAtomicUpdateTest extends 
SolrTestCaseJ4 {
     );
 
     for(int i = 10; i < 20; ++i) {
-      doc = sdoc("id", String.valueOf(i), "inplace_updatable_int", 
Collections.singletonMap("inc", "1"));
-      addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+      doc = sdoc("id", String.valueOf(i), "_root_", "1", 
"inplace_updatable_int", Collections.singletonMap("inc", "1"));
+      addAndGetVersion(doc, null);
       assertU(commit());
     }
 
     for(int i = 10; i < 20; ++i) {
-      doc = sdoc("id", String.valueOf(i), "inplace_updatable_int", 
Collections.singletonMap("inc", "1"));
-      addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+      doc = sdoc("id", String.valueOf(i), "_root_", "1", 
"inplace_updatable_int", Collections.singletonMap("inc", "1"));
+      addAndGetVersion(doc, null);
       assertU(commit());
     }
 
     // ensure updates work when block has more than 10 children
     for(int i = 10; i < 20; ++i) {
       docs = IntStream.range(i * 10, (i * 10) + 5).mapToObj(x -> sdoc("id", 
String.valueOf(x), "string_s", "grandChild")).collect(Collectors.toList());
-      doc = sdoc("id", String.valueOf(i), "grandChildren", 
Collections.singletonMap("add", docs));
-      addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+      doc = sdoc("id", String.valueOf(i), "_root_", "1", "grandChildren", 
Collections.singletonMap("add", docs));
+      addAndGetVersion(doc, null);
       assertU(commit());
     }
 
     for(int i =10; i < 20; ++i) {
-      doc = sdoc("id", String.valueOf(i), "inplace_updatable_int", 
Collections.singletonMap("inc", "1"));
-      addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+      doc = sdoc("id", String.valueOf(i), "_root_", "1", 
"inplace_updatable_int", Collections.singletonMap("inc", "1"));
+      addAndGetVersion(doc, null);
       assertU(commit());
     }
 
@@ -260,7 +260,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
 
     List<SolrInputDocument> docs = IntStream.range(10, 20).mapToObj(x -> 
sdoc("id", String.valueOf(x), "string_s", 
"child")).collect(Collectors.toList());
     doc = sdoc("id", "1", "children", Collections.singletonMap("add", docs));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -281,11 +281,11 @@ public class NestedAtomicUpdateTest extends 
SolrTestCaseJ4 {
           sdoc(
               "id",
               String.valueOf(i),
-              "_root_", //shows we can specify the root field here instead of 
params
+              "_root_",
               "1",
               "grandChildren",
               Collections.singletonMap("add", docs));
-      addAndGetVersion(doc, params("wt", "json")); // no _route_ with root
+      addAndGetVersion(doc, null);
       assertU(commit());
     }
 
@@ -312,7 +312,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
     );
 
     doc = sdoc("id", "1", "child1", Collections.singletonMap("add", 
sdocs(sdoc("id", "3", "child_s", "child"))));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -324,9 +324,9 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
         "/response/docs/[0]/child1/[0]/child_s=='child'"
     );
 
-    doc = sdoc("id", "2",
+    doc = sdoc("id", "2", "_root_", "1",
         "grandChild", Collections.singletonMap("add", sdocs(sdoc("id", "4", 
"child_s", "grandChild"), sdoc("id", "5", "child_s", "grandChild"))));
-    addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -343,7 +343,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
 
     doc = sdoc("id", "1",
         "child2", Collections.singletonMap("add", sdocs(sdoc("id", "8", 
"child_s", "child"))));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -362,7 +362,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
 
     doc = sdoc("id", "1",
         "new_s", Collections.singletonMap("add", "new string"));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -420,7 +420,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
     doc = sdoc("id", "1",
         "cat_ss", Collections.singletonMap("add", "bbb"),
         "child2", Collections.singletonMap("add", sdoc("id", "3", "cat_ss", 
"child")));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
 
      assertJQ(req("qt","/get", "id","1", "fl","id, cat_ss, child1, child2, 
[child]")
@@ -442,8 +442,9 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
     );
 
     doc = sdoc("id", "2",
+        "_root_", "1",
         "child3", Collections.singletonMap("add", sdoc("id", "4", "cat_ss", 
"grandChild")));
-    addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+    addAndGetVersion(doc, null);
 
     assertJQ(req("qt","/get", "id","1", "fl","id, cat_ss, child1, child2, 
child3, [child]")
         ,"=={'doc':{'id':'1'" +
@@ -460,9 +461,9 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
     assertU(commit());
 
     //add greatGrandChild
-    doc = sdoc("id", "4",
+    doc = sdoc("id", "4", "_root_", "1",
         "child4", Collections.singletonMap("add", sdoc("id", "5", "cat_ss", 
"greatGrandChild")));
-    addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+    addAndGetVersion(doc, null);
 
     assertJQ(req("qt","/get", "id","1", "fl","id, cat_ss, child1, child2, 
child3, child4, [child]")
         ,"=={'doc':{'id':'1'" +
@@ -479,9 +480,9 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
     assertU(commit());
 
     //add another greatGrandChild
-    doc = sdoc("id", "4",
+    doc = sdoc("id", "4", "_root_", "1",
         "child4", Collections.singletonMap("add", sdoc("id", "6", "cat_ss", 
"greatGrandChild")));
-    addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -497,13 +498,13 @@ public class NestedAtomicUpdateTest extends 
SolrTestCaseJ4 {
             sdoc("id", "8", "cat_ss", "child")
         ))
     );
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
     doc = sdoc("id", "1",
         "new_s", Collections.singletonMap("add", "new string"));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     assertU(commit());
 
@@ -578,10 +579,10 @@ public class NestedAtomicUpdateTest extends 
SolrTestCaseJ4 {
             "       }}"
     );
 
-    doc = sdoc("id", "1",
+    doc = sdoc("id", "1", "_root_", "1",
         "cat_ss", Collections.singletonMap("set", Arrays.asList("aaa", "bbb")),
         "child1", Collections.singletonMap("set", sdoc("id", "3", "cat_ss", 
"child")));
-    addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+    addAndGetVersion(doc, null);
 
 
     assertJQ(req("qt","/get", "id","1", "fl","id, cat_ss, child1, [child]")
@@ -600,9 +601,9 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
             "       }}"
     );
 
-    doc = sdoc("id", "3",
+    doc = sdoc("id", "3", "_root_", "1",
         "child2", Collections.singletonMap("set", sdoc("id", "4", "cat_ss", 
"child")));
-    addAndGetVersion(doc, params("wt", "json", "_route_", "1"));
+    addAndGetVersion(doc, null);
 
     assertJQ(req("qt","/get", "id","1", "fl","id, cat_ss, child1, child2, 
[child]")
         ,"=={'doc':{'id':'1'" +
@@ -652,7 +653,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
 
     doc = sdoc("id", "1",
         "child1", Collections.singletonMap("add", sdoc("id", "2", "cat_ss", 
"child")));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     // commit the changes
     assertU(commit());
@@ -715,7 +716,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
 
     doc = sdoc("id", "1",
         "child1", Collections.singletonMap("remove", sdoc("id", "3", "cat_ss", 
"child")));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
 
     assertJQ(req("qt","/get", "id","1", "fl","id, cat_ss, child1, [child]")
@@ -793,7 +794,7 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
             ", cat_ss:[\"aaa\",\"ccc\"], 
child1:[{\"id\":\"2\",\"cat_ss\":[\"child\"]}, 
{\"id\":\"3\",\"cat_ss\":[\"child\"]}]}}");
 
     doc = sdoc("id", "1", "child1", Collections.singletonMap("set", empty ? 
new ArrayList<>() : null));
-    addAndGetVersion(doc, params("wt", "json"));
+    addAndGetVersion(doc, null);
 
     assertJQ(req("qt", "/get", "id", "1", "fl", "id, latlon, cat_ss, child1, 
[child]"),
         "=={\"doc\":{'id':\"1\", \"latlon\":\"0,0\", 
cat_ss:[\"aaa\",\"ccc\"]}}");
diff --git 
a/solr/solr-ref-guide/modules/indexing-guide/pages/partial-document-updates.adoc
 
b/solr/solr-ref-guide/modules/indexing-guide/pages/partial-document-updates.adoc
index bd5dcb6..77b97e7 100644
--- 
a/solr/solr-ref-guide/modules/indexing-guide/pages/partial-document-updates.adoc
+++ 
b/solr/solr-ref-guide/modules/indexing-guide/pages/partial-document-updates.adoc
@@ -149,12 +149,8 @@ Solr offers two solutions to address this:
 * Clients can use the (default) `compositeId` router's "prefix routing" 
feature when indexing all documents to ensure that all child/descendent 
documents in a Block use the same `id` prefix as the Root level document.
 This will cause Solr's default routing logic to automatically send child 
document updates to the correct shard.
 
-Furthermore, you _should_ (sometimes _must_) specify the Root document's ID in 
the `\_root_`
-field of this partial update.
-This is how Solr understands that you are updating a child
-document, and not a Root document.
-Without it, Solr only guesses that the `\_route_` parameter is
-equivalent, but it may be absent or not equivalent (e.g., when using the 
`implicit` router).
+Furthermore, you _must_ specify the Root document's ID in the `\_root_` field 
of this partial update.
+This is how Solr understands that you are updating a child document, and not a 
Root document.
 
 All of the examples below use `id` prefixes, so no `\_route_` parameter will 
be necessary for these examples.
 ====
diff --git 
a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc 
b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc
index a879b9b..019f04e 100644
--- 
a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc
+++ 
b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-9.adoc
@@ -333,3 +333,5 @@ where they really belong: /admin/threads, 
/admin/properties, /admin/logging
 
 * SOLR-15949: Docker: the official image now uses Java 17 provided by Eclipse 
Temurin.  Formerly it was Java 11 from OpenJDK.
 (janhoy, David Smiley)
+
+* Atomic/partial updates to nested documents now _require_ the `\_root_` field 
to clearly show the document isn't a root document.  Solr 8 would fallback on 
the `\_route_` param but no longer.
\ No newline at end of file

Reply via email to