09/09: Hibernate Search, Lucene and JBoss Seam
Category: JBoss Seam | Posted by: JohnHaselden
In this article, I will describe the steps required to configure Hibernate Search with JBoss Seam.
First thing we need to do is add the Hibernate Search settings to the persistence.xml file as follows.
This sets up the directory provider class, the location of the indexes and the listeners for handling the update to the indexes on insert, update and delete operations.
Next, we need to add in the hibernate-search.jar, hibernate-commons-annotations.jar and lucene-core.jar files into the lib directory in your ear file.
OK, now we need to tell Hibernate Search which objects to index and which attributes we are going to be interested in.
What we have here is an @Indexed annotation which tells Hibernate Search that this persistent class is indexable. From there we have a @DocumentId which indicates that this attribute is the ID of the class and is to be stored and not indexed. From there on we have two attributes name and description. Both of these attributes are annotated with @Field and in this example both are tokenized and this indicates to Hibernate Search to allow the analyzer to process the property. Other allowed values here are Index.NO (don't analyze), Index.UN_TOKENIZED (no pre-processing from the analyzer), Index.NO_NORM (don't store the normalization data).
Now we have everything indexed in Lucene, we need a way of searching it. For this we will have a SearchManager class.
OK, now let's have a search.xhtml file to display the results. Below is a snippet from this file
Then add an entry into pages.xml for search.xhtml
One more step. Let's add a search box to the menu. If you have followed the standard layout for a Seam project, then you will have a menu.xhtml file within the layout of your view. If not, then just place the following in a logical place in your application.
And from here, you should have a working search of your mapped entities. Which is great. But what happens if you have an attribute on this entity that dictates the visibility of the entity in the web site? Well in this instance, you need to use a Filter. These filters extend the org.apache.lucene.search.Filter class.
Imagine we have a status attribute on the Product class with three values; "L" for live, "D" for deleted and "P" for pending. Obviously in our customer facing search, we only want the live products to be returned. So our filter will look like:
In order to indicate to Hibernate Search that we wish to apply the filter to the Product class, we add the following annotation to the class.
And that is it.
For further information, check out the Hibernate Search documentation.
First thing we need to do is add the Hibernate Search settings to the persistence.xml file as follows.
<!-- use a file system based index -->
<property name="hibernate.search.default.directory_provider"
value="org.hibernate.search.store.FSDirectoryProvider" />
<!-- directory where the indexes will be stored -->
<property name="hibernate.search.default.indexBase"
value="[/path/to/your/location/directory]" />
<property name="hibernate.ejb.event.post-insert"
value="org.hibernate.search.event.FullTextIndexEventListener" />
<property name="hibernate.ejb.event.post-update"
value="org.hibernate.search.event.FullTextIndexEventListener" />
<property name="hibernate.ejb.event.post-delete"
value="org.hibernate.search.event.FullTextIndexEventListener" />
This sets up the directory provider class, the location of the indexes and the listeners for handling the update to the indexes on insert, update and delete operations.
Next, we need to add in the hibernate-search.jar, hibernate-commons-annotations.jar and lucene-core.jar files into the lib directory in your ear file.
OK, now we need to tell Hibernate Search which objects to index and which attributes we are going to be interested in.
@Entity
@Name("product")
@Indexed
public class Product implements Serializable {
static final long serialVersionUID = 1l;
@Id @GeneratedValue @DocumentId
private Long id;
@NotNull
@Field(index = Index.TOKENIZED)
private String name;
@NotNull
@Field(index = Index.TOKENIZED)
private String description;
// getters and setters
}
What we have here is an @Indexed annotation which tells Hibernate Search that this persistent class is indexable. From there we have a @DocumentId which indicates that this attribute is the ID of the class and is to be stored and not indexed. From there on we have two attributes name and description. Both of these attributes are annotated with @Field and in this example both are tokenized and this indicates to Hibernate Search to allow the analyzer to process the property. Other allowed values here are Index.NO (don't analyze), Index.UN_TOKENIZED (no pre-processing from the analyzer), Index.NO_NORM (don't store the normalization data).
Now we have everything indexed in Lucene, we need a way of searching it. For this we will have a SearchManager class.
@Name("SearchManager")
public class SearchManager {
@In
private FullTextEntityManager entityManager;
private String searchPattern;
// getters and setters for searchPattern
public ListgetResults() {
MapboostFields = new HashMap (2);
// increase the importance of the name field
// over the other product fields
boostFields.put("name", 4f);
String[] productFields = {"name", "description"};
QueryParser parser = new MultiFieldQueryParser(productFields,
new StandardAnalyzer(), boostFields);
parser.setAllowLeadingWildcard(true);
Query luceneQuery;
try {
luceneQuery = parser.parse(searchPattern);
} catch (ParseException pe) {
log.error("found a problem in search", pe);
return null;
}
// extract the products
Listproducts =
entityManager.createFullTextQuery(luceneQuery, Product.class).
setMaxResults(20).getResultList();
return products;
}
}
OK, now let's have a search.xhtml file to display the results. Below is a snippet from this file
<rich:dataGrid value="#{SearchManager.results}" var="product">
[ loop over the values ]
</rich:dataGrid>
Then add an entry into pages.xml for search.xhtml
<page view-id="/search.xhtml">
<param name="searchPattern"
value="#{SearchManager.searchPattern}"/>
</page>
One more step. Let's add a search box to the menu. If you have followed the standard layout for a Seam project, then you will have a menu.xhtml file within the layout of your view. If not, then just place the following in a logical place in your application.
<h:form id="search_form">
<h:inputText id="searchPattern" required="true"
value="#{SearchManager.searchPattern}" />
<h:commandButton action="/search.xhtml"
value="search"></h:commandButton>
</h:form>
And from here, you should have a working search of your mapped entities. Which is great. But what happens if you have an attribute on this entity that dictates the visibility of the entity in the web site? Well in this instance, you need to use a Filter. These filters extend the org.apache.lucene.search.Filter class.
Imagine we have a status attribute on the Product class with three values; "L" for live, "D" for deleted and "P" for pending. Obviously in our customer facing search, we only want the live products to be returned. So our filter will look like:
public class LiveProductFilter extends Filter {
private static final long serialVersionUID = 1l;
public BitSet bits(IndexReader reader) throws IOException {
BitSet bitSet = new BitSet( reader.maxDoc() );
TermDocs termDocs = reader.termDocs( new Term("status", "L") );
while ( termDocs.next() ) {
bitSet.set( termDocs.doc() );
}
return bitSet;
}
}
In order to indicate to Hibernate Search that we wish to apply the filter to the Product class, we add the following annotation to the class.
@FullTextFilterDefs ( {
@FullTextFilterDef(name="liveProduct",
impl = LiveProductFilter.class, cache=false)
})
And that is it.
For further information, check out the Hibernate Search documentation.
Comments
JohnHaselden wrote:
Hi,
Usually I check the style of the article after I've finished it however with this one I finished it at 1am and decided that it was time to sleep!
Hopefully it looks a lot better now.
Usually I check the style of the article after I've finished it however with this one I finished it at 1am and decided that it was time to sleep!
Hopefully it looks a lot better now.
10/09 08:37:04
Jonathan wrote:
What is the justification of using a Hibernate (Lucene) search over normal JPA query?
14/09 21:03:25
JohnHaselden wrote:
For me the justification would be the ability to offer the whole set of logic operations to the user from a search box. In the above example, you could choose to search by name field only for a specific key word (e.g. name:seam), combine boolean parameters and basically offer a really powerful search function without having to put in too much effort. There may also be a performance benefit for using Lucene for full-text searches, however this depends on your data set and your database.
With any other CRUD operations and operations where you, within your code, completely have control over your search parameters and can express these simply with SQL or HQL then your JPA/Hibernate query is preferable.
With any other CRUD operations and operations where you, within your code, completely have control over your search parameters and can express these simply with SQL or HQL then your JPA/Hibernate query is preferable.
14/09 22:07:01
Sheng Gu wrote:
Hi there,
Thanks for the excellent example which I am looking for. This is my first time to try to integrate Hibernate search into Seam. My question is whether I can put @Field(index = Index.TOKENIZED) annotation before getter/setter methods, since I put all JPA annotations before getter/setter methods, not before fields. Thanks.
Thanks for the excellent example which I am looking for. This is my first time to try to integrate Hibernate search into Seam. My question is whether I can put @Field(index = Index.TOKENIZED) annotation before getter/setter methods, since I put all JPA annotations before getter/setter methods, not before fields. Thanks.
15/09 16:11:15
JohnHaselden wrote:
You can indeed use the annotations on the getters. The Hibernate Search documentation has an example of such an annotation in section 4.1.1 - basic mapping (http://www.hibernate.org/hi...)
15/09 18:37:30
Hasan wrote:
Nice example. Can u give me an example which returns multiple entities ?
17/09 09:34:26
JohnHaselden wrote:
I must admit that I haven't actually tried this to verify that it works, but according to the Hibernate Search API documentation (http://www.hibernate.org/hi...), you can pass through mulitple entity Class definitions as parameters to the createFullTextQuery method.
So if you had a book store site selling both books and magazines for example, you would potentially create the query with:
entityManager.createFullTextQuery(luceneQuery, Book.class, Magazine.class);
Please let me know if this works for you!
So if you had a book store site selling both books and magazines for example, you would potentially create the query with:
entityManager.createFullTextQuery(luceneQuery, Book.class, Magazine.class);
Please let me know if this works for you!
17/09 10:02:27
Sanne Grinovero wrote:
yes the last example is correct, the results are polymorphic and you will get subclasses too for listed Types, of all entities compatible with the Query.
besides performance and query flexibility, don't forget you can search for Similarity.. it's fulltext!
Also when searching for several terms it is important that the default order of the result is sorted by relevance.
besides performance and query flexibility, don't forget you can search for Similarity.. it's fulltext!
Also when searching for several terms it is important that the default order of the result is sorted by relevance.
28/09 16:11:44
jose wrote:
Hi,
I tried the example but I have a nullpointerexception on entityManager.createFullTextQuery(luceneQuery, Product.class).setMaxResults(20).getResultList();
when I call the search method.
entityManager is null, but I can't see, where you instanciate entityManager, so how does it work ?
I am new with hibernate, but I don't use seam.
Thanks by advance,
I tried the example but I have a nullpointerexception on entityManager.createFullTextQuery(luceneQuery, Product.class).setMaxResults(20).getResultList();
when I call the search method.
entityManager is null, but I can't see, where you instanciate entityManager, so how does it work ?
I am new with hibernate, but I don't use seam.
Thanks by advance,
23/04 16:33:28
JohnHaselden wrote:
Hi Jose,
With Hibernate alone I have no idea. The entityManager in here is a Seam-specific component that is instantiated by Seam during initialisation. I would recommend reading through the following article which is specific to Hibernate - http://www.javaworld.com/ja...
With Hibernate alone I have no idea. The entityManager in here is a Seam-specific component that is instantiated by Seam during initialisation. I would recommend reading through the following article which is specific to Hibernate - http://www.javaworld.com/ja...
23/04 16:39:28
Jignesh wrote:
JOhn,
I am using tomcat and I don't have persistence.xml file, so where to store details of persistence.xml file
I am using tomcat and I don't have persistence.xml file, so where to store details of persistence.xml file
23/11 21:44:49
Jignesh wrote:
Do we need to use file based indexing or there are other ways to use indexing...
<property name="hibernate.search.default.directory_provider"
value="org.hibernate.search.store.FSDirectoryProvider" />
<!-- directory where the indexes will be stored -->
<property name="hibernate.search.default.indexBase"
value="[/path/to/your/location/directory]" />
<property name="hibernate.search.default.directory_provider"
value="org.hibernate.search.store.FSDirectoryProvider" />
<!-- directory where the indexes will be stored -->
<property name="hibernate.search.default.indexBase"
value="[/path/to/your/location/directory]" />
23/11 22:11:10
JohnHaselden wrote:
You would have to consult the documentation as, not using Tomcat, I would not be able to help in this matter.
26/11 19:41:40
Brendan Richards wrote:
jose:
the "entityManager" is your entry point for accessing hibernate via the Java Persistence APIs. You also have the option of using the Hibernate Session instead.
This link shows both approaches:
http://docs.jboss.org/hiber...
Jignesh:
persistence.xml is specific to the JPA api. You could alternatively configure via hibernate.cfg.xml
the "entityManager" is your entry point for accessing hibernate via the Java Persistence APIs. You also have the option of using the Hibernate Session instead.
This link shows both approaches:
http://docs.jboss.org/hiber...
Jignesh:
persistence.xml is specific to the JPA api. You could alternatively configure via hibernate.cfg.xml
04/01 12:23:09
Oceareeralhed wrote:
http://publiclandauctions.c... classic car auctions florida
http://carauctionssussex.al... abc minneapolis car auction
http://publiclandauctions.c... northbay auto auction in fairfield
http://skipcocarauction.hos... hayward auto auction bay cities
http://skipcocarauction.hos... kentucky auto auction vehicles
http://illinoiscarauction.h... auto auction direct victor new york
http://paautoauction.webser... insurance salvage auto auction
http://policecarsauctioned.... used cars for sale in meansville ga
http://mncarauctions.exotic... columbus car auction obetz
http://skipcocarauction.hos... city police auction california
http://publiclandauctions.c... free public police auctions
http://rmclassiccarauction.... washington car auctions
http://mncarauctions.exotic... turners car auctions auckland nz
http://policecarsauctioned.... sunburst auto auction cars
http://paautoauction.webser... city of portland police auctions
http://carauctionssussex.al... abc minneapolis car auction
http://publiclandauctions.c... northbay auto auction in fairfield
http://skipcocarauction.hos... hayward auto auction bay cities
http://skipcocarauction.hos... kentucky auto auction vehicles
http://illinoiscarauction.h... auto auction direct victor new york
http://paautoauction.webser... insurance salvage auto auction
http://policecarsauctioned.... used cars for sale in meansville ga
http://mncarauctions.exotic... columbus car auction obetz
http://skipcocarauction.hos... city police auction california
http://publiclandauctions.c... free public police auctions
http://rmclassiccarauction.... washington car auctions
http://mncarauctions.exotic... turners car auctions auckland nz
http://policecarsauctioned.... sunburst auto auction cars
http://paautoauction.webser... city of portland police auctions
29/03 22:42:16



anjan bacchu wrote:
this page looks bad in firefox 3 on windows xp. Can you test your page on firefox 3 ? a lot of java guys are on firefox :-)
BR,
~A