From hibernate-commits at lists.jboss.org Tue Dec 2 09:18:09 2008
Content-Type: multipart/mixed; boundary="===============8303571248220900466=="
MIME-Version: 1.0
From: hibernate-commits at lists.jboss.org
To: hibernate-commits at lists.jboss.org
Subject: [hibernate-commits] Hibernate SVN: r15635 -
search/trunk/doc/reference/en/modules.
Date: Tue, 02 Dec 2008 09:18:09 -0500
Message-ID:
--===============8303571248220900466==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Author: hardy.ferentschik
Date: 2008-12-02 09:18:09 -0500 (Tue, 02 Dec 2008)
New Revision: 15635
Modified:
search/trunk/doc/reference/en/modules/query.xml
Log:
HSEARCH-303
Review of the query chapter.
Modified: search/trunk/doc/reference/en/modules/query.xml
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- search/trunk/doc/reference/en/modules/query.xml 2008-12-02 14:05:20 UTC=
(rev 15634)
+++ search/trunk/doc/reference/en/modules/query.xml 2008-12-02 14:18:09 UTC=
(rev 15635)
@@ -31,86 +31,109 @@
=
The second most important capability of Hibernate Search is the
ability to execute a Lucene query and retrieve entities managed by an
- Hibernate session, providing the power of Lucene without living the
+ Hibernate session, providing the power of Lucene without leaving the
Hibernate paradigm, and giving another dimension to the Hibernate classic
- search mechanisms (HQL, Criteria query, native SQL query).
+ search mechanisms (HQL, Criteria query, native SQL query). Preparing and
+ executing a query consists of four simple steps:
=
- To access the Hibernate Search querying
- facilities, you have to use an Hibernate
- FullTextSession . A Search Session wraps a regular
- org.hibernate.Session to provide query and indexi=
ng
- capabilities.
+
+
+ Creating a FullTextSession
+
=
- Session session =3D sessionFactory.openSession();
+
+ Creating a Lucene query
+
+
+
+ Wrapping the Lucene query using a
+ org.hibernate.Query
+
+
+
+ Executing the search by calling for example
+ list() or
+ scroll()
+
+
+
+ To access the querying facilities, you have to use an
+ FullTextSession . This Search specfic session wra=
ps a
+ regular org.hibernate.Session to provide query and
+ indexing capabilities.
+
+
+ Creating a FullTextSession
+
+ Session session =3D sessionFactory.openSession();
...
FullTextSession fullTextSession =3D Search.getFullTextSession(session); =
+
=
- The search facility is built on native Lucene queries.
+ The actual search facility is built on native Lucene queries which=
the
+ following example illustrates.
=
- org.apache.lucene.queryParser.QueryParser parser =3D new=
QueryParser("title", new StopAnalyzer() );
+
+ Creating a Lucene query
=
+ org.apache.lucene.queryParser.QueryParser parser =3D =
+ new QueryParser("title", new StopAnalyzer() );
+
org.apache.lucene.search.Query luceneQuery =3D parser.parse( "summary:Fest=
ina Or brand:Seiko" );
org.hibernate.Query fullTextQuery =3D fullTextSess=
ion.createFullTextQuery( luceneQuery );
-
List result =3D fullTextQuery.list(); //return a list of managed objects =
+
=
The Hibernate query built on top of the Lucene query is a regular
- org.hibernate.Query , you are in the same paradigm as=
the
- other Hibernate query facilities (HQL, Native or Criteria). The regular
- list() , uniqueResult() ,
- iterate() and scroll() can be
+ org.hibernate.Query, which means you are in the same
+ paradigm as the other Hibernate query facilities (HQL, Native or Criteri=
a).
+ The regular list() , uniqueResult(),
+ iterate() and scroll() methods can=
be
used.
=
- For people using Java Persistence (aka EJB 3.0 Persistence) APIs of
- Hibernate, the same extensions exist:
+ In case you are using the Java Persistence APIs of Hibernate (aka =
EJB
+ 3.0 Persistence), the same extensions exist:
=
- EntityManager em =3D entityManagerFactory.createEntityMa=
nager();
+
+ Creating a Search query using the JPA API
=
+ EntityManager em =3D entityManagerFactory.createEntity=
Manager();
+
FullTextEntityManager fullTextEntityManager =3D =
org.hibernate.hibernate.search.jpa.Search.getFullTextEntityManager(em);
=
...
-org.apache.lucene.queryParser.QueryParser parser =3D new QueryParser("titl=
e", new StopAnalyzer() );
+org.apache.lucene.queryParser.QueryParser parser =3D =
+ new QueryParser("title", new StopAnalyzer() );
=
org.apache.lucene.search.Query luceneQuery =3D parser.parse( "summary:Fest=
ina Or brand:Seiko" );
javax.persistence.Query fullTextQuery =3D fullText=
EntityManager.createFullTextQuery( luceneQuery );
=
List result =3D fullTextQuery.getResultList(); //return a list of managed =
objects
+
=
- The following examples show the Hibernate APIs but the same example
- can be easily rewritten with the Java Persistence API by just adjusting =
the
- way the FullTextQuery is retrieved.
+ The following examples we will use the Hibernate APIs but the same
+ example can be easily rewritten with the Java Persistence API by just
+ adjusting the way the FullTextQuery is
+ retrieved.
=
Building queries
=
- Hibernate Search queries are built on top of Lucene queries. It
- gives you a total freedom on the kind of Lucene queries you are willin=
g to
- execute. However, once built, Hibernate Search abstract the query
- processing from your application using org.hibernate.Query as your pri=
mary
- query manipulation API.
+ Hibernate Search queries are built on top of Lucene queries which
+ gives you total freedom on the type of Lucene query you want to execut=
e.
+ However, once built, Hibernate Search wraps further query processing u=
sing
+ org.hibernate.Query as your primary query
+ manipulation API.
=
Building a Lucene query
=
- This subject is generally speaking out of the scope of this
- documentation. Please refer to the Lucene documentation Lucene In Ac=
tion
- or Hibernate Search in Action from Manning.
-
- It is essential to use the same analyzer when indexing a field=
and
- when querying that field. Hibernate Search gives you access to the
- analyzers used during indexing time (see for more information).
-
- //retrieve an analyzer by name
-Analyzer analyzer =3D fullTextSession.getSearchFactory().getAnalyzer("phon=
etic-analyzer");
-
-//or the scoped analyzer for a given entity
-Analyzer analyzer =3D fullTextSession.getSearchFactory().getAnalyzer(Song.=
class);
-
- Using the same analyzer at indexing and querying time is
- important. See for more information.=
para>
+ It is out of the scope of this documentation on how to exactly
+ build a Lucene query. Please refer to the online Lucene documentatio=
n or
+ get hold of a copy of either Lucene In Action or Hibernate Search in
+ Action.
=
@@ -122,39 +145,58 @@
Once the Lucene query is built, it needs to be wrapped into =
an
Hibernate Query.
=
- FullTextSession fullTextSession =3D Search.getFull=
TextSession( session );
+
+ Wrapping a Lucene query into a Hibernate Query
+
+ FullTextSession fullTextSession =3D Search.getFu=
llTextSession( session );
org.hibernate.Query fullTextQuery =3D fullTextSession.createFullTextQuery(=
luceneQuery );
+
=
If not specified otherwise, the query will be executed again=
st
all indexed entities, potentially returning all types of indexed
classes. It is advised, from a performance point of view, to restr=
ict
the returned types:
=
- org.hibernate.Query fullTextQuery =3D fullTextSess=
ion.createFullTextQuery( luceneQuery, Customer.class );
-//or
+
+ Filtering the search result by entity type
+
+ org.hibernate.Query fullTextQuery =3D fullTextSe=
ssion.createFullTextQuery( luceneQuery, Customer.class );
+// or
fullTextQuery =3D fullTextSession.createFullTextQuery( luceneQuery, Item.c=
lass, Actor.class );
+
=
- The first example returns only matching customers, the second
- returns matching actors and items.
+ The first example returns only matching
+ Customers, the second returns matching
+ Actors and Items. The
+ type restriction is fully polymorphic which means that if there are
+ two indexed subclasses Salesman and
+ Customer of the baseclass
+ Person, it is possible to just specify
+ Person.class in order to filter on result
+ types.
=
Pagination
=
- It is recommended to restrict the number of returned objects=
per
- query. It is a very common use case as well, the user usually navi=
gate
- from one page to an other. The way to define pagination is exactly=
the
- way you would define pagination in a plain HQL or Criteria
- query.
+ Out of performace reasons it is recommended to restrict the
+ number of returned objects per query. In fact is a very common use
+ case anyway that the user navigates from one page to an other. The=
way
+ to define pagination is exactly the way you would define paginatio=
n in
+ a plain HQL or Criteria query.
=
- org.hibernate.Query fullTextQuery =3D fullTextSess=
ion.createFullTextQuery( luceneQuery, Customer.class );
+
+ Defining pagination for a search query
+
+ org.hibernate.Query fullTextQuery =3D fullTextSe=
ssion.createFullTextQuery( luceneQuery, Customer.class );
fullTextQuery.setFirstResult(15); //start from the 15th element
fullTextQuery.setMaxResults(10); //return 10 elements
+
=
It is still possible to get the total number of matching
- elements regardless of the pagination. See
- getResultSize() below
+ elements regardless of the pagination via
+ fulltextQuery.getResultSize=
()
=
@@ -163,22 +205,24 @@
=
Apache Lucene provides a very flexible and powerful way to s=
ort
results. While the default sorting (by relevance) is appropriate m=
ost
- of the time, it can interesting to sort by one or several
- properties.
+ of the time, it can be interesting to sort by one or several other
+ properties. In order to do so set the Lucene Sort object to apply a
+ Lucene sorting strategy.
=
- Inject the Lucene Sort object to apply a Lucene sorting stra=
tegy
- to an Hibernate Search.
+
+ Specifying a Lucene Sort in order =
to
+ sort the results
=
- org.hibernate.search.FullTextQuery query =3D s.cre=
ateFullTextQuery( query, Book.class );
+ org.hibernate.search.FullTextQuery query =3D s.c=
reateFullTextQuery( query, Book.class );
org.apache.lucene.search.Sort sort =3D new Sort(new SortField("title"));
query.setSort(sort);
List results =3D query.list();
+
=
One can notice the FullTextQuery
interface which is a sub interface of
- org.hibernate.Query.
-
- Fields used for sorting must not be tokenized.
+ org.hibernate.Query. Be aware that fields u=
sed
+ for sorting must not be tokenized.
=
@@ -191,8 +235,13 @@
It is often useful, however, to refine the fetching strategy=
for
a specific use case.
=
- Criteria criteria =3D s.createCriteria( Book.class=
).setFetchMode( "authors", FetchMode.JOIN );
+
+ Specifying FetchMode on a
+ query
+
+ Criteria criteria =3D s.createCriteria( Book.cla=
ss ).setFetchMode( "authors", FetchMode.JOIN );
s.createFullTextQuery( luceneQuery ).setCriteriaQuery( criteria );
+
=
In this example, the query will return all Books matching the
luceneQuery. The authors collection will be loaded from the same q=
uery
@@ -215,7 +264,11 @@
overkill. Only a small subset of the properties is necessary.
Hibernate Search allows you to return a subset of properties:
=
- org.hibernate.search.FullTextQuery query =3D s.cre=
ateFullTextQuery( luceneQuery, Book.class );
+
+ Using projection instead of returning the full domain
+ object
+
+ org.hibernate.search.FullTextQuery query =3D s.c=
reateFullTextQuery( luceneQuery, Book.class );
query.setProjection( "id", "summary", "body", "mai=
nAuthor.name" );
List results =3D query.list();
Object[] firstResult =3D (Object[]) results.get(0);
@@ -223,6 +276,7 @@
String summary =3D firstResult[1];
String body =3D firstResult[2];
String authorName =3D firstResult[3];
+
=
Hibernate Search extracts the properties from the Lucene ind=
ex
and convert them back to their object representation, returning a =
list
@@ -246,6 +300,17 @@
the latter being the simpler version. All Hibernate Search
built-in types are two-way.
+
+
+ you can only project simple properties of the indexed en=
tity
+ or its embedded associations. This means you cannot project a
+ whole embedded entity.
+
+
+
+ projection does not work on collections or maps which are
+ indexed via @IndexedEmbedded
+
=
Projection is useful for another kind of usecases. Lucene
@@ -253,13 +318,17 @@
using some special placeholders, the projection mechanism can retr=
ieve
them:
=
- org.hibernate.search.FullTextQuery query =3D s.cre=
ateFullTextQuery( luceneQuery, Book.class );
+
+ Using projection in order to retrieve meta data
+
+ org.hibernate.search.FullTextQuery query =3D s.c=
reateFullTextQuery( luceneQuery, Book.class );
query.setProjection( FullTextQuery.SCORE, FullText=
Query.THIS, "mainAuthor.name" );
List results =3D query.list();
Object[] firstResult =3D (Object[]) results.get(0);
float score =3D firstResult[0];
Book book =3D firstResult[1];
String authorName =3D firstResult[2];
+
=
You can mix and match regular fields and special placeholder=
s.
Here is the list of available placeholders:
@@ -315,7 +384,7 @@
=
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 apply. All the common operations are available:
+ object semantic applies. All the common operations are available:
list(), uniqueResult(),
iterate(),
scroll().
@@ -338,8 +407,8 @@
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, y=
ou
- can use query.setFetchSize(): When an objec=
t is
+ scroll, but wish to load objects in batch, =
you
+ can use query.setFetchSize(). When an objec=
t is
accessed, and if not already loaded, Hibernate Search will load the =
next
fetchSize objects in one pass.
=
@@ -367,21 +436,23 @@
=
- But it would be costly to retrieve all the matching
- documents.
-
- Hibernate Search allows you to retrieve the total number of
+ 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.
=
- org.hibernate.search.FullTextQuery query =3D s.creat=
eFullTextQuery( luceneQuery, Book.class );
+
+ Determining the result size of a query
+
+ org.hibernate.search.FullTextQuery query =3D s.cre=
ateFullTextQuery( luceneQuery, Book.class );
assert 3245 =3D=3D query.getResultSize(); //return the number of matching books without loading a single one
=
org.hibernate.search.FullTextQuery query =3D s.createFullTextQuery( lucene=
Query, Book.class );
query.setMaxResult(10);
List results =3D query.list();
assert 3245 =3D=3D query.getResultSize(); //return the total number of matching books regardless of pagination
+
=
Like Google, the number of results is approximative if the i=
ndex
@@ -399,7 +470,10 @@
ResultTransformer operation post query to mat=
ch
the targeted data structure:
=
- org.hibernate.search.FullTextQuery query =3D s.creat=
eFullTextQuery( luceneQuery, Book.class );
+
+ Using ResultTransformer in conjuncton with projections
+
+ org.hibernate.search.FullTextQuery query =3D s.cre=
ateFullTextQuery( luceneQuery, Book.class );
query.setProjection( "title", "mainAuthor.name" );
=
query.setResultTransformer( =
@@ -409,6 +483,7 @@
for(BookView view : results) {
log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() );
}
+
=
Examples of ResultTransformer
implementations can be found in the Hibernate Core codebase.
@@ -419,12 +494,12 @@
=
You will find yourself sometimes puzzled by a result showing u=
p in
a query or a result not showing up in a query. Luke is a great tool =
to
- understand those mysteries. But Hibernate Search also let's you acce=
ss
- 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 giv=
en
- result:
+ 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 ob=
ject
+ for a given result:
=
@@ -443,21 +518,26 @@
constant.
=
- The Document id has nothing to do with the entity id. do not
- mess up the two notions.
+ 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.
=
- FullTextQuery ftQuery =3D s.createFullTextQuery( luc=
eneQuery, Dvd.class )
+
+ Retrieving the Lucene Explanation object using
+ projection
+
+ FullTextQuery ftQuery =3D s.createFullTextQuery( l=
uceneQuery, Dvd.class )
.setProjection( FullTextQuery.DOCUMENT_ID, FullTextQuery.EXPLANATION, FullTextQuery.THIS );
@SuppressWarnings("unchecked") List<Object[]> results =3D ftQuery.li=
st();
for (Object[] result : results) {
Explanation e =3D (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
@@ -497,10 +577,14 @@
For people familiar with the notion of Hibernate Core filters, the API=
is
very similar:
=
- fullTextQuery =3D s.createFullTextQuery( query, Driver=
.class );
+
+ Enabling fulltext filters for a given query
+
+ fullTextQuery =3D s.createFullTextQuery( query, Driv=
er.class );
fullTextQuery.enableFullTextFilter("bestDriver");
fullTextQuery.enableFullTextFilter("security").setParameter( "login", "and=
re" );
fullTextQuery.list(); //returns only best drivers where andre has credenti=
als
+
=
In this example we enabled two filters on top of the query. You =
can
enable (or disable) as many filters as you like.
@@ -515,7 +599,10 @@
are defined. Each named filter has to specify its actual filter
implementation.
=
- @Entity
+
+ Defining and implementing a Filter
+
+ @Entity
@Indexed
@FullTextFilterDefs( {
@FullTextFilterDef(name =3D "bestDriver", impl=
=3D BestDriversFilter.class), =
@@ -523,8 +610,8 @@
})
public class Driver { ... }
=
- public class BestDriversFilter extends org.apache.lucene.search.Filter {
+ public class BestDriversFilter extends org.apache.lucene.search.Filter {
=
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
OpenBitSet bitSet =3D new OpenBitSet( reader.maxDoc() );
@@ -535,6 +622,7 @@
return bitSet;
}
}
+
=
BestDriversFilter is an example of a simp=
le
Lucene filter which reduces the result set to drivers whose score is 5=
. In
@@ -546,7 +634,10 @@
you want to use does not have a no-arg constructor, you can use the
factory pattern:
=
- @Entity
+
+ Creating a filter using the factory pattern
+
+ @Entity
@Indexed
@FullTextFilterDef(name =3D "bestDriver", impl =3D BestDriversFilterFactor=
y.class)
public class Driver { ... }
@@ -560,6 +651,7 @@
return new CachingWrapperFilter(bestDriversFilter);
}
}
+
=
Hibernate Search will look for a @Factory
annotated method and use it to build the filter instance. The factory =
must
@@ -571,13 +663,20 @@
the filter. For example a security filter might want to know which
security level you want to apply:
=
- fullTextQuery =3D s.createFullTextQuery( query, Driver=
.class );
+
+ Passing parameters to a defined filter
+
+ fullTextQuery =3D s.createFullTextQuery( query, Driv=
er.class );
fullTextQuery.enableFullTextFilter("security").set=
Parameter( "level", 5 );
+
=
Each parameter name should have an associated setter on either t=
he
filter or filter factory of the targeted named filter definition.
=
- public class SecurityFilterFactory {
+
+ Using paramters in the actual filter implementation
+
+ public class SecurityFilterFactory {
private Integer level;
=
/**
@@ -600,6 +699,7 @@
return new CachingWrapperFilter( new QueryWrapperFilter(query) );
}
}
+
=
Note the method annotated @Key returning a
FilterKey object. The returned object has a spe=
cial
--===============8303571248220900466==--