Solr
  1. Solr
  2. SOLR-536

Automatic binding of results to Beans (for solrj)

    Details

    • Type: New Feature New Feature
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 1.3
    • Fix Version/s: 1.3
    • Component/s: clients - java
    • Labels:
      None

      Description

      as we are using java5 .we can use annotations to bind SolrDocument to java beans directly.

      This can make the usage of solrj a bit simpler

      The QueryResponse class in solrj can have an extra method as follows

      public <T> List<T> getResultBeans(Class<T> klass)

      and the bean can have annotations as

      class MyBean{
      @Field("id") //name is optional
      String id;

      @Field("category")
      List<String> categories
      }

      1. SOLR-536.patch
        6 kB
        Noble Paul
      2. SOLR-536.patch
        19 kB
        Ryan McKinley
      3. SOLR-536.patch
        13 kB
        Noble Paul
      4. SOLR-536.patch
        12 kB
        Noble Paul
      5. SOLR-DocObjBinder.patch
        2 kB
        Noble Paul

        Activity

        Hide
        Noble Paul added a comment -

        user reported bug
        I have a multivalued Solr text field, called 'categories', which is mapped
        to a String[] in my java bean. I am directly converting the search results
        to this bean.
        This works absolutely fine if the field has two or more values, but If the
        field has exactly one value, I get the following exception -

        *Caused by: java.lang.RuntimeException: Exception while setting value
        : [Ljava.lang.Object;@15b48b2 on private java.lang.String[]
        com.app.model.Unit.categories
        at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.set(DocumentObjectBinder.java:230)
        at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.inject(DocumentObjectBinder.java:199)
        at org.apache.solr.client.solrj.beans.DocumentObjectBinder.getBeans(DocumentObjectBinder.java:57)
        at org.apache.solr.client.solrj.response.QueryResponse.getBeans(QueryResponse.java:256)

        Fixed

        Show
        Noble Paul added a comment - user reported bug I have a multivalued Solr text field, called 'categories', which is mapped to a String[] in my java bean. I am directly converting the search results to this bean. This works absolutely fine if the field has two or more values, but If the field has exactly one value, I get the following exception - *Caused by: java.lang.RuntimeException: Exception while setting value : [Ljava.lang.Object;@15b48b2 on private java.lang.String[] com.app.model.Unit.categories at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.set(DocumentObjectBinder.java:230) at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.inject(DocumentObjectBinder.java:199) at org.apache.solr.client.solrj.beans.DocumentObjectBinder.getBeans(DocumentObjectBinder.java:57) at org.apache.solr.client.solrj.response.QueryResponse.getBeans(QueryResponse.java:256) Fixed
        Hide
        Ryan McKinley added a comment -

        ok, I put it back in rev 671037

        Show
        Ryan McKinley added a comment - ok, I put it back in rev 671037
        Hide
        Noble Paul added a comment -

        let us make the field transient.

          private transient SolrServer solrServer;
        

        Even if the field is absent it still works

        This method is very useful because this is a client API and the user should be exposed to minimal classes/interfaces

        Show
        Noble Paul added a comment - let us make the field transient. private transient SolrServer solrServer; Even if the field is absent it still works This method is very useful because this is a client API and the user should be exposed to minimal classes/interfaces
        Hide
        Ryan McKinley added a comment -

        i removed it in rev671034 – with SolrServer in the response, the response is not serializable...

        Show
        Ryan McKinley added a comment - i removed it in rev671034 – with SolrServer in the response, the response is not serializable...
        Hide
        Ryan McKinley added a comment -

        hymmm, after using this for a bit, i'm not sure we should have the utility funcion:

        public <T> List<T> getBeans(Class<T> type)
        

        in QueryResponse. My problem with it now is that if I use the response in a context where it needs to be serialized (wicket), then the SolrServer also needs to get serialized... that is a problem. I think its better to keep that functionality outside of the QueryResponse class.

        Show
        Ryan McKinley added a comment - hymmm, after using this for a bit, i'm not sure we should have the utility funcion: public <T> List<T> getBeans( Class <T> type) in QueryResponse. My problem with it now is that if I use the response in a context where it needs to be serialized (wicket), then the SolrServer also needs to get serialized... that is a problem. I think its better to keep that functionality outside of the QueryResponse class.
        Hide
        Noble Paul added a comment -

        Adds methods to SolrServer to directly add beans to Solr
        all add() has a corresponding addBean method

        This can help mask people from SolrinputDocument, DocumentObjectBinder etc .a non-static field is added to SolrServer

        private DocumentObjectBinder binder;
        
        Show
        Noble Paul added a comment - Adds methods to SolrServer to directly add beans to Solr all add() has a corresponding addBean method This can help mask people from SolrinputDocument, DocumentObjectBinder etc .a non-static field is added to SolrServer private DocumentObjectBinder binder;
        Hide
        Noble Paul added a comment -

        yes concurrency is impotant. The cache.get() is called by too many threads in a webapp . Collections.synchronizedMap() has the get() synchronized so more contention. cost per operation is less but waiting time can be longer for Collections..synchronizedMap()

        Show
        Noble Paul added a comment - yes concurrency is impotant. The cache.get() is called by too many threads in a webapp . Collections.synchronizedMap() has the get() synchronized so more contention. cost per operation is less but waiting time can be longer for Collections..synchronizedMap()
        Hide
        Mike Klaas added a comment -

        > This is expensive
        > private final Map<Class, List<DocField>> infocache =
        > Collections.synchronizedMap( new HashMap<Class, List<DocField>>() );

        > Let us make it
        > private final Map<Class, List<DocField>> infocache =
        > new ConcurrentHashMap<Class, List<DocField>>() ;

        Expensive? I'd expect the synchronizedMap to be faster and more memory compact. The ConcurrentHashMap is definitely more concurrent, though.

        Show
        Mike Klaas added a comment - > This is expensive > private final Map<Class, List<DocField>> infocache = > Collections.synchronizedMap( new HashMap<Class, List<DocField>>() ); > Let us make it > private final Map<Class, List<DocField>> infocache = > new ConcurrentHashMap<Class, List<DocField>>() ; Expensive? I'd expect the synchronizedMap to be faster and more memory compact. The ConcurrentHashMap is definitely more concurrent, though.
        Hide
        Ryan McKinley added a comment -

        done. thanks

        Show
        Ryan McKinley added a comment - done. thanks
        Hide
        Noble Paul added a comment -

        Ryan:
        This is expensive

         private final Map<Class, List<DocField>> infocache = 
            Collections.synchronizedMap( new HashMap<Class, List<DocField>>() );
        

        Let us make it

         private final Map<Class, List<DocField>> infocache = 
            new ConcurrentHashMap<Class, List<DocField>>() ;
        
        Show
        Noble Paul added a comment - Ryan: This is expensive private final Map< Class , List<DocField>> infocache = Collections.synchronizedMap( new HashMap< Class , List<DocField>>() ); Let us make it private final Map< Class , List<DocField>> infocache = new ConcurrentHashMap< Class , List<DocField>>() ;
        Hide
        Ryan McKinley added a comment -

        This was commited with some minor changes – it was moved out of the 'response' package (since it also handles object construction) and put into a "beans" package.

        thanks Noble

        Show
        Ryan McKinley added a comment - This was commited with some minor changes – it was moved out of the 'response' package (since it also handles object construction) and put into a "beans" package. thanks Noble
        Hide
        Noble Paul added a comment -

        Let us remove that method . without static cache it might lead to incorrect usage

        The cache cant grow much. One instance of DocField per class. Unless creates classes during runtime it may not cause a problem.

        Show
        Noble Paul added a comment - Let us remove that method . without static cache it might lead to incorrect usage The cache cant grow much. One instance of DocField per class. Unless creates classes during runtime it may not cause a problem.
        Hide
        Ryan McKinley added a comment -

        I think we should remove it from QueryResponce. After SOLR-215, I'm reluctant to bake in static variables unless absolutely necessary. In this case, I don't think using DocumentObjectBinder directly is a big deal – it also lets the user decide if access needs to be synchronized or not.

        We can always add the function (and static cache) later if it is necessary.

        Show
        Ryan McKinley added a comment - I think we should remove it from QueryResponce. After SOLR-215 , I'm reluctant to bake in static variables unless absolutely necessary. In this case, I don't think using DocumentObjectBinder directly is a big deal – it also lets the user decide if access needs to be synchronized or not. We can always add the function (and static cache) later if it is necessary.
        Hide
        Noble Paul added a comment -

        The objective was to mask people from DocumentObjectBinder altogether .

        The process of reading the annotations and caching the information is expensive.
        If we make the cache field static in DocumentIObjectBinder this should be fine. Otherwise we must remove this method.

        Show
        Noble Paul added a comment - The objective was to mask people from DocumentObjectBinder altogether . The process of reading the annotations and caching the information is expensive. If we make the cache field static in DocumentIObjectBinder this should be fine. Otherwise we must remove this method.
        Hide
        Ryan McKinley added a comment -

        Here is an updated version of the patch that also lets you use the same annotation to convert from an object to a SolrInputDocument.

        I still wonder if we should have the utility function in QueryResponse.java:

        public <T> List<T> getBeans(Class<T> klass){
          return new DocumentObjectBinder().getBeans(klass, getResults());
        }
        

        it seems like hanging on to a DocumentObjectBinder outside of the response will be more efficient. (Though perhaps not a big deal)

        Show
        Ryan McKinley added a comment - Here is an updated version of the patch that also lets you use the same annotation to convert from an object to a SolrInputDocument. I still wonder if we should have the utility function in QueryResponse.java: public <T> List<T> getBeans( Class <T> klass){ return new DocumentObjectBinder().getBeans(klass, getResults()); } it seems like hanging on to a DocumentObjectBinder outside of the response will be more efficient. (Though perhaps not a big deal)
        Hide
        Noble Paul added a comment -

        Incorporated the changes suggested by Ryan

        Show
        Noble Paul added a comment - Incorporated the changes suggested by Ryan
        Hide
        Noble Paul added a comment -

        Leaving out the function is fine. Let the users manage that static instance
        Let us not have automatic conversion as of now. If people ask for it we can add it later.

        Show
        Noble Paul added a comment - Leaving out the function is fine. Let the users manage that static instance Let us not have automatic conversion as of now. If people ask for it we can add it later.
        Hide
        Ryan McKinley added a comment -

        looks good – i would leave out the function in QueryResponse.java – it seems just as easy for whoever is calling this to keep the static DocumentObjectBinder (and keeps the static out of our maintenance)

        Should it do automatic type conversion.?
        i.e solr gives the field as say int and the bean has the field as String

        Sounds good.

        Show
        Ryan McKinley added a comment - looks good – i would leave out the function in QueryResponse.java – it seems just as easy for whoever is calling this to keep the static DocumentObjectBinder (and keeps the static out of our maintenance) Should it do automatic type conversion.? i.e solr gives the field as say int and the bean has the field as String Sounds good.
        Hide
        Noble Paul added a comment -

        First cut .
        There is a Class DocumentObjectBinder used by QueryResponse.

        The testcase demonstrates the usage.
        The annotation can be applied on a setter method also.

        Questions:
        Do we need binding for dynamic fields like

        @Field("*_t")
        String a_t:

        Should it do automatic type conversion.?
        i.e solr gives the field as say int and the bean has the field as String

        Show
        Noble Paul added a comment - First cut . There is a Class DocumentObjectBinder used by QueryResponse. The testcase demonstrates the usage. The annotation can be applied on a setter method also. Questions: Do we need binding for dynamic fields like @Field("*_t") String a_t: Should it do automatic type conversion.? i.e solr gives the field as say int and the bean has the field as String

          People

          • Assignee:
            Ryan McKinley
            Reporter:
            Noble Paul
          • Votes:
            3 Vote for this issue
            Watchers:
            1 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development