You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by no...@apache.org on 2009/07/15 07:55:46 UTC
svn commit: r794144 - in /lucene/solr/trunk: CHANGES.txt
src/solrj/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
Author: noble
Date: Wed Jul 15 05:55:46 2009
New Revision: 794144
URL: http://svn.apache.org/viewvc?rev=794144&view=rev
Log:
SOLR-1129 Support binding dynamic fields to beans in SolrJ
Modified:
lucene/solr/trunk/CHANGES.txt
lucene/solr/trunk/src/solrj/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
lucene/solr/trunk/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
Modified: lucene/solr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/CHANGES.txt?rev=794144&r1=794143&r2=794144&view=diff
==============================================================================
--- lucene/solr/trunk/CHANGES.txt (original)
+++ lucene/solr/trunk/CHANGES.txt Wed Jul 15 05:55:46 2009
@@ -911,7 +911,9 @@
per-handler basis (shalin)
70. SOLR-716: Added support for properties in configuration files. Properties can be specified in
- solr.xml and can be used in solrconfig.xml and schema.xml (Henri Biestro, hossman, ryan, shalin)
+ solr.xml and can be used in solrconfig.xml and schema.xml (Henri Biestro, hossman, ryan, shalin)
+
+71. SOLR-1129 : Support binding dynamic fields to beans in SolrJ (Avlesh Singh , noble)
Changes in runtime behavior
1. SOLR-559: use Lucene updateDocument, deleteDocuments methods. This
Modified: lucene/solr/trunk/src/solrj/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/solrj/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java?rev=794144&r1=794143&r2=794144&view=diff
==============================================================================
--- lucene/solr/trunk/src/solrj/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java (original)
+++ lucene/solr/trunk/src/solrj/org/apache/solr/client/solrj/beans/DocumentObjectBinder.java Wed Jul 15 05:55:46 2009
@@ -19,11 +19,9 @@
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrInputDocument;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Method;
-import java.lang.reflect.Array;
+import java.lang.reflect.*;
import java.util.*;
+import java.util.regex.Pattern;
import java.util.concurrent.ConcurrentHashMap;
import java.nio.ByteBuffer;
@@ -49,7 +47,6 @@
T obj = null;
try {
obj = clazz.newInstance();
- result.add(obj);
} catch (Exception e) {
throw new RuntimeException("Could not instantiate object of " + clazz,e);
}
@@ -57,6 +54,7 @@
DocField docField = fields.get(i);
docField.inject(obj, sdoc);
}
+ result.add(obj);
}
return result;
}
@@ -112,6 +110,14 @@
private Class type;
private boolean isArray = false, isList=false;
+ /*
+ * dynamic fields may use a Map based data structure to bind a given field.
+ * if a mapping is done using, "Map<String, List<String>> foo", <code>isContainedInMap</code>
+ * is set to <code>TRUE</code> as well as <code>isList</code> is set to <code>TRUE</code>
+ */
+ boolean isContainedInMap =false;
+ private Pattern dynamicFieldNamePatternMatcher;
+
public DocField(AccessibleObject member) {
if (member instanceof java.lang.reflect.Field) {
field = (java.lang.reflect.Field) member;
@@ -158,6 +164,13 @@
name = setter.getName();
}
}
+ }
+ //dynamic fields are annotated as @Field("categories_*")
+ else if(annotation.value().indexOf('*') >= 0){
+ //if the field was annotated as a dynamic field, convert the name into a pattern
+ //the wildcard (*) is supposed to be either a prefix or a suffix, hence the use of replaceFirst
+ name = annotation.value().replaceFirst("\\*", "\\.*");
+ dynamicFieldNamePatternMatcher = Pattern.compile("^"+name+"$");
} else {
name = annotation.value();
}
@@ -189,40 +202,150 @@
isArray = true;
type = type.getComponentType();
}
+ //corresponding to the support for dynamicFields
+ else if (type == Map.class || type == HashMap.class) {
+ isContainedInMap = true;
+ //assigned a default type
+ type = Object.class;
+ if(field != null){
+ if(field.getGenericType() instanceof ParameterizedType){
+ //check what are the generic values
+ ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
+ Type[] types = parameterizedType.getActualTypeArguments();
+ if(types != null && types.length == 2 && types[0] == String.class){
+ //the key should always be String
+ //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){
+ type = Object.class;
+ isList = true;
+ }else{
+ //else assume it is a primitive and put in the source type itself
+ type = (Class) types[1];
+ }
+ }
+ //Of all the Parameterized types, only List is supported
+ else if(types[1] instanceof ParameterizedType){
+ Type rawType = ((ParameterizedType)types[1]).getRawType();
+ if(rawType== Collection.class || rawType == List.class || rawType == ArrayList.class){
+ type = Object.class;
+ isList = true;
+ }
+ }
+ //Array types
+ else if(types[1] instanceof GenericArrayType){
+ type = (Class) ((GenericArrayType) types[1]).getGenericComponentType();
+ isArray = true;
+ }
+ //Throw an Exception if types are not known
+ else{
+ throw new RuntimeException("Allowed type for values of mapping a dynamicField are : " +
+ "Object, Object[] and List");
+ }
+ }
+ }
+ }
+ }
}
- public <T> void inject(T obj, SolrDocument sdoc) {
- Object val = sdoc.getFieldValue(name);
- if(val == null) return;
- if (isArray) {
- if (val instanceof List) {
- List collection = (List) val;
- set(obj, collection.toArray((Object[]) Array.newInstance(type,collection.size())));
+ /**
+ * 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>
+ *
+ * Returns <code>SolrDocument.getFieldValue</code> for regular fields,
+ * and <code>Map<String, List<Object>></code> for a dynamic field. The key is all matching fieldName's.
+ */
+ @SuppressWarnings("unchecked")
+ private Object getFieldValue(SolrDocument sdoc){
+ Object fieldValue = sdoc.getFieldValue(name);
+ if(fieldValue != null) {
+ //this is not a dynamic field. so return te value
+ return fieldValue;
+ }
+ //reading dynamic field values
+ if(dynamicFieldNamePatternMatcher != null){
+ Map<String, Object> allValuesMap = null;
+ ArrayList allValuesList = null;
+ if(isContainedInMap){
+ allValuesMap = new HashMap<String, Object>();
} else {
- Object[] arr = (Object[]) Array.newInstance(type, 1);
- arr[0] = val;
- set(obj, arr);
- }
- } else if (isList) {
- if (val instanceof List) {
- set(obj, val);
+ allValuesList = new ArrayList();
+ }
+ for(String field : sdoc.getFieldNames()){
+ if(dynamicFieldNamePatternMatcher.matcher(field).find()){
+ Object val = sdoc.getFieldValue(field);
+ if(val == null) continue;
+ if(isContainedInMap){
+ if(isList){
+ if (!(val instanceof List)) {
+ ArrayList al = new ArrayList();
+ al.add(val);
+ val = al;
+ }
+ } else if(isArray){
+ if (!(val instanceof List)) {
+ Object[] arr= (Object[]) Array.newInstance(type,1);
+ arr[0] = val;
+ val= arr;
+ } else {
+ val = Array.newInstance(type,((List)val).size());
+ }
+ }
+ allValuesMap.put(field, val);
+ }else {
+ if (val instanceof Collection) {
+ allValuesList.addAll((Collection) val);
+ } else {
+ allValuesList.add(val);
+ }
+ }
+ }
+ }
+ if (isContainedInMap) {
+ return allValuesMap.isEmpty() ? null : allValuesMap;
} else {
- ArrayList l = new ArrayList();
- l.add(val);
- set(obj, l);
+ return allValuesList.isEmpty() ? null : allValuesList;
}
- } else {
- if (val instanceof List) {
- List l = (List) val;
- if(l.size()>0)
- set(obj, l.get(0));
- }
- else {
- set(obj,val) ;
+ }
+ return null;
+ }
+ <T> void inject(T obj, SolrDocument sdoc) {
+ Object val = getFieldValue(sdoc);
+ if(val == null) {
+ System.out.println("val null for "+ name);
+ return;
+ }
+ if(isArray && !isContainedInMap){
+ List list = null;
+ if(val.getClass().isArray()){
+ set(obj,val);
+ return;
+ } else if (val instanceof List) {
+ list = (List) val;
+ } else{
+ list = new ArrayList();
+ list.add(val);
}
+ set(obj, list.toArray((Object[]) Array.newInstance(type,list.size())));
+ } else if(isList && !isContainedInMap){
+ if (!(val instanceof List)) {
+ ArrayList list = new ArrayList();
+ list.add(val);
+ val = list;
+ }
+ set(obj, val);
+ } else if(isContainedInMap){
+ if (val instanceof Map) {
+ set(obj, val);
+ }
+ } else {
+ set(obj, val);
}
+
}
-
+
+
private void set(Object obj, Object v) {
if(v!= null && type == ByteBuffer.class && v.getClass()== byte[].class) {
v = ByteBuffer.wrap((byte[])v);
Modified: lucene/solr/trunk/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java
URL: http://svn.apache.org/viewvc/lucene/solr/trunk/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java?rev=794144&r1=794143&r2=794144&view=diff
==============================================================================
--- lucene/solr/trunk/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java (original)
+++ lucene/solr/trunk/src/test/org/apache/solr/client/solrj/beans/TestDocumentObjectBinder.java Wed Jul 15 05:55:46 2009
@@ -18,8 +18,6 @@
import junit.framework.TestCase;
-import org.apache.solr.client.solrj.beans.DocumentObjectBinder;
-import org.apache.solr.client.solrj.beans.Field;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.util.ClientUtils;
@@ -34,6 +32,7 @@
import java.util.Arrays;
import java.util.Date;
import java.util.List;
+import java.util.Map;
public class TestDocumentObjectBinder extends TestCase
{
@@ -57,7 +56,7 @@
SolrInputField catfield = out.getField( "cat" );
Assert.assertEquals( 3, catfield.getValueCount() );
Assert.assertEquals( "[aaa, bbb, ccc]", catfield.getValue().toString() );
-
+
// Test the error on not settable stuff...
NotGettableItem ng = new NotGettableItem();
ng.setInStock( false );
@@ -79,7 +78,21 @@
Assert.assertEquals("hello", l.get(0).categories[0]);
}
-
+
+ public void testDynamicFieldBinding(){
+ DocumentObjectBinder binder = new DocumentObjectBinder();
+ XMLResponseParser parser = new XMLResponseParser();
+ NamedList<Object> nl = parser.processResponse(new StringReader(xml));
+ QueryResponse res = new QueryResponse(nl, null);
+ List<Item> l = binder.getBeans(Item.class,res.getResults());
+ Assert.assertArrayEquals(new String[]{"Mobile Store","iPod Store","CCTV Store"}, l.get(3).getAllSuppliers());
+ Assert.assertTrue(l.get(3).supplier.containsKey("supplier_1"));
+ Assert.assertTrue(l.get(3).supplier.containsKey("supplier_2"));
+ Assert.assertEquals(2, l.get(3).supplier.size());
+ Assert.assertEquals("[Mobile Store, iPod Store]", l.get(3).supplier.get("supplier_1").toString());
+ Assert.assertEquals("[CCTV Store]", l.get(3).supplier.get("supplier_2").toString());
+ }
+
public void testToAndFromSolrDocument()
{
Item item = new Item();
@@ -119,6 +132,20 @@
boolean inStock = false;
+ @Field("supplier_*")
+ Map<String, List<String>> supplier;
+
+ private String[] allSuppliers;
+
+ @Field("supplier_*")
+ public void setAllSuppliers(String[] allSuppliers){
+ this.allSuppliers = allSuppliers;
+ }
+
+ public String[] getAllSuppliers(){
+ return this.allSuppliers;
+ }
+
@Field
public void setInStock(Boolean b) {
inStock = b;
@@ -175,6 +202,7 @@
"<str>car power adapter for iPod, white</str></arr><str name=\"id\">IW-02</str><bool name=\"inStock\">false</bool>" +
"<str name=\"manu\">Belkin</str><str name=\"name\">iPod & iPod Mini USB 2.0 Cable</str>" +
"<int name=\"popularity\">1</int><float name=\"price\">11.5</float><str name=\"sku\">IW-02</str>" +
+ "<str name=\"supplier_1\">Mobile Store</str><str name=\"supplier_1\">iPod Store</str><str name=\"supplier_2\">CCTV Store</str>" +
"<date name=\"timestamp\">2008-04-16T10:35:57.140Z</date><float name=\"weight\">2.0</float></doc></result>\n" +
"</response>";
}