Once the Hibernate Search query is built, executing it is in no way
different than executing a HQL or Criteria query. The same paradigm and
object semantic applies. All the common operations are available:
list()
, uniqueResult()
,
iterate()
,
scroll()
.
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.
Pagination is a preferred method over scrolling though.
It is sometime 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)
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 5.11. Determining the result size of a query
org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class ); assert 3245 == query.getResultSize(); //return the number of matching books without loading a single one org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class ); query.setMaxResult(10); List results = query.list(); assert 3245 == query.getResultSize(); //return the total number of matching books regardless of pagination
Like Google, the number of results is approximative if the index is not fully up-to-date with the database (asynchronous cluster for example).
Especially when using projection, the data structure returned by a
query (an object array in this case), is not always matching the
application needs. It is possible to apply a
ResultTransformer
operation post query to match
the targeted data structure:
Example 5.12. Using ResultTransformer in conjunction 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.
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.
The Document id has nothing to do with the entity id. Do not mess up these two notions.
The second approach let's you project the
Explanation
object using the
FullTextQuery.EXPLANATION
constant.
Example 5.13. 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. Don't do it if you don't need the object