7.2. Retrieving the Results

After building the Hibernate Guide, it can be executed in the same way as a HQL or Criteria query. The same paradigm and object semantic apply to Lucene Query query and all the common operations like: list(), uniqueResult(), iterate(), scroll() are available.

7.2.1. Performance Considerations

If you expect a reasonable number of results (for example using pagination) and expect to work on all of them, list() or uniqueResult() are recommended. list() work best if the entity batch-size is set up properly. Note that Hibernate Search has to process all Lucene Hits elements (within the pagination) when using list() , uniqueResult() and iterate().
If you wish to minimize Lucene document loading, scroll() is more appropriate. Don't forget to close the ScrollableResults object when you're done, since it keeps Lucene resources. If you expect to use scroll, but wish to load objects in batch, you can use query.setFetchSize(). When an object is accessed, and if not already loaded, Hibernate Search will load the next fetchSize objects in one pass.

Important

Pagination is preferred over scrolling.

7.2.2. Result Size

It is sometimes useful to know the total number of matching documents:
  • for the Google-like feature "1-10 of about 888,000,000"
  • to implement a fast pagination navigation
  • to implement a multi step search engine (adding approximation if the restricted query return no or not enough results)
  • To provide a total search results feature, as provided by Google searches. For example, "1-10 of about 888,000,000 results".
  • To implement fast pagination navigation.
  • to implement a multi-step search engine that adds approximation if the restricted query returns zero or not enough results.
Of course it would be too costly to retrieve all the matching documents. Hibernate Search allows you to retrieve the total number of matching documents regardless of the pagination parameters. Even more interesting, you can retrieve the number of matching elements without triggering a single object load.

Example 7.13. Determining the Result Size of a Query

org.hibernate.search.FullTextQuery query = 
    s.createFullTextQuery( luceneQuery, Book.class );
//return the number of matching books without loading a single one
assert 3245 == query.getResultSize(); 

org.hibernate.search.FullTextQuery query = 
    s.createFullTextQuery( luceneQuery, Book.class );
query.setMaxResult(10);
List results = query.list();
//return the total number of matching books regardless of pagination
assert 3245 == query.getResultSize();

Note

Like Google, the number of results is approximation if the index is not fully up-to-date with the database (asynchronous cluster for example).

7.2.3. ResultTransformer

As seen in Section 7.1.10.5, “Projection” projection results are returns as Object arrays. This data structure is not always matching the application needs. In this cases It is possible to apply a ResultTransformer which post query execution can build the needed data structure:

Example 7.14. Using ResultTransformer with Projections

org.hibernate.search.FullTextQuery query = 
    s.createFullTextQuery( luceneQuery, Book.class );
query.setProjection( "title", "mainAuthor.name" );

query.setResultTransformer( new StaticAliasToBeanResultTransformer( BookView.class, "title", "author" ) );
List<BookView> results = (List<BookView>) query.list();
for(BookView view : results) {
    log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() );
}
Examples of ResultTransformer implementations can be found in the Hibernate Core codebase.

7.2.4. Understanding Results

You will find yourself sometimes puzzled by a result showing up in a query or a result not showing up in a query. Luke is a great tool to understand those mysteries. However, Hibernate Search also gives you access to the Lucene Explanation object for a given result (in a given query). This class is considered fairly advanced to Lucene users but can provide a good understanding of the scoring of an object. You have two ways to access the Explanation object for a given result:
  • Use the fullTextQuery.explain(int) method
  • Use projection
The first approach takes a document id as a parameter and return the Explanation object. The document id can be retrieved using projection and the FullTextQuery.DOCUMENT_ID constant.

Warning

The Document id has nothing to do with the entity id. Do not mess up these two notions.
In the second approach you project the Explanation object using the FullTextQuery.EXPLANATION constant.

Example 7.15. Retrieving the Lucene Explanation Object Using Projection

FullTextQuery ftQuery = s.createFullTextQuery( luceneQuery, Dvd.class )
        .setProjection( 
             FullTextQuery.DOCUMENT_ID, 
             FullTextQuery.EXPLANATION, 
             FullTextQuery.THIS );
@SuppressWarnings("unchecked") List<Object[]> results = ftQuery.list();
for (Object[] result : results) {
    Explanation e = (Explanation) result[1];
    display( e.toString() );
}
Be careful, building the explanation object is quite expensive, it is roughly as expensive as running the Lucene query again. It is recommended that you do not undertake this if another object is unnecessary.