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. + 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 <classname>Sort</classname> 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 <classname>FetchMode</classname> 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</tit= le> + + <programlisting>org.hibernate.search.FullTextQuery query =3D s.cre= ateFullTextQuery( luceneQuery, Book.class ); query.setProjection( "title", "mainAuthor.name" ); = <emphasis role=3D"bold">query.setResultTransformer( = @@ -409,6 +483,7 @@ for(BookView view : results) { log.info( "Book: " + view.getTitle() + ", " + view.getAuthor() ); }</programlisting> + </example> = <para>Examples of <classname>ResultTransformer</classname> implementations can be found in the Hibernate Core codebase.</para> @@ -419,12 +494,12 @@ = <para>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 <classname>Explanation</classname> 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:</para> + understand those mysteries. However, Hibernate Search also gives you + access to the Lucene <classname>Explanation</classname> 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:</para> = <itemizedlist> <listitem> @@ -443,21 +518,26 @@ constant.</para> = <warning> - <para>The Document id has nothing to do with the entity id. do not - mess up the two notions.</para> + <para>The Document id has nothing to do with the entity id. Do not + mess up these two notions.</para> </warning> = <para>The second approach let's you project the <classname>Explanation</classname> object using the <literal>FullTextQuery.EXPLANATION</literal> constant.</para> = - <programlisting>FullTextQuery ftQuery =3D s.createFullTextQuery( luc= eneQuery, Dvd.class ) + <example> + <title>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==--