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.