Skip to content

Commit

Permalink
CAPI-571 Fix SolrJ Convert Child Docs Correctly
Browse files Browse the repository at this point in the history
The current solrj Document DTO to SolrInputDocument binder
treat all child docs as anonymous rather than prefix them with
the given path. This will result in the document being indexed
without the `_nest_path_` field populated which is required
for the ChildDocumentTransformer (`fl=[child]`) to work.

In order to fix this if the Document DTO contain any fields
which are also a DTO or a List/Array of DTOs, they should be
converted as SolrInputDocuments on its own and nested as sub
document(s) within the parent document.
  • Loading branch information
yfernando-bw committed Feb 2, 2024
1 parent d06ab17 commit 5da8927
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,66 @@ public SolrInputDocument toSolrInputDocument(Object obj) {
if (field.child != null) {
addChild(obj, field, doc);
} else {
doc.setField(field.name, field.get(obj));
addField(obj, field, doc);
}
}
}
return doc;
}

private void addField(Object obj, DocField field, SolrInputDocument doc) {
Object val = field.get(obj);
if (val != null && isMappedObject(val)) {
if (val instanceof Collection) {
@SuppressWarnings({"rawtypes"})
Collection collection = (Collection) val;
List<SolrInputDocument> docs = new ArrayList<>(collection.size());
for (final Object o : collection) {
final SolrInputDocument inpDoc = toSolrInputDocument(o);
docs.add(inpDoc);
}
val = docs;
} else if (val.getClass().isArray()) {
Object[] objs = (Object[]) val;
SolrInputDocument[] docs = (SolrInputDocument[]) Array.newInstance(SolrInputDocument.class, objs.length);
for (int i = 0; i < objs.length; i++) {
docs[i] = toSolrInputDocument(objs[i]);
}
val = docs;
} else {
val = toSolrInputDocument(val);
}
}

doc.addField(field.name, val);
}

private boolean isMappedObject(Object obj) {
Object first;
if (obj instanceof Collection) {
@SuppressWarnings({"rawtypes"})
Collection collection = (Collection) obj;
if (collection.isEmpty()) {
return false;
}
first = collection.iterator().next();
} else if (obj.getClass().isArray()) {
Object[] objs = (Object[]) obj;
if (objs.length == 0) {
return false;
}
first = objs[0];
} else {
first = obj;
}

return isMappedClass(first.getClass());
}

private boolean isMappedClass(Class clazz) {
return !getDocFields(clazz).isEmpty();
}

private void addChild(Object obj, DocField field, SolrInputDocument doc) {
Object val = field.get(obj);
if (val == null) return;
Expand Down Expand Up @@ -249,6 +302,16 @@ private void storeType() {
if (annotation.child()) {
populateChild(field.getGenericType());
} else {
try {
Class parameterizedType = Class.forName(((ParameterizedType) field.getGenericType())
.getActualTypeArguments()[0].getTypeName());
if (isMappedClass(parameterizedType)) {
type = parameterizedType;
return;
}
} catch (ClassNotFoundException e) {
throw new BindingException("Invalid type information available for " + (field == null ? setter : field));
}
type = Object.class;
}
} else if (type == byte[].class) {
Expand Down Expand Up @@ -333,8 +396,18 @@ private void populateChild(Type typ) {
*/
@SuppressWarnings({"unchecked", "rawtypes"})
private Object getFieldValue(SolrDocument solrDocument) {
if (child != null) {
List<SolrDocument> children = solrDocument.getChildDocuments();
if (child != null || isMappedClass(type)) {
List<SolrDocument> children = null;
if(solrDocument.hasChildDocuments()){
children = solrDocument.getChildDocuments();
} else if (solrDocument.getFieldValue(name) != null){
if (isList || isArray) {
children = (List<SolrDocument>) solrDocument.getFieldValue(name);
} else {
children = new ArrayList<>();
children.add((SolrDocument) solrDocument.getFieldValue(name));
}
}
if (children == null || children.isEmpty()) return null;
if (isList) {
ArrayList list = new ArrayList(children.size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@
import org.junit.Test;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -151,10 +153,94 @@ public void testChild() throws Exception {
assertEquals(arrIn.child[1].name, arrOut.child[1].name);

}
public void testMappedChild() throws Exception {
SingleValueMappedChild in = new SingleValueMappedChild();
in.id = "1";
in.child = new Child();
in.child.id = "1.0";
in.child.name = "Name One";
DocumentObjectBinder binder = new DocumentObjectBinder();
SolrInputDocument solrInputDoc = binder.toSolrInputDocument(in);
SolrDocument solrDoc = toSolrDocument(solrInputDoc);
assertNull(solrInputDoc.getChildDocuments());
assertNull(solrDoc.getChildDocuments());
SingleValueMappedChild out = binder.getBean(SingleValueMappedChild.class, solrDoc);
assertEquals(in.id, out.id);
assertEquals(in.child.id, out.child.id);
assertEquals(in.child.name, out.child.name);

SingleValueMappedChild inNoChild = new SingleValueMappedChild();
in.id = "1-no-child";
SolrInputDocument solrInputDocNoChild = binder.toSolrInputDocument(inNoChild);
SolrDocument solrDocNoChild = toSolrDocument(solrInputDocNoChild);
assertNull(solrInputDocNoChild.getChildDocuments());
assertNull(solrDocNoChild.getChildDocuments());
SingleValueMappedChild outNoChild = binder.getBean(SingleValueMappedChild.class, solrDocNoChild);
assertEquals(inNoChild.id, outNoChild.id);
assertNull(inNoChild.child);
assertNull(outNoChild.child);

ListMappedChild listIn = new ListMappedChild();
listIn.id = "2";
Child child = new Child();
child.id = "1.1";
child.name = "Name Two";
listIn.child = Arrays.asList(in.child, child);
solrInputDoc = binder.toSolrInputDocument(listIn);
solrDoc = toSolrDocument(solrInputDoc);
assertNull(solrInputDoc.getChildDocuments());
assertNull(solrDoc.getChildDocuments());
ListMappedChild listOut = binder.getBean(ListMappedChild.class, solrDoc);
assertEquals(listIn.id, listOut.id);
assertEquals(listIn.child.get(0).id, listOut.child.get(0).id);
assertEquals(listIn.child.get(0).name, listOut.child.get(0).name);
assertEquals(listIn.child.get(1).id, listOut.child.get(1).id);
assertEquals(listIn.child.get(1).name, listOut.child.get(1).name);

ArrayMappedChild arrIn = new ArrayMappedChild();
arrIn.id = "3";
arrIn.child = new Child[]{in.child, child};
solrInputDoc = binder.toSolrInputDocument(arrIn);
solrDoc = toSolrDocument(solrInputDoc);
assertNull(solrInputDoc.getChildDocuments());
assertNull(solrDoc.getChildDocuments());
ArrayMappedChild arrOut = binder.getBean(ArrayMappedChild.class, solrDoc);
assertEquals(arrIn.id, arrOut.id);
assertEquals(arrIn.child[0].id, arrOut.child[0].id);
assertEquals(arrIn.child[0].name, arrOut.child[0].name);
assertEquals(arrIn.child[1].id, arrOut.child[1].id);
assertEquals(arrIn.child[1].name, arrOut.child[1].name);
}

private static SolrDocument toSolrDocument(SolrInputDocument d) {
SolrDocument doc = new SolrDocument();
for (SolrInputField field : d) {
if (field.getValue() != null) {
if (field.getValue() instanceof Collection) {
Collection<?> values = (Collection<?>) field.getValue();
if (!values.isEmpty() && values.iterator().next() instanceof SolrInputDocument) {
List<SolrDocument> docs = new ArrayList<>(values.size());
for (Object value : values) {
docs.add(toSolrDocument((SolrInputDocument) value));
}
doc.setField(field.getName(), docs);
continue;
}
} else if (field.getValue().getClass().isArray()) {
Object[] values = (Object[]) field.getValue();
if (values.length > 0 && values[0] instanceof SolrInputDocument) {
SolrDocument[] docs = new SolrDocument[values.length];
for (int i = 0; i < values.length; i++) {
docs[i] = toSolrDocument((SolrInputDocument) values[i]);
}
doc.setField(field.getName(), docs);
continue;
}
} else if (field.getValue() instanceof SolrInputDocument) {
doc.setField(field.getName(), toSolrDocument((SolrInputDocument) field.getValue()));
continue;
}
}
doc.setField(field.getName(), field.getValue());
}
if (d.getChildDocuments() != null) {
Expand Down Expand Up @@ -245,7 +331,32 @@ public static class ArrayChild {
@Field(child = true)
Child[] child;
}


public static class SingleValueMappedChild {
@Field
String id;

@Field
Child child;
}

public static class ListMappedChild {

@Field
String id;

@Field
List<Child> child;
}

public static class ArrayMappedChild {

@Field
String id;

@Field
Child[] child;
}

public static class NotGettableItem {
@Field
Expand Down

0 comments on commit 5da8927

Please sign in to comment.