You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2015/02/14 20:04:23 UTC
svn commit: r1659845 - in /lucene/dev/trunk/solr: ./
solrj/src/java/org/apache/solr/client/solrj/beans/
solrj/src/java/org/apache/solr/client/solrj/util/
solrj/src/test/org/apache/solr/client/solrj/beans/
Author: noble
Date: Sat Feb 14 19:04:22 2015
New Revision: 1659845
URL: http://svn.apache.org/r1659845
Log:
SOLR-1945: Add support for child docs in DocumentObjectBinder
Modified:
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/Field.java
lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java
lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1659845&r1=1659844&r2=1659845&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Sat Feb 14 19:04:22 2015
@@ -114,6 +114,8 @@ New Features
* SOLR-6832: Queries be served locally rather than being forwarded to another replica.
(Sachin Goyal, Timothy Potter)
+* SOLR-1945 : Add support for child docs in DocumentObjectBinder (Noble Paul, Mark Miller)
+
Bug Fixes
----------------------
Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java?rev=1659845&r1=1659844&r2=1659845&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java Sat Feb 14 19:04:22 2015
@@ -28,12 +28,12 @@ import java.nio.ByteBuffer;
/**
* A class to map objects to and from solr documents.
- *
+ *
*
* @since solr 1.3
*/
public class DocumentObjectBinder {
-
+
private final Map<Class, List<DocField>> infocache = new ConcurrentHashMap<>();
public DocumentObjectBinder() {
@@ -52,7 +52,7 @@ public class DocumentObjectBinder {
public <T> T getBean(Class<T> clazz, SolrDocument solrDoc) {
return getBean(clazz, null, solrDoc);
}
-
+
private <T> T getBean(Class<T> clazz, List<DocField> fields, SolrDocument solrDoc) {
if (fields == null) {
fields = getDocFields(clazz);
@@ -68,7 +68,7 @@ public class DocumentObjectBinder {
throw new BindingException("Could not instantiate object of " + clazz, e);
}
}
-
+
public SolrInputDocument toSolrInputDocument(Object obj) {
List<DocField> fields = getDocFields(obj.getClass());
if (fields.isEmpty()) {
@@ -86,12 +86,33 @@ public class DocumentObjectBinder {
doc.setField(e.getKey(), e.getValue(), 1.0f);
}
} else {
- doc.setField(field.name, field.get(obj), 1.0f);
+ if (field.child != null) {
+ addChild(obj, field, doc);
+ } else {
+ doc.setField(field.name, field.get(obj), 1.0f);
+ }
}
}
return doc;
}
-
+
+ private void addChild(Object obj, DocField field, SolrInputDocument doc) {
+ Object val = field.get(obj);
+ if (val == null) return;
+ if (val instanceof Collection) {
+ Collection collection = (Collection) val;
+ for (Object o : collection) {
+ SolrInputDocument child = toSolrInputDocument(o);
+ doc.addChildDocument(child);
+ }
+ } else if (val.getClass().isArray()) {
+ Object[] objs = (Object[]) val;
+ for (Object o : objs) doc.addChildDocument(toSolrInputDocument(o));
+ } else {
+ doc.addChildDocument(toSolrInputDocument(val));
+ }
+ }
+
private List<DocField> getDocFields(Class clazz) {
List<DocField> fields = infocache.get(clazz);
if (fields == null) {
@@ -112,17 +133,24 @@ public class DocumentObjectBinder {
members.addAll(Arrays.asList(superClazz.getDeclaredMethods()));
superClazz = superClazz.getSuperclass();
}
-
+ boolean childFieldFound = false;
for (AccessibleObject member : members) {
if (member.isAnnotationPresent(Field.class)) {
member.setAccessible(true);
- fields.add(new DocField(member));
+ DocField df = new DocField(member);
+ if (df.child != null) {
+ if (childFieldFound)
+ throw new BindingException(clazz.getName() + " cannot have more than one Field with child=true");
+ childFieldFound = true;
+ }
+ fields.add(df);
}
}
return fields;
}
- private static class DocField {
+ private class DocField {
+ private Field annotation;
private String name;
private java.lang.reflect.Field field;
private Method setter;
@@ -130,6 +158,7 @@ public class DocumentObjectBinder {
private Class type;
private boolean isArray;
private boolean isList;
+ private List<DocField> child;
/*
* dynamic fields may use a Map based data structure to bind a given field.
@@ -145,10 +174,10 @@ public class DocumentObjectBinder {
} else {
setter = (Method) member;
}
- Field annotation = member.getAnnotation(Field.class);
+ annotation = member.getAnnotation(Field.class);
storeName(annotation);
storeType();
-
+
// Look for a matching getter
if (setter != null) {
String gname = setter.getName();
@@ -172,7 +201,7 @@ public class DocumentObjectBinder {
}
private void storeName(Field annotation) {
- if (annotation.value().equals(Field.DEFAULT)) {
+ if (annotation.value().equals(DEFAULT)) {
if (field != null) {
name = field.getName();
} else {
@@ -204,15 +233,24 @@ public class DocumentObjectBinder {
type = params[0];
}
- if(type == Collection.class || type == List.class || type == ArrayList.class) {
- type = Object.class;
+ if (type == Collection.class || type == List.class || type == ArrayList.class) {
isList = true;
+ if (annotation.child()) {
+ populateChild(field.getGenericType());
+ } else {
+ type = Object.class;
+ }
} else if (type == byte[].class) {
//no op
} else if (type.isArray()) {
isArray = true;
- type = type.getComponentType();
+ if (annotation.child()) {
+ populateChild(type.getComponentType());
+ } else {
+ type = type.getComponentType();
+ }
} else if (type == Map.class || type == HashMap.class) { //corresponding to the support for dynamicFields
+ if (annotation.child()) throw new BindingException("Map should is not a valid type for a child document");
isContainedInMap = true;
//assigned a default type
type = Object.class;
@@ -226,7 +264,7 @@ public class DocumentObjectBinder {
//Raw and primitive types
if (types[1] instanceof Class) {
//the value could be multivalued then it is a List, Collection, ArrayList
- if (types[1]== Collection.class || types[1] == List.class || types[1] == ArrayList.class) {
+ if (types[1] == Collection.class || types[1] == List.class || types[1] == ArrayList.class) {
type = Object.class;
isList = true;
} else {
@@ -234,8 +272,8 @@ public class DocumentObjectBinder {
type = (Class) types[1];
}
} else if (types[1] instanceof ParameterizedType) { //Of all the Parameterized types, only List is supported
- Type rawType = ((ParameterizedType)types[1]).getRawType();
- if(rawType== Collection.class || rawType == List.class || rawType == ArrayList.class){
+ Type rawType = ((ParameterizedType) types[1]).getRawType();
+ if (rawType == Collection.class || rawType == List.class || rawType == ArrayList.class) {
type = Object.class;
isList = true;
}
@@ -249,9 +287,32 @@ public class DocumentObjectBinder {
}
}
}
+ } else {
+ if (annotation.child()) {
+ populateChild(type);
+ }
}
}
+ private void populateChild(Type typ) {
+ if (typ == null) {
+ throw new RuntimeException("no type information available for" + (field == null ? setter : field));
+ }
+ if (typ.getClass() == Class.class) {//of type class
+ type = (Class) typ;
+ } else if (typ instanceof ParameterizedType) {
+ try {
+ type = Class.forName(((ParameterizedType) typ).getActualTypeArguments()[0].getTypeName());
+ } catch (ClassNotFoundException e) {
+ throw new BindingException("Invalid type information available for" + (field == null ? setter : field));
+ }
+ } else {
+ throw new BindingException("Invalid type information available for" + (field == null ? setter : field));
+
+ }
+ child = getDocFields(type);
+ }
+
/**
* Called by the {@link #inject} method to read the value(s) for a field
* This method supports reading of all "matching" fieldName's in the <code>SolrDocument</code>
@@ -261,6 +322,26 @@ public class DocumentObjectBinder {
*/
@SuppressWarnings("unchecked")
private Object getFieldValue(SolrDocument solrDocument) {
+ if (child != null) {
+ List<SolrDocument> children = solrDocument.getChildDocuments();
+ if (children == null || children.isEmpty()) return null;
+ if (isList) {
+ ArrayList list = new ArrayList(children.size());
+ for (SolrDocument c : children) {
+ list.add(getBean(type, child, c));
+ }
+ return list;
+ } else if (isArray) {
+ Object[] arr = (Object[]) Array.newInstance(type, children.size());
+ for (int i = 0; i < children.size(); i++) {
+ arr[i] = getBean(type, child, children.get(i));
+ }
+ return arr;
+
+ } else {
+ return getBean(type, child, children.get(0));
+ }
+ }
Object fieldValue = solrDocument.getFieldValue(name);
if (fieldValue != null) {
//this is not a dynamic field. so return the value
@@ -365,7 +446,7 @@ public class DocumentObjectBinder {
} else if (setter != null) {
setter.invoke(obj, v);
}
- }
+ }
catch (Exception e) {
throw new BindingException("Exception while setting value : " + v + " on " + (field != null ? field : setter), e);
}
@@ -389,4 +470,5 @@ public class DocumentObjectBinder {
}
}
}
+ public static final String DEFAULT = "#default";
}
Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/Field.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/Field.java?rev=1659845&r1=1659844&r2=1659845&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/Field.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/beans/Field.java Sat Feb 14 19:04:22 2015
@@ -18,18 +18,21 @@ package org.apache.solr.client.solrj.bea
import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.apache.solr.client.solrj.beans.DocumentObjectBinder.DEFAULT;
import java.lang.annotation.Target;
import java.lang.annotation.Retention;
/**
+ * This class can be used to annotate a field or a setter an any class
+ * and SlrJ would help you convert to SolrInputDocument and from SolrDocument
*
* @since solr 1.3
*/
@Target({FIELD, METHOD})
@Retention(RUNTIME)
public @interface Field {
- public static final String DEFAULT ="#default";
+ boolean child() default false;
String value() default DEFAULT;
}
Modified: lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java?rev=1659845&r1=1659844&r2=1659845&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java (original)
+++ lucene/dev/trunk/solr/solrj/src/java/org/apache/solr/client/solrj/util/ClientUtils.java Sat Feb 14 19:04:22 2015
@@ -83,11 +83,16 @@ public class ClientUtils
* @param d SolrInputDocument to convert
* @return a SolrDocument with the same fields and values as the SolrInputDocument
*/
- public static SolrDocument toSolrDocument( SolrInputDocument d )
- {
+ public static SolrDocument toSolrDocument(SolrInputDocument d) {
SolrDocument doc = new SolrDocument();
- for( SolrInputField field : d ) {
- doc.setField( field.getName(), field.getValue() );
+ for (SolrInputField field : d) {
+ doc.setField(field.getName(), field.getValue());
+ }
+ if (d.getChildDocuments() != null) {
+ for (SolrInputDocument in : d.getChildDocuments()) {
+ doc.addChildDocument(toSolrDocument(in));
+ }
+
}
return doc;
}
Modified: lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java?rev=1659845&r1=1659844&r2=1659845&view=diff
==============================================================================
--- lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java (original)
+++ lucene/dev/trunk/solr/solrj/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java Sat Feb 14 19:04:22 2015
@@ -26,7 +26,6 @@ import org.apache.solr.common.SolrInputD
import org.apache.solr.common.SolrInputField;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.util.NamedList;
-import org.junit.Assert;
import org.junit.Test;
import java.io.StringReader;
@@ -36,7 +35,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import static org.junit.Assert.*;
public class TestDocumentObjectBinder extends LuceneTestCase {
@@ -164,6 +162,52 @@ public class TestDocumentObjectBinder ex
assertEquals(supB, out1.supplier.get("supplier_supB"));
}
+ public void testChild() throws Exception {
+ SingleValueChild in = new SingleValueChild();
+ in.id = "1";
+ in.child = new Child();
+ in.child.id = "1.0";
+ in.child.name = "Name One";
+ DocumentObjectBinder binder = new DocumentObjectBinder();
+ SolrInputDocument doc = binder.toSolrInputDocument(in);
+ assertEquals(1, doc.getChildDocuments().size());
+ assertEquals(1, ClientUtils.toSolrDocument(doc).getChildDocuments().size());
+ SingleValueChild out = binder.getBean(SingleValueChild.class, ClientUtils.toSolrDocument(doc));
+ assertEquals(in.id, out.id);
+ assertEquals(in.child.id, out.child.id);
+ assertEquals(in.child.name, out.child.name);
+
+ ListChild listIn = new ListChild();
+ listIn.id = "2";
+ Child child = new Child();
+ child.id = "1.1";
+ child.name = "Name Two";
+ listIn.child = Arrays.asList(in.child, child);
+ doc = binder.toSolrInputDocument(listIn);
+ assertEquals(2, doc.getChildDocuments().size());
+ assertEquals(2, ClientUtils.toSolrDocument(doc).getChildDocuments().size());
+ ListChild listOut = binder.getBean(ListChild.class, ClientUtils.toSolrDocument(doc));
+ 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);
+
+ ArrayChild arrIn = new ArrayChild();
+ arrIn.id = "3";
+ arrIn.child = new Child[]{in.child, child};
+ doc = binder.toSolrInputDocument(arrIn);
+ assertEquals(2, doc.getChildDocuments().size());
+ assertEquals(2, ClientUtils.toSolrDocument(doc).getChildDocuments().size());
+ ArrayChild arrOut = binder.getBean(ArrayChild.class, ClientUtils.toSolrDocument(doc));
+ 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);
+
+ }
+
public static class Item {
@Field
String id;
@@ -184,7 +228,7 @@ public class TestDocumentObjectBinder ex
@Field("supplier_*")
Map<String, List<String>> supplier;
-
+
@Field("sup_simple_*")
Map<String, String> supplier_simple;
@@ -192,7 +236,7 @@ public class TestDocumentObjectBinder ex
@Field("supplier_*")
public void setAllSuppliers(String[] allSuppliers) {
- this.allSuppliers = allSuppliers;
+ this.allSuppliers = allSuppliers;
}
public String[] getAllSuppliers() {
@@ -203,12 +247,47 @@ public class TestDocumentObjectBinder ex
public void setInStock(Boolean b) {
inStock = b;
}
-
+
// required if you want to fill SolrDocuments with the same annotaion...
public boolean isInStock() {
return inStock;
}
}
+
+ public static class Child {
+ @Field
+ String id;
+
+ @Field
+ String name;
+
+ }
+
+ public static class SingleValueChild {
+ @Field
+ String id;
+
+ @Field(child = true)
+ Child child;
+ }
+
+ public static class ListChild {
+ @Field
+ String id;
+
+ @Field(child = true)
+ List<Child> child;
+
+ }
+
+ public static class ArrayChild {
+
+ @Field
+ String id;
+
+ @Field(child = true)
+ Child[] child;
+ }
public static class NotGettableItem {