[hibernate-commits] Hibernate SVN: r15635 - search/trunk/doc/reference/en/modules.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Tue Dec 2 09:18:09 EST 2008


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
===================================================================
--- 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 @@
 
   <para>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).</para>
+  search mechanisms (HQL, Criteria query, native SQL query). Preparing and
+  executing a query consists of four simple steps:</para>
 
-  <para>To access the <productname>Hibernate Search</productname> querying
-  facilities, you have to use an Hibernate
-  <classname>FullTextSession</classname> . A Search Session wraps a regular
-  <classname>org.hibernate.Session</classname> to provide query and indexing
-  capabilities.</para>
+  <itemizedlist>
+    <listitem>
+      <para>Creating a <classname>FullTextSession</classname></para>
+    </listitem>
 
-  <programlisting>Session session = sessionFactory.openSession();
+    <listitem>
+      <para>Creating a Lucene query</para>
+    </listitem>
+
+    <listitem>
+      <para>Wrapping the Lucene query using a
+      <classname>org.hibernate.Query</classname></para>
+    </listitem>
+
+    <listitem>
+      <para>Executing the search by calling for example
+      <methodname>list()</methodname> or
+      <methodname>scroll()</methodname></para>
+    </listitem>
+  </itemizedlist>
+
+  <para>To access the querying facilities, you have to use an
+  <classname>FullTextSession</classname> . This Search specfic session wraps a
+  regular <classname>org.hibernate.Session</classname> to provide query and
+  indexing capabilities.</para>
+
+  <example>
+    <title>Creating a FullTextSession</title>
+
+    <programlisting>Session session = sessionFactory.openSession();
 ...
 FullTextSession fullTextSession = Search.getFullTextSession(session);    </programlisting>
+  </example>
 
-  <para>The search facility is built on native Lucene queries.</para>
+  <para>The actual search facility is built on native Lucene queries which the
+  following example illustrates.</para>
 
-  <programlisting>org.apache.lucene.queryParser.QueryParser parser = new QueryParser("title", new StopAnalyzer() );
+  <example>
+    <title>Creating a Lucene query</title>
 
+    <programlisting>org.apache.lucene.queryParser.QueryParser parser = 
+    new QueryParser("title", new StopAnalyzer() );
+
 org.apache.lucene.search.Query luceneQuery = parser.parse( "summary:Festina Or brand:Seiko" );
 <emphasis role="bold">org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );
         </emphasis>
-
 List result = fullTextQuery.list(); //return a list of managed objects    </programlisting>
+  </example>
 
   <para>The Hibernate query built on top of the Lucene query is a regular
-  <literal>org.hibernate.Query</literal> , you are in the same paradigm as the
-  other Hibernate query facilities (HQL, Native or Criteria). The regular
-  <literal>list()</literal> , <literal>uniqueResult()</literal> ,
-  <literal>iterate()</literal> and <literal>scroll()</literal> can be
+  <literal>org.hibernate.Query</literal>, which means you are in the same
+  paradigm as the other Hibernate query facilities (HQL, Native or Criteria).
+  The regular <literal>list()</literal> , <literal>uniqueResult()</literal>,
+  <literal>iterate()</literal> and <literal>scroll()</literal> methods can be
   used.</para>
 
-  <para>For people using Java Persistence (aka EJB 3.0 Persistence) APIs of
-  Hibernate, the same extensions exist:</para>
+  <para>In case you are using the Java Persistence APIs of Hibernate (aka EJB
+  3.0 Persistence), the same extensions exist:</para>
 
-  <programlisting>EntityManager em = entityManagerFactory.createEntityManager();
+  <example>
+    <title>Creating a Search query using the JPA API</title>
 
+    <programlisting>EntityManager em = entityManagerFactory.createEntityManager();
+
 FullTextEntityManager fullTextEntityManager = 
     org.hibernate.hibernate.search.jpa.Search.getFullTextEntityManager(em);
 
 ...
-org.apache.lucene.queryParser.QueryParser parser = new QueryParser("title", new StopAnalyzer() );
+org.apache.lucene.queryParser.QueryParser parser = 
+    new QueryParser("title", new StopAnalyzer() );
 
 org.apache.lucene.search.Query luceneQuery = parser.parse( "summary:Festina Or brand:Seiko" );
 <emphasis role="bold">javax.persistence.Query fullTextQuery = fullTextEntityManager.createFullTextQuery( luceneQuery );</emphasis>
 
 List result = fullTextQuery.getResultList(); //return a list of managed objects  </programlisting>
+  </example>
 
-  <para>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.</para>
+  <para>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 <classname>FullTextQuery</classname> is
+  retrieved.</para>
 
   <section>
     <title>Building queries</title>
 
-    <para>Hibernate Search queries are built on top of Lucene queries. It
-    gives you a total freedom on the kind of Lucene queries you are willing to
-    execute. However, once built, Hibernate Search abstract the query
-    processing from your application using org.hibernate.Query as your primary
-    query manipulation API.</para>
+    <para>Hibernate Search queries are built on top of Lucene queries which
+    gives you total freedom on the type of Lucene query you want to execute.
+    However, once built, Hibernate Search wraps further query processing using
+    <classname>org.hibernate.Query</classname> as your primary query
+    manipulation API. </para>
 
     <section>
       <title>Building a Lucene query</title>
 
-      <para>This subject is generally speaking out of the scope of this
-      documentation. Please refer to the Lucene documentation Lucene In Action
-      or Hibernate Search in Action from Manning.</para>
-
-      <para>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 <xref
-      linkend="analyzer-retrievinganalyzer" /> for more information).</para>
-
-      <programlisting>//retrieve an analyzer by name
-Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer("phonetic-analyzer");
-
-//or the scoped analyzer for a given entity
-Analyzer analyzer = fullTextSession.getSearchFactory().getAnalyzer(Song.class);</programlisting>
-
-      <para>Using the same analyzer at indexing and querying time is
-      important. See <xref linkend="analyzer" /> for more information.</para>
+      <para>It is out of the scope of this documentation on how to exactly
+      build a Lucene query. Please refer to the online Lucene documentation or
+      get hold of a copy of either Lucene In Action or Hibernate Search in
+      Action.</para>
     </section>
 
     <section>
@@ -122,39 +145,58 @@
         <para>Once the Lucene query is built, it needs to be wrapped into an
         Hibernate Query.</para>
 
-        <programlisting>FullTextSession fullTextSession = Search.getFullTextSession( session );
+        <example>
+          <title>Wrapping a Lucene query into a Hibernate Query</title>
+
+          <programlisting>FullTextSession fullTextSession = Search.getFullTextSession( session );
 org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery );</programlisting>
+        </example>
 
         <para>If not specified otherwise, the query will be executed against
         all indexed entities, potentially returning all types of indexed
         classes. It is advised, from a performance point of view, to restrict
         the returned types:</para>
 
-        <programlisting>org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Customer.class );
-//or
+        <example>
+          <title>Filtering the search result by entity type</title>
+
+          <programlisting>org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Customer.class );
+// or
 fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Item.class, Actor.class );</programlisting>
+        </example>
 
-        <para>The first example returns only matching customers, the second
-        returns matching actors and items.</para>
+        <para>The first example returns only matching
+        <classname>Customer</classname>s, the second returns matching
+        <classname>Actor</classname>s and <classname>Item</classname>s. The
+        type restriction is fully polymorphic which means that if there are
+        two indexed subclasses <classname>Salesman</classname> and
+        <classname>Customer</classname> of the baseclass
+        <classname>Person</classname>, it is possible to just specify
+        <classname>Person.class</classname> in order to filter on result
+        types. </para>
       </section>
 
       <section>
         <title>Pagination</title>
 
-        <para>It is recommended to restrict the number of returned objects per
-        query. It is a very common use case as well, the user usually navigate
-        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.</para>
+        <para>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 pagination in
+        a plain HQL or Criteria query.</para>
 
-        <programlisting>org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Customer.class );
+        <example>
+          <title>Defining pagination for a search query</title>
+
+          <programlisting>org.hibernate.Query fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, Customer.class );
 fullTextQuery.setFirstResult(15); //start from the 15th element
 fullTextQuery.setMaxResults(10); //return 10 elements</programlisting>
+        </example>
 
         <note>
           <para>It is still possible to get the total number of matching
-          elements regardless of the pagination. See
-          <methodname>getResultSize()</methodname> below</para>
+          elements regardless of the pagination via
+          <methodname>fulltextQuery.</methodname><methodname>getResultSize()</methodname></para>
         </note>
       </section>
 
@@ -163,22 +205,24 @@
 
         <para>Apache Lucene provides a very flexible and powerful way to sort
         results. While the default sorting (by relevance) is appropriate most
-        of the time, it can interesting to sort by one or several
-        properties.</para>
+        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.</para>
 
-        <para>Inject the Lucene Sort object to apply a Lucene sorting strategy
-        to an Hibernate Search.</para>
+        <example>
+          <title>Specifying a Lucene <classname>Sort</classname> in order to
+          sort the results</title>
 
-        <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( query, Book.class );
+          <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( query, Book.class );
 org.apache.lucene.search.Sort sort = new Sort(new SortField("title"));
 <emphasis role="bold">query.setSort(sort);</emphasis>
 List results = query.list();</programlisting>
+        </example>
 
         <para>One can notice the <classname>FullTextQuery</classname>
         interface which is a sub interface of
-        <classname>org.hibernate.Query</classname>.</para>
-
-        <para>Fields used for sorting must not be tokenized.</para>
+        <classname>org.hibernate.Query</classname>. Be aware that fields used
+        for sorting must not be tokenized.</para>
       </section>
 
       <section>
@@ -191,8 +235,13 @@
         <para>It is often useful, however, to refine the fetching strategy for
         a specific use case.</para>
 
-        <programlisting>Criteria criteria = s.createCriteria( Book.class ).setFetchMode( "authors", FetchMode.JOIN );
+        <example>
+          <title>Specifying <classname>FetchMode</classname> on a
+          query</title>
+
+          <programlisting>Criteria criteria = s.createCriteria( Book.class ).setFetchMode( "authors", FetchMode.JOIN );
 s.createFullTextQuery( luceneQuery ).setCriteriaQuery( criteria );</programlisting>
+        </example>
 
         <para>In this example, the query will return all Books matching the
         luceneQuery. The authors collection will be loaded from the same query
@@ -215,7 +264,11 @@
         overkill. Only a small subset of the properties is necessary.
         Hibernate Search allows you to return a subset of properties:</para>
 
-        <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
+        <example>
+          <title>Using projection instead of returning the full domain
+          object</title>
+
+          <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
 query.<emphasis role="bold">setProjection( "id", "summary", "body", "mainAuthor.name" )</emphasis>;
 List results = query.list();
 Object[] firstResult = (Object[]) results.get(0);
@@ -223,6 +276,7 @@
 String summary = firstResult[1];
 String body = firstResult[2];
 String authorName = firstResult[3];</programlisting>
+        </example>
 
         <para>Hibernate Search extracts the properties from the Lucene index
         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.</para>
           </listitem>
+
+          <listitem>
+            <para>you can only project simple properties of the indexed entity
+            or its embedded associations. This means you cannot project a
+            whole embedded entity.</para>
+          </listitem>
+
+          <listitem>
+            <para>projection does not work on collections or maps which are
+            indexed via <classname>@IndexedEmbedded</classname></para>
+          </listitem>
         </itemizedlist>
 
         <para>Projection is useful for another kind of usecases. Lucene
@@ -253,13 +318,17 @@
         using some special placeholders, the projection mechanism can retrieve
         them:</para>
 
-        <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
+        <example>
+          <title>Using projection in order to retrieve meta data</title>
+
+          <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
 query.<emphasis role="bold">setProjection( FullTextQuery.SCORE, FullTextQuery.THIS, "mainAuthor.name" )</emphasis>;
 List results = query.list();
 Object[] firstResult = (Object[]) results.get(0);
 float score = firstResult[0];
 Book book = firstResult[1];
 String authorName = firstResult[2];</programlisting>
+        </example>
 
         <para>You can mix and match regular fields and special placeholders.
         Here is the list of available placeholders:</para>
@@ -315,7 +384,7 @@
 
     <para>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:
     <methodname>list()</methodname>, <methodname>uniqueResult()</methodname>,
     <methodname>iterate()</methodname>,
     <methodname>scroll()</methodname>.</para>
@@ -338,8 +407,8 @@
       <methodname>scroll()</methodname> is more appropriate. Don't forget to
       close the <classname>ScrollableResults</classname> object when you're
       done, since it keeps Lucene resources. If you expect to use
-      <methodname>scroll</methodname> but wish to load objects in batch, you
-      can use <methodname>query.setFetchSize()</methodname>: When an object is
+      <methodname>scroll,</methodname> but wish to load objects in batch, you
+      can use <methodname>query.setFetchSize()</methodname>. When an object is
       accessed, and if not already loaded, Hibernate Search will load the next
       <literal>fetchSize</literal> objects in one pass.</para>
 
@@ -367,21 +436,23 @@
         </listitem>
       </itemizedlist>
 
-      <para>But it would be costly to retrieve all the matching
-      documents.</para>
-
-      <para>Hibernate Search allows you to retrieve the total number of
+      <para>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.</para>
 
-      <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
+      <example>
+        <title>Determining the result size of a query</title>
+
+        <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
 assert 3245 == <emphasis role="bold">query.getResultSize()</emphasis>; //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 == <emphasis role="bold">query.getResultSize()</emphasis>; //return the total number of matching books regardless of pagination</programlisting>
+      </example>
 
       <note>
         <para>Like Google, the number of results is approximative if the index
@@ -399,7 +470,10 @@
       <classname>ResultTransformer</classname> operation post query to match
       the targeted data structure:</para>
 
-      <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
+      <example>
+        <title>Using ResultTransformer in conjuncton with projections</title>
+
+        <programlisting>org.hibernate.search.FullTextQuery query = s.createFullTextQuery( luceneQuery, Book.class );
 query.setProjection( "title", "mainAuthor.name" );
 
 <emphasis role="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 up 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 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 object for a given
-      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 object
+      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 = s.createFullTextQuery( luceneQuery, Dvd.class )
+      <example>
+        <title>Retrieving the Lucene Explanation object using
+        projection</title>
+
+        <programlisting>FullTextQuery ftQuery = s.createFullTextQuery( luceneQuery, Dvd.class )
         .setProjection( FullTextQuery.DOCUMENT_ID, <emphasis role="bold">FullTextQuery.EXPLANATION</emphasis>, FullTextQuery.THIS );
 @SuppressWarnings("unchecked") List&lt;Object[]&gt; results = ftQuery.list();
 for (Object[] result : results) {
     Explanation e = (Explanation) result[1];
     display( e.toString() );
 }</programlisting>
+      </example>
 
       <para>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:</para>
 
-    <programlisting>fullTextQuery = s.createFullTextQuery( query, Driver.class );
+    <example>
+      <title>Enabling fulltext filters for a given query</title>
+
+      <programlisting>fullTextQuery = s.createFullTextQuery( query, Driver.class );
 fullTextQuery.enableFullTextFilter("bestDriver");
 fullTextQuery.enableFullTextFilter("security").setParameter( "login", "andre" );
 fullTextQuery.list(); //returns only best drivers where andre has credentials</programlisting>
+    </example>
 
     <para>In this example we enabled two filters on top of the query. You can
     enable (or disable) as many filters as you like.</para>
@@ -515,7 +599,10 @@
     are defined. Each named filter has to specify its actual filter
     implementation.</para>
 
-    <programlisting>@Entity
+    <example>
+      <title>Defining and implementing a Filter</title>
+
+      <programlisting>@Entity
 @Indexed
 @FullTextFilterDefs( {
     <emphasis role="bold">@FullTextFilterDef(name = "bestDriver", impl = BestDriversFilter.class)</emphasis>, 
@@ -523,8 +610,8 @@
 })
 public class Driver { ... }</programlisting>
 
-    <programlisting>public class BestDriversFilter extends <emphasis
-        role="bold">org.apache.lucene.search.Filter</emphasis> {
+      <programlisting>public class BestDriversFilter extends <emphasis
+          role="bold">org.apache.lucene.search.Filter</emphasis> {
 
     public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
         OpenBitSet bitSet = new OpenBitSet( reader.maxDoc() );
@@ -535,6 +622,7 @@
         return bitSet;
     }
 }</programlisting>
+    </example>
 
     <para><classname>BestDriversFilter</classname> is an example of a simple
     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:</para>
 
-    <programlisting>@Entity
+    <example>
+      <title>Creating a filter using the factory pattern</title>
+
+      <programlisting>@Entity
 @Indexed
 @FullTextFilterDef(name = "bestDriver", impl = BestDriversFilterFactory.class)
 public class Driver { ... }
@@ -560,6 +651,7 @@
         return new CachingWrapperFilter(bestDriversFilter);
     }
 }</programlisting>
+    </example>
 
     <para>Hibernate Search will look for a <literal>@Factory</literal>
     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:</para>
 
-    <programlisting>fullTextQuery = s.createFullTextQuery( query, Driver.class );
+    <example>
+      <title>Passing parameters to a defined filter</title>
+
+      <programlisting>fullTextQuery = s.createFullTextQuery( query, Driver.class );
 fullTextQuery.enableFullTextFilter("security")<emphasis role="bold">.setParameter( "level", 5 )</emphasis>;</programlisting>
+    </example>
 
     <para>Each parameter name should have an associated setter on either the
     filter or filter factory of the targeted named filter definition.</para>
 
-    <programlisting>public class SecurityFilterFactory {
+    <example>
+      <title>Using paramters in the actual filter implementation</title>
+
+      <programlisting>public class SecurityFilterFactory {
     private Integer level;
 
     /**
@@ -600,6 +699,7 @@
         return new CachingWrapperFilter( new QueryWrapperFilter(query) );
     }
 }</programlisting>
+    </example>
 
     <para>Note the method annotated <classname>@Key</classname> returning a
     <classname>FilterKey</classname> object. The returned object has a special




More information about the hibernate-commits mailing list