SOLR-13961: Allow null/empty for removal of child doc in atomic update

Cherry pick: b5fd6d7b22
This commit is contained in:
Thomas Wöckinger 2019-11-25 09:57:07 -05:00 committed by David Smiley
parent 4cde41b828
commit 5e24a010e0
3 changed files with 71 additions and 1 deletions

View File

@ -77,6 +77,9 @@ Improvements
* SOLR-13905: Make findRequestType in AuditEvent more robust (janhoy)
* SOLR-13961: When using partial/atomic updates to remove child documents, we now support setting to null or
an empty list as equivalent to the "remove" command. (Thomas Wöckinger)
Optimizations
---------------------
(No changes)

View File

@ -552,7 +552,7 @@ public class AtomicUpdateDocumentMerger {
}
private Object getNativeFieldValue(String fieldName, Object val) {
if(isChildDoc(val)) {
if (isChildDoc(val) || val == null || (val instanceof Collection && ((Collection) val).isEmpty())) {
return val;
}
SchemaField sf = schema.getField(fieldName);

View File

@ -642,6 +642,73 @@ public class NestedAtomicUpdateTest extends SolrTestCaseJ4 {
);
}
@Test
public void testBlockAtomicSetToNull() throws Exception {
testBlockAtomicSetToNullOrEmpty(false);
}
@Test
public void testBlockAtomicSetToEmpty() throws Exception {
testBlockAtomicSetToNullOrEmpty(true);
}
private void testBlockAtomicSetToNullOrEmpty(boolean empty) throws Exception {
SolrInputDocument doc = sdoc("id", "1",
"cat_ss", new String[] {"aaa", "ccc"},
"child1", sdocs(sdoc("id", "2", "cat_ss", "child"), sdoc("id", "3", "cat_ss", "child")));
assertU(adoc(doc));
BytesRef rootDocId = new BytesRef("1");
SolrCore core = h.getCore();
SolrInputDocument block = RealTimeGetComponent.getInputDocument(core, rootDocId,
RealTimeGetComponent.Resolution.ROOT_WITH_CHILDREN);
// assert block doc has child docs
assertTrue(block.containsKey("child1"));
assertJQ(req("q", "id:1"), "/response/numFound==0");
// commit the changes
assertU(commit());
SolrInputDocument committedBlock = RealTimeGetComponent.getInputDocument(core, rootDocId,
RealTimeGetComponent.Resolution.ROOT_WITH_CHILDREN);
BytesRef childDocId = new BytesRef("2");
// ensure the whole block is returned when resolveBlock is true and id of a child doc is provided
assertEquals(committedBlock.toString(), RealTimeGetComponent
.getInputDocument(core, childDocId, RealTimeGetComponent.Resolution.ROOT_WITH_CHILDREN).toString());
assertJQ(req("q", "id:1"), "/response/numFound==1");
assertJQ(req("qt", "/get", "id", "1", "fl", "id, cat_ss, child1, [child]"), "=={\"doc\":{'id':\"1\"" +
", cat_ss:[\"aaa\",\"ccc\"], child1:[{\"id\":\"2\",\"cat_ss\":[\"child\"]}, {\"id\":\"3\",\"cat_ss\":[\"child\"]}]}}");
assertU(commit());
assertJQ(req("qt", "/get", "id", "1", "fl", "id, cat_ss, child1, [child]"), "=={\"doc\":{'id':\"1\"" +
", 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"));
assertJQ(req("qt", "/get", "id", "1", "fl", "id, cat_ss, child1, [child]"),
"=={\"doc\":{'id':\"1\", cat_ss:[\"aaa\",\"ccc\"]}}");
assertU(commit());
// a cut-n-paste of the first big query, but this time it will be retrieved from the index rather than the
// transaction log
// this requires ChildDocTransformer to get the whole block, since the document is retrieved using an index lookup
assertJQ(req("qt", "/get", "id", "1", "fl", "id, cat_ss, child1, [child]"),
"=={'doc':{'id':'1', cat_ss:[\"aaa\",\"ccc\"]}}");
// ensure the whole block has been committed correctly to the index.
assertJQ(req("q", "id:1", "fl", "*, [child]"),
"/response/numFound==1",
"/response/docs/[0]/id=='1'",
"/response/docs/[0]/cat_ss/[0]==\"aaa\"",
"/response/docs/[0]/cat_ss/[1]==\"ccc\"");
}
private static void assertDocContainsSubset(SolrInputDocument subsetDoc, SolrInputDocument fullDoc) {
for(SolrInputField field: subsetDoc) {
String fieldName = field.getName();