lucene-solr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From no...@apache.org
Subject 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
Date Wed, 15 Jul 2009 05:55:46 GMT
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 &amp; 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>";
 }



Mime
View raw message