diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 5012c64c4ad..070aa0959c1 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -93,6 +93,9 @@ Improvements * SOLR-13720: BlockJoinParentQParser.getCachedFilter()made public for accessing from QParser plugins (Stanislav Livotov via Mikhail Khludnev) +* SOLR-13728: If a partial update (aka atomic update) is attempted on a document that has child docs, then ensure + the schema supports it (_root_ stored/docValues) by throwing an exception. (David Smiley) + Bug Fixes ---------------------- diff --git a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java index da2b71acfb2..980be8d6007 100644 --- a/solr/core/src/java/org/apache/solr/schema/IndexSchema.java +++ b/solr/core/src/java/org/apache/solr/schema/IndexSchema.java @@ -1974,6 +1974,17 @@ public class IndexSchema { return nestPathType instanceof NestPathField; } + /** + * Does this schema supports partial updates (aka atomic updates) and child docs as well. + */ + public boolean supportsPartialUpdatesOfChildDocs() { + if (savesChildDocRelations() == false) { + return false; + } + SchemaField rootField = getField(IndexSchema.ROOT_FIELD_NAME); + return rootField.stored() || rootField.hasDocValues(); + } + public PayloadDecoder getPayloadDecoder(String field) { FieldType ft = getFieldType(field); if (ft == null) 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 11667c8a60c..5e04077fb39 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 @@ -23,6 +23,8 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import com.google.common.annotations.VisibleForTesting; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.CharsRefBuilder; import org.apache.solr.client.solrj.SolrRequest; @@ -693,6 +695,13 @@ public class DistributedUpdateProcessor extends UpdateRequestProcessor { // create a new doc by default if an old one wasn't found mergedDoc = docMerger.merge(sdoc, new SolrInputDocument()); } else { + // Safety check: don't allow an update to an existing doc that has children, unless we actually support this. + if (req.getSchema().isUsableForChildDocs() // however, next line we see it doesn't support child docs + && req.getSchema().supportsPartialUpdatesOfChildDocs() == false + && req.getSearcher().count(new TermQuery(new Term(IndexSchema.ROOT_FIELD_NAME, idBytes))) > 1) { + throw new SolrException(ErrorCode.BAD_REQUEST, "This schema does not support partial updates to nested docs. See ref guide."); + } + String oldRootDocRootFieldVal = (String) oldRootDocWithChildren.getFieldValue(IndexSchema.ROOT_FIELD_NAME); if(req.getSchema().savesChildDocRelations() && oldRootDocRootFieldVal != null && !idString.equals(oldRootDocRootFieldVal)) { 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 6c66285160c..12404862887 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 @@ -18,6 +18,7 @@ package org.apache.solr.update.processor; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.List; @@ -1179,6 +1180,36 @@ public class AtomicUpdatesTest extends SolrTestCaseJ4 { "/response/docs/[0]/single_i_dvn==5"); } + /** + * Test what happens if we try to update the parent of a doc with children. + * This fails because _root_ is not stored which is currently required for doing this. + */ + @Test + public void testUpdateNestedDocUnsupported() throws Exception { + assertU(adoc(sdoc( + "id", "1", + "children", Arrays.asList(sdoc( + "id", "100", + "cat", "childCat1") + ) + ))); + + assertU(commit()); + + // update the parent doc to have a category + try { + assertU(adoc(sdoc( + "id", "1", + "cat", Collections.singletonMap("add", Arrays.asList("parentCat")) + ))); + fail("expected a failure"); + } catch (Exception e) { + assertEquals("org.apache.solr.common.SolrException: " + + "This schema does not support partial updates to nested docs. See ref guide.", e.toString()); + } + + } + @Test public void testInvalidOperation() { SolrInputDocument doc;