-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: HSEARCH-566: mapping of ElementCollection in 3.3RC1?
PostPosted: Fri Nov 19, 2010 1:12 am 
Beginner
Beginner

Joined: Wed Sep 30, 2009 5:29 am
Posts: 29
This question is re: HSEARCH-566: Support mapping of @javax.persistence.ElementCollection
http://opensource.atlassian.com/project ... SEARCH-566

The issue is marked as targetted for 3.3, but is still in status Unresolved and the comments sound like the feature is not in for 3.3..

I've looked at the pdf docs for 3.3RC1 but see no mention of ElementCollection. Before I spend time building a test project and experimenting, can I ask if this feature is supported?

I guess it would mean we could define a collection in java code, and have each member of the collection appear in the index, i.e.:

Code:
   @ElementCollection(fetch=FetchType.LAZY)
   private Map<String, String> attributes;


Also, will we be able to map a collection of diverse types like:

Code:
   @ElementCollection(fetch=FetchType.LAZY)
   private Map<String, Object> attributes;


where each value of attributes is a different type? (Integer, Date, String etc.) or is the best way to model attributes of diverse types to express them as distinct fields with their own @Field annotation and mapping (bridge etc.)?

Some background to my question: I have a basic Item class which is mapped to a lucene index using HSearch 3.2. A customer wants to be able to store a custom type of item (motorbikes), which will need custom fields being mapped and searchable (cc, year, registration number). A perfect solution would be to be able to store a Map<String, Object> of e.g. "cc" => Integer(1000), "year"=>Integer(2002), "regno"=>String("ABC123") .. but I imagine this will be impossible to map to a Lucene index via a simple @ElementCollection annotation, right?

I guess the 'best' way handle these fields is to derive a class from my Item class and add these fields as a specific attributes annotated with @Field.

A test has shown me that simply deriving a Bike class from Item, and adding these fields, makes the new fields show up in the database 'item' table, and on the Item lucene index, so this solution would seem to work, but I'd rather not express the fields as distinct attributes if I can avoid it, because my data model becomes hardcoded in my java source, rather than being left as a very flexible Map<String, Object>.

Comments on any of these points would be welcome :)
Thanks
Nick


Top
 Profile  
 
 Post subject: Re: HSEARCH-566: mapping of ElementCollection in 3.3RC1?
PostPosted: Fri Nov 19, 2010 4:30 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
The short answer is HSEARCH-566 is not yet implemented.
I am also not so sure how much your usecase has to do with @ElementCollection. Just because you are using an element collection does not mean we can automatically know how you want to index your map. That said, you can always write your own field bridge in which you index your Map<String, Object> any way you like. Have you considered this approach?

--Hardy


Top
 Profile  
 
 Post subject: Re: HSEARCH-566: mapping of ElementCollection in 3.3RC1?
PostPosted: Fri Nov 19, 2010 6:25 am 
Beginner
Beginner

Joined: Wed Sep 30, 2009 5:29 am
Posts: 29
hardy.ferentschik wrote:
The short answer is HSEARCH-566 is not yet implemented.
I am also not so sure how much your usecase has to do with @ElementCollection. Just because you are using an element collection does not mean we can automatically know how you want to index your map.

Thank you for your comments.

In my naivety, I presume that introspection of the contents of the Map at the time of serialisation to the index would give enough info for basic indexing. I imagine this is the direction that HSEARCH-566 is heading in, though perhaps it's strictly type-safe and actually the types of the object bridged to the index are fixed at compile time.

hardy.ferentschik wrote:
That said, you can always write your own field bridge in which you index your Map<String, Object> any way you like. Have you considered this approach?


I had not thought of that. I've knocked together a FieldBridge implementation that looks like it should do the job.

I annotate my Map like this:

Code:
   @ElementCollection(fetch=FetchType.LAZY)
   @Field
   @FieldBridge(impl=MapFieldBridge.class)
   private Map<String, String> attributes;


And the MapFieldBridge.java looks like:

Code:

import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.document.Document;
import org.hibernate.search.bridge.FieldBridge;
import org.hibernate.search.bridge.LuceneOptions;

/**
*
* @author neek
*/
public class MapFieldBridge implements FieldBridge {
   private final static Logger logger = Logger.getLogger(MapFieldBridge.class.getName());

   // bridges used for attributes of certain types
   static private final SortableDoubleBridge sdb = new SortableDoubleBridge();

   @Override
   public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
      if (value instanceof Map) {
         Map<String, Object> map = (Map<String, Object>)value;

         for (Map.Entry<String, Object> entry : map.entrySet()) {
            logger.log(Level.FINEST, "  mapping field ({0}) with value ({1})",
                  new Object[]{entry.getKey(), entry.getValue()});

            Object v = entry.getValue();
            if (v instanceof Integer || v instanceof Long || v instanceof String) {
               // trust the built in string conversion
               luceneOptions.addFieldToDocument(entry.getKey(),
                     v.toString(), document);
            } else if (v instanceof Double) {
               // use SortableDoubleBridge
               luceneOptions.addFieldToDocument(entry.getKey(),
                     sdb.objectToString(v), document);
            } else {
               // what to do here? (if anything)
               logger.log(Level.WARNING, "Unhandled field type ({0}) for field ({1})",
                     new Object[]{v.getClass().getName(), entry.getKey()});
               luceneOptions.addFieldToDocument(entry.getKey(), v.toString(), document);
            }

         }
      }
   }

}


This seems to be populating the index correctly. I'll go see if it plays well with my search and rehydration code. I have a feeling I need a TwoWayFieldBridge, not just a FieldBridge, in which case, how would it know what object type to turn each String from the lucene index back into? Perhaps I would have to store a metainfo field per Map field for this purpose.


Top
 Profile  
 
 Post subject: Re: HSEARCH-566: mapping of ElementCollection in 3.3RC1?
PostPosted: Fri Nov 19, 2010 6:29 am 
Hibernate Team
Hibernate Team

Joined: Thu Apr 05, 2007 5:52 am
Posts: 1689
Location: Sweden
Quote:
This seems to be populating the index correctly. I'll go see if it plays well with my search and rehydration code. I have a feeling I need a TwoWayFieldBridge, not just a FieldBridge, in which case, how would it know what object type to turn each String from the lucene index back into? Perhaps I would have to store a metainfo field per Map field for this purpose.


Sure, you can add whatever information you need into the index.


Top
 Profile  
 
 Post subject: Re: HSEARCH-566: mapping of ElementCollection in 3.3RC1?
PostPosted: Tue Nov 23, 2010 12:14 am 
Beginner
Beginner

Joined: Wed Sep 30, 2009 5:29 am
Posts: 29
I mistakenly defined my Java attribute as Map<String, String> earlier, which led me to believe my code was working. What I actually needed was:

Code:
   @ElementCollection(fetch=FetchType.LAZY)
   @Field
   @FieldBridge(impl=ltd.yavin.liveauction.db.MapFieldBridge.class)
   private Map<String, Object> attributes;


The use of "String, Object" as Map template arguments is giving me:

Code:
Nov 23, 2010 10:52:42 AM ltd.yavin.liveauction.db.HibernateUtil init
SEVERE: org.hibernate.MappingException in static init! (collection element mapping has wrong number of columns: test.searchindextest.MainEntity.attributes type: object)
org.hibernate.MappingException: collection element mapping has wrong number of columns: test.searchindextest.MainEntity.attributes type: object
        at org.hibernate.mapping.Collection.validate(Collection.java:302)
        at org.hibernate.mapping.IndexedCollection.validate(IndexedCollection.java:90)
        at org.hibernate.cfg.Configuration.validate(Configuration.java:1197)
        at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1378)
        at org.hibernate.cfg.AnnotationConfiguration.buildSessionFactory(AnnotationConfiguration.java:954)


I think this job is beyond me, I'm just going to have to keep it simple and derive a class for my custom attribute, index them using simple built in bridges, and write a bunch of special handling code for my custom entity objects. (and then possibly have to unpick all this work if HSEARCH-566 means I can do all this in the way I first envisaged :))


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.