//$Id: FullTextSearchAction.java 7961 2008-04-17 04:30:44Z norman.richards@jboss.com $ package com.jboss.dvd.seam; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.ejb.Remove; import javax.ejb.Stateful; import org.apache.lucene.search.Query; import org.hibernate.search.jpa.FullTextEntityManager; import org.hibernate.search.jpa.FullTextQuery; import org.hibernate.search.query.dsl.QueryBuilder; import org.jboss.seam.ScopeType; import org.jboss.seam.annotations.Begin; import org.jboss.seam.annotations.Destroy; import org.jboss.seam.annotations.End; import org.jboss.seam.annotations.In; import org.jboss.seam.annotations.Logger; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Out; import org.jboss.seam.annotations.datamodel.DataModel; import org.jboss.seam.log.Log; /** * Hibernate Search version of the store querying mechanism. * This version is updated to show the new Query capabilities * of Hibernate Search 3.3, which requires Hibernate 3.6 and * as such is the recommended (minimal) version to use for JBoss6. * This code resides in a separate src directory as it wouldn't * compile with older Hibernate Search versions. * * @author Emmanuel Bernard * @author Sanne Grinovero */ @Stateful @Name("search") public class FullTextSearchAction implements FullTextSearch, Serializable { static final long serialVersionUID = -6536629890251170098L; @In(create=true) ShoppingCart cart; /** * Note that when using Seam's injection the entityManager * can be assigned directly to a FullTextEntityManager. * Same trick applies to FullTextSession. */ @In FullTextEntityManager entityManager; @Logger Log log; //@RequestParameter Long id; int pageSize = 15; int currentPage = 0; boolean hasMore = false; int numberOfResults; String searchQuery; @DataModel List<Product> searchResults; //@DataModelSelection Product selectedProduct; @Out(required = false) Product dvd; @Out(scope=ScopeType.CONVERSATION, required=false) Map<Product, Boolean> searchSelections; public String getSearchQuery() { return searchQuery; } public void setSearchQuery(String searchQuery) { this.searchQuery = searchQuery; } public int getNumberOfResults() { return numberOfResults; } @Begin(join = true) public String doSearch() { currentPage = 0; updateResults(); return "browse"; } public void nextPage() { if (!isLastPage()) { currentPage++; updateResults(); } } public void prevPage() { if (!isFirstPage()) { currentPage--; updateResults(); } } @Begin(join = true) public void selectFromRequest() { if (id != null) { dvd = entityManager.find(Product.class, id); } else if (selectedProduct != null) { dvd = selectedProduct; } } public boolean isLastPage() { return ( searchResults != null ) && !hasMore; } public boolean isFirstPage() { return ( searchResults != null ) && ( currentPage == 0 ); } @SuppressWarnings("unchecked") private void updateResults() { javax.persistence.Query query = null; if (searchQuery == null || "".equals(searchQuery)) { query = entityManager.createQuery("from Product"); numberOfResults =query.getResultList().size(); } else { query = searchQuery(searchQuery); numberOfResults =( (FullTextQuery) query).getResultSize(); } List<Product> items = query .setMaxResults(pageSize + 1) .setFirstResult(pageSize * currentPage) .getResultList(); if (items.size() > pageSize) { searchResults = new ArrayList(items.subList(0, pageSize)); hasMore = true; } else { searchResults = items; hasMore = false; } searchSelections = new HashMap<Product, Boolean>(); } private FullTextQuery searchQuery(String textQuery) { QueryBuilder queryBuilder = entityManager.getSearchFactory() .buildQueryBuilder().forEntity(Product.class).get(); //Hibernate Search fulltext query example: //query to match exact terms occurrence, using custom boosts: Query queryToFindExactTerms = queryBuilder.keyword() .onFields("title").boostedTo(4f) .andField("description").boostedTo(2f) .andField("actors.name").boostedTo(2f) .andField("categories.name").boostedTo(0.5f) .matching(textQuery) .createQuery(); //Similar query, but using NGram matching instead of exact terms: Query queryToFindMathingNGrams = queryBuilder.keyword() .onFields("title:ngrams").boostedTo(2f) .andField("description:ngrams") .andField("actors.name:ngrams") .andField("categories.name:ngrams").boostedTo(0.2f) .matching(textQuery) .createQuery(); //Combine them for best results, note exact uses an higher boost: Query fullTextQuery = queryBuilder.bool() .should(queryToFindMathingNGrams) .should(queryToFindExactTerms) .createQuery(); log.info("Executing fulltext query {0}", fullTextQuery); return entityManager.createFullTextQuery(fullTextQuery, Product.class); } /** * Add the selected DVD to the cart */ public void addToCart() { cart.addProduct(dvd, 1); } /** * Add many items to cart */ public void addAllToCart() { for (Product item : searchResults) { Boolean selected = searchSelections.get(item); if (selected != null && selected) { searchSelections.put(item, false); cart.addProduct(item, 1); } } } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public Long getSelectedId() { return id; } public void setSelectedId(Long id) { this.id = id; } @End public void reset() { } @Destroy @Remove public void destroy() { } }