diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java b/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java index ef7f736eff15..47e6ef9ae4c7 100644 --- a/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java +++ b/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java @@ -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 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; @@ -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) { @@ -333,8 +396,18 @@ private void populateChild(Type typ) { */ @SuppressWarnings({"unchecked", "rawtypes"}) private Object getFieldValue(SolrDocument solrDocument) { - if (child != null) { - List children = solrDocument.getChildDocuments(); + if (child != null || isMappedClass(type)) { + List children = null; + if(solrDocument.hasChildDocuments()){ + children = solrDocument.getChildDocuments(); + } else if (solrDocument.getFieldValue(name) != null){ + if (isList || isArray) { + children = (List) 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()); diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java b/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java index 4894c7476b9f..67bc95ecb8db 100644 --- a/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java +++ b/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java @@ -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; @@ -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 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) { @@ -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; + } + + public static class ArrayMappedChild { + + @Field + String id; + + @Field + Child[] child; + } public static class NotGettableItem { @Field