Help

One of the innovations we have brought to Hibernate Search is an alternative way to define the mapping information: a programmatic API.

The traditional way to map an entity into Hibernate Search is to use annotations. And it's perfectly fine for 95% of the use cases. In some cases though, some people had had a need for a more dynamic approach:

  • they use a metamodel to generate or customize what is indexed in their entities and need to reconfigure things either on redeployment or on the fly based on some contextual information.
  • they ship a product to multiple customers that require some customization.

What people asked for: the XML Way(tm)

For a while, people with this requirement have asked for an XML format equivalent to what annotations could do. Now the problem with XML is that:

  • it's very verbose in it's way to duplicate the structural information of your code
<class name="Address">
  <property name="street1">
    <field>
      <analyzer definition="ngram"/>
    </field>
   </property>
   <!-- ... -->
</class>
  • while XML itself is type-safe, XML editors are still close to stone age, and developers writing XML in notepad are unfortunately quite common
  • even if XML is type-safe, one cannot refactor the Java code and expect to get compile time errors or even better automatic integrated refactoring. For example, if I rename Address to Location, I still need to remember to change this in my xml file
  • and finally, dynamically generating an XML stream to cope with the dynamic reconfiguration use case is not what I would call an intuitive solution

So we took a different road.

What they get: a fluent programmatic API

Instead of writing the mapping in XML, let's write it in Java. And to make things easier let's use a fluent contextual API (have intuitive method names, only expose the relevant operations).

SearchMapping mapping = new SearchMapping();

mapping
    .analyzerDef( "ngram", StandardTokenizerFactory.class )
        .filter( LowerCaseFilterFactory.class )
        .filter( NGramFilterFactory.class )
            .param( "minGramSize", "3" )
            .param( "maxGramSize", "3" )

    .entity(Address.class)
        .indexed()
        .property("addressId", METHOD)
            .documentId()
        .property("street1", METHOD)
            .field()
            .field()
                .name("street1_ngram")
                .analyzer("ngram")
        .property("country", METHOD)
            .indexedEmbedded()
        .property("movedIn", METHOD)
            .dateBridge(Resolution.DAY);

As you can see, it's very easy to figure out what is going on here. But something you cannot see in this example is that your IDE only offers the relevant methods contextually. For example, unless you have just declared a property(), you won't be able to add a field() to it. Likewise, you can set an analyzer on a field, only if you are defining a field. It's like the dynamic languages fluent APIs be better ;)

The next step is to associate the programmatic mapping object to the Hibernate configuration.

//in Hibernate native
Configuration configuration = ...;
configuration.setProperty( "hibernate.search.mapping_model", mapping );
SessionFactory factory = configuration.buildSessionFactory();
//in JPA
Map<String,String> properties = new HashMap<String,String)(1);
properties.put( "hibernate.search.mapping_model", mapping );
EntityManagerFactory emf = Persistence.createEntityManagerFactory( "userPU", properties );

And voila!

Extensibility

The beauty of this API is that it's very easy for XML fan boys to create their own XML schema descriptors and use the programmatic API when parsing the XML stream. More interestingly, an application can expose specific configuration options (via a simple configuration file, a UI or any other form) and use this configuration to customize the mapping programmatically.

Please give this API a try, tell us what works and what does not, we are still figuring out things to make it as awesome as possible :)

Download

Many thanks to Amin Mohammed-Coleman for taking my half done initiative and polishing it up.

You can get Hibernate Search 3.2 Beta 1 here, the complete API documentation is present in the distribution; chapter 4.4.

14 comments:
 
02. Dec 2009, 21:10 CET | Link
Sanne

Nice post! Is the METHOD parameter going to be mandatory?

 
02. Dec 2009, 21:15 CET | Link

Christian rightly pointed out to me that while the property name is string based and thus not type-safe refactoring wise, you can use the JPA metamodel to bring back type safety with a little additional verbosity

mapping
    .analyzerDef( "ngram", StandardTokenizerFactory.class )
        .filter( LowerCaseFilterFactory.class )
        .filter( NGramFilterFactory.class )
            .param( "minGramSize", "3" )
            .param( "maxGramSize", "3" )

    .entity(Address.class)
        .indexed()
        .property(Address_.addressId.getName(), METHOD)
            .documentId()
        .property(Address_.street1.getName(), METHOD)
            .field()
            .field()
                .name("street1_ngram")
                .analyzer("ngram")
        .property(Address_.country.getName(), METHOD)
            .indexedEmbedded()
        .property(Address_.movedIn.getName(), METHOD)
            .dateBridge(Resolution.DAY);

We might even support JPA 2 Attribute natively and benefit from the type, For example a dateBridge should fail at compile time if the property is not of type Date. It's probably a bigger task though :)

 
02. Dec 2009, 21:16 CET | Link

you tell me. What's the alternative you see to the explicit ElementType attribute?

 
02. Dec 2009, 21:36 CET | Link
musketyr

looks so Groovy ;)

 
02. Dec 2009, 21:39 CET | Link
waddle

You give three problems with XML but :

1- XML editors are good. If we want a special editor for a framework, we can have it (a good example is Spring Tools). I think the tooling should give us what's missing in XML completion 2- refactoring is available for XML and without tooling, Eclipse does it 3- Ok for the third problem but do we want dynamic reconfiguration. I mean, does the underlying system can be so dynamic ?

More globaly, I think programmtic configuration remains not readable at all and doesn't seem to be fastly written. Of course, it's the only intuitive way to make thing more dynamic but again, when do we need such dynamic behaviour ? Can you give me some examples please ?

regards,

 
02. Dec 2009, 22:40 CET | Link
William Draï

Excellent. Would be nice to have the same configuration API for Hibernate Validator.

 
02. Dec 2009, 23:23 CET | Link
Excellent. Would be nice to have the same configuration API for Hibernate Validator.

Good idea. I've had that in mind esp since someone from the Nokia labs presented something along those lines at Devoxx I've opened the JIRA issue in the mean time :)

 
02. Dec 2009, 23:40 CET | Link
1- XML editors are good. If we want a special editor for a framework, we can have it (a good example is Spring Tools). I think the tooling should give us what's missing in XML completion

That's true, for example JBoss Tools has done a fantastic job at doing refactoring between your objects and your JSF pages. But it's an enormous amount of work... just for Eclipse. If every Java framework on earth had to write a plugin for the three major IDEs, there would be no framework (no time :) ). why not let the language compiler do the work for free! I can tell you that the JBoss Tools team is pretty excited about the type-safety that EE 6 is bringing to the table as it siimplify their life a great deal.

2- refactoring is available for XML and without tooling, Eclipse does it

I don't follow you, in the prevuous sentence you said you needed additional plugins to bridge java and the xml land.

3- Ok for the third problem but do we want dynamic reconfiguration. I mean, does the underlying system can be so dynamic ?

Yes, by restarting the app or restarting the Hibernate factory, the configuration is updated. Of course we could go further and do hot-reconfiguration but that's probably not that useful.

More globaly, I think programmtic configuration remains not readable at all and doesn't seem to be fastly written. Of course, it's the only intuitive way to make thing more dynamic but again, when do we need such dynamic behaviour ? Can you give me some examples please ?

That's your opinion, I find XML mappings far less readable than the programmatic approach (when done correctly) and it does not force me to do context switching between XML and Java or whatever your language may be.

 
03. Dec 2009, 00:46 CET | Link
waddle
Emmanuel Bernard wrote on Dec 02, 2009 17:40:
1- XML editors are good. If we want a special editor for a framework, we can have it (a good example is Spring Tools). I think the tooling should give us what's missing in XML completion
That's true, for example JBoss Tools has done a fantastic job at doing refactoring between your objects and your JSF pages. But it's an enormous amount of work... just for Eclipse. If every Java framework on earth had to write a plugin for the three major IDEs, there would be no framework (no time :) ). why not let the language compiler do the work for free! I can tell you that the JBoss Tools team is pretty excited about the type-safety that EE 6 is bringing to the table as it siimplify their life a great deal.

I don't really think it's fone for free as we must learn an other API and get a bunch of technical code which means less readability (it's my personnal opinion as you said :-) )

2- refactoring is available for XML and without tooling, Eclipse does it
I don't follow you, in the prevuous sentence you said you needed additional plugins to bridge java and the xml land.

I was only talking about completion in the first point. Refactoring is another aspect, which is address directly by Eclipse in my example, without any plugin help.

3- Ok for the third problem but do we want dynamic reconfiguration. I mean, does the underlying system can be so dynamic ?
Yes, by restarting the app or restarting the Hibernate factory, the configuration is updated. Of course we could go further and do hot-reconfiguration but that's probably not that useful.

doesn't it ? I think it may be regarding the start time of modern apps. I prefer JRebel approach on this for development, and still don't see when it can be usefull on production.

More globaly, I think programmtic configuration remains not readable at all and doesn't seem to be fastly written. Of course, it's the only intuitive way to make thing more dynamic but again, when do we need such dynamic behaviour ? Can you give me some examples please ?
That's your opinion, I find XML mappings far less readable than the programmatic approach (when done correctly) and it does not force me to do context switching between XML and Java or whatever your language may be.

Of course, it's just ma opinion. But here's some reasons. As the example you take becomes more complicated, the more readable XML seems to me (regions in XML file can also help and we can't declare regions in Java code as in Csharp). And to be readable, the code has be left as it is by the code formatter, knowing that indentation rules are cannot be partially applied. Regarding to context switching...hum it's a tab navigation, or did I miss your point ?

I think that the crusade against XML with APIs, Annotations, Aspects, etc. is becoming an obsession and only often brings readability, maintenability and better productivity.

 
03. Dec 2009, 06:21 CET | Link
Flavio Costa

Nice work but I have a little doubt.

Using JEE we don't actually handle the EntityManagerFactory (the container does that for me by bootstrapping the Persistence Unit and then I only inject the EntityManager, either using @PersistenceContext or @In, if I'm using Seam).

How am I supposed to configure Hiberante Search in this kind of enviroment.

 
03. Dec 2009, 15:42 CET | Link
Flavio Costa wrote on Dec 03, 2009 00:21:
Nice work but I have a little doubt. Using JEE we don't actually handle the EntityManagerFactory (the container does that for me by bootstrapping the Persistence Unit and then I only inject the EntityManager, either using @PersistenceContext or @In, if I'm using Seam). How am I supposed to configure Hiberante Search in this kind of enviroment.

Yes good point. It' something I did not have a good idea until yesterday. But I think we will do something like this for the next version or the one after.

 
03. Dec 2009, 21:22 CET | Link
Flavio Costa

Seems to be the way to good Emmanuel. Nice work.

 
03. Dec 2009, 21:22 CET | Link
Flavio Costa

Errm.

Seems to be the way to GO.

 
05. Mar 2010, 18:00 CET | Link

quick warning: the property was renamed to

hibernate.search.model_mapping

Post Comment