Java Mailing List Archive

http://www.gg3721.com/

Home » Hibernate Commits List »

[hibernate-commits] Hibernate SVN: r14940 - in search/trunk/src:
 java/org/hibernate/search/engine and 5 other directories.

hibernate-commits

2008-07-16


Author LoginPost Reply
Author: epbernard
Date: 2008-07-16 20:15:47 -0400 (Wed, 16 Jul 2008)
New Revision: 14940

Added:
 search/trunk/src/test/org/hibernate/search/test/query/explain/
 search/trunk/src/test/org/hibernate/search/test/query/explain/Dvd.java
 search/trunk/src/test/org/hibernate/search/test/query/explain/ExplanationTest.java
Modified:
 search/trunk/src/java/org/hibernate/search/FullTextQuery.java
 search/trunk/src/java/org/hibernate/search/ProjectionConstants.java
 search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java
 search/trunk/src/java/org/hibernate/search/jpa/FullTextQuery.java
 search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextQueryImpl.java
 search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
Log:
HSEARCH-154 add explain method

Modified: search/trunk/src/java/org/hibernate/search/FullTextQuery.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/FullTextQuery.java  2008-07-16 18:08:17 UTC (rev 14939)
+++ search/trunk/src/java/org/hibernate/search/FullTextQuery.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@

import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.Explanation;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.transform.ResultTransformer;
@@(protected) @@
 void disableFullTextFilter(String name);

 /**
+   * Return the Lucene {@(protected)}
+   * object describing the score computation for the matching object/document
+   * in the current query
+   *
+   * @param documentId Lucene Document id to be explain. This is NOT the object id
+   * @return Lucene Explanation
+   */
+  Explanation explain(int documentId);
+
+  /**
  * {link:Query#setFirstResult}
  */
 FullTextQuery setFirstResult(int firstResult);

Modified: search/trunk/src/java/org/hibernate/search/ProjectionConstants.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/ProjectionConstants.java  2008-07-16 18:08:17 UTC (rev 14939)
+++ search/trunk/src/java/org/hibernate/search/ProjectionConstants.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@
  */
 public String DOCUMENT_ID = "__HSearch_DocumentId";
 /**
+   * Lucene {@(protected)
+   * the matching object/document
+   * This feature is relatively expensive, do not use unless you return a limited
+   * amount of objects (using pagination)
+   * To retrieve explanation of a single result, consider retrieving {@(protected)}
+   * and using fullTextQuery.explain(int)
+   */
+  public String EXPLANATION = "__HSearch_Explanation";
+  
+  /**
  * Object class
  */
 //TODO OBJECT CLASS

Modified: search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java  2008-07-16 18:08:17 UTC (rev 14939)
+++ search/trunk/src/java/org/hibernate/search/engine/DocumentExtractor.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@

import org.apache.lucene.document.Document;
import org.apache.lucene.search.Hits;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
import org.hibernate.search.engine.EntityInfo;
import org.hibernate.search.ProjectionConstants;

@@(protected) @@
public class DocumentExtractor {
 private final SearchFactoryImplementor searchFactoryImplementor;
 private final String[] projection;
+  private final IndexSearcher searcher;
+  private final Query preparedQuery;

-  public DocumentExtractor(SearchFactoryImplementor searchFactoryImplementor, String... projection) {
+  public DocumentExtractor(Query preparedQuery, IndexSearcher searcher, SearchFactoryImplementor searchFactoryImplementor, String... projection) {
   this.searchFactoryImplementor = searchFactoryImplementor;
   this.projection = projection;
+    this.searcher = searcher;
+    this.preparedQuery = preparedQuery;
 }

 private EntityInfo extract(Document document) {
@@(protected) @@
       else if ( ProjectionConstants.BOOST.equals( projection[x] ) ) {
         eip[x] = doc.getBoost();
       }
+        else if ( ProjectionConstants.EXPLANATION.equals( projection[x] ) ) {
+          eip[x] = searcher.explain( preparedQuery, hits.id( index ) );
+        }
       else if ( ProjectionConstants.THIS.equals( projection[x] ) ) {
         //THIS could be projected more than once
         //THIS loading delayed to the Loader phase

Modified: search/trunk/src/java/org/hibernate/search/jpa/FullTextQuery.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/jpa/FullTextQuery.java  2008-07-16 18:08:17 UTC (rev 14939)
+++ search/trunk/src/java/org/hibernate/search/jpa/FullTextQuery.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@

import org.apache.lucene.search.Sort;
import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.Explanation;
import org.hibernate.Criteria;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.search.ProjectionConstants;
@@(protected) @@
  *
  */
 FullTextQuery setResultTransformer(ResultTransformer transformer);
+
+  /**
+   * Return the Lucene {@(protected)}
+   * object describing the score computation for the matching object/document
+   * in the current query
+   *
+   * @param documentId Lucene Document id to be explain. This is NOT the object id
+   * @return Lucene Explanation
+   */
+  Explanation explain(int documentId);
}

Modified: search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextQueryImpl.java  2008-07-16 18:08:17 UTC (rev 14939)
+++ search/trunk/src/java/org/hibernate/search/jpa/impl/FullTextQueryImpl.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@

import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.Explanation;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
@@(protected) @@
   return this;
 }

+  public Explanation explain(int documentId) {
+    return query.explain( documentId );
+  }
+
 public int executeUpdate() {
   throw new IllegalStateException( "Update not allowed in FullTextQueries" );
 }

Modified: search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java  2008-07-16 18:08:17 UTC (rev 14939)
+++ search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@
import org.apache.lucene.search.Similarity;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.Explanation;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
@@(protected) @@
     return new IteratorImpl( Collections.EMPTY_LIST, noLoader );
   }
   try {
-      Hits hits = getHits( searcher );
+      QueryAndHits queryAndHits = getQueryAndHits( searcher );
     int first = first();
-      int max = max( first, hits );
+      int max = max( first, queryAndHits.hits );
     Session sess = (Session) this.session;

     int size = max - first + 1 < 0 ? 0 : max - first + 1;
     List<EntityInfo> infos = new ArrayList<EntityInfo>( size );
-      DocumentExtractor extractor = new DocumentExtractor( searchFactoryImplementor, indexProjection );
+      DocumentExtractor extractor = new DocumentExtractor( queryAndHits.preparedQuery, searcher, searchFactoryImplementor, indexProjection );
     for (int index = first; index <= max; index++) {
       //TODO use indexSearcher.getIndexReader().document( hits.id(index), FieldSelector(indexProjection) );
-        infos.add( extractor.extract( hits, index ) );
+        infos.add( extractor.extract( queryAndHits.hits, index ) );
     }
     Loader loader = getLoader( sess, searchFactoryImplementor );
     return new IteratorImpl( infos, loader );
@@(protected) @@
   //find the directories
   IndexSearcher searcher = buildSearcher( searchFactory );
   //FIXME: handle null searcher
-    Hits hits;
   try {
-      hits = getHits( searcher );
+      QueryAndHits queryAndHits = getQueryAndHits( searcher );
     int first = first();
-      int max = max( first, hits );
-      DocumentExtractor extractor = new DocumentExtractor( searchFactory, indexProjection );
+      int max = max( first, queryAndHits.hits );
+      DocumentExtractor extractor = new DocumentExtractor( queryAndHits.preparedQuery, searcher, searchFactory, indexProjection );
     Loader loader = getLoader( (Session) this.session, searchFactory );
-      return new ScrollableResultsImpl( searcher, hits, first, max, fetchSize, extractor, loader, searchFactory );
+      return new ScrollableResultsImpl( searcher, queryAndHits.hits, first, max, fetchSize, extractor, loader, searchFactory );
   }
   catch (IOException e) {
     //close only in case of exception
@@(protected) @@
   //find the directories
   IndexSearcher searcher = buildSearcher( searchFactoryImplementor );
   if ( searcher == null ) return Collections.EMPTY_LIST;
-    Hits hits;
   try {
-      hits = getHits( searcher );
+      QueryAndHits queryAndHits = getQueryAndHits( searcher );
     int first = first();
-      int max = max( first, hits );
+      int max = max( first, queryAndHits.hits );
     Session sess = (Session) this.session;

     int size = max - first + 1 < 0 ? 0 : max - first + 1;
     List<EntityInfo> infos = new ArrayList<EntityInfo>( size );
-      DocumentExtractor extractor = new DocumentExtractor( searchFactoryImplementor, indexProjection );
+      DocumentExtractor extractor = new DocumentExtractor( queryAndHits.preparedQuery, searcher, searchFactoryImplementor, indexProjection );
     for (int index = first; index <= max; index++) {
-        infos.add( extractor.extract( hits, index ) );
+        infos.add( extractor.extract( queryAndHits.hits, index ) );
     }
     Loader loader = getLoader( sess, searchFactoryImplementor );
     List list = loader.load( infos.toArray( new EntityInfo[infos.size()] ) );
@@(protected) @@
   }
 }

+  public Explanation explain(int documentId) {
+    Explanation explanation = null;
+    SearchFactoryImplementor searchFactoryImplementor = getSearchFactoryImplementor();
+    Searcher searcher = buildSearcher( searchFactoryImplementor );
+    if (searcher == null) {
+      throw new SearchException("Unable to build explanation for document id:"
+          + documentId + ". no index found");
+    }
+    try {
+      org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
+      buildFilters();
+      explanation = searcher.explain( query, documentId );
+    }
+    catch (IOException e) {
+      throw new HibernateException( "Unable to query Lucene index and build explanation", e );
+    }
+    finally {
+      //searcher cannot be null
+      try {
+        closeSearcher( searcher, searchFactoryImplementor.getReaderProvider() );
+      }
+      catch (SearchException e) {
+        log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
+      }
+    }
+    return explanation;
+  }
+
 /**
  * Execute the lucene search and return the machting hits.
  *
@@(protected) @@
  * @return The lucene hits.
  * @throws IOException in case there is an error executing the lucene search.
  */
-  private Hits getHits(Searcher searcher) throws IOException {
+  private QueryAndHits getQueryAndHits(Searcher searcher) throws IOException {
   Hits hits;
   org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
   buildFilters();
   hits = searcher.search( query, filter, sort );
   setResultSize( hits );
-    return hits;
+    return new QueryAndHits( query, hits );
 }

 private void buildFilters() {
@@(protected) @@
     else {
       Hits hits;
       try {
-          hits = getHits( searcher );
+          hits = getQueryAndHits( searcher ).hits;
         resultSize = hits.length();
       }
       catch (IOException e) {
@@(protected) @@
     throw new UnsupportedOperationException( "noLoader should not be used" );
   }
 };
+
+  private static class QueryAndHits {
+    private QueryAndHits(org.apache.lucene.search.Query preparedQuery, Hits hits) {
+      this.preparedQuery = preparedQuery;
+      this.hits = hits;
+    }
+
+    public final org.apache.lucene.search.Query preparedQuery;
+    public final Hits hits;
+  }
}

Added: search/trunk/src/test/org/hibernate/search/test/query/explain/Dvd.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/query/explain/Dvd.java                  (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/query/explain/Dvd.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@
+package org.hibernate.search.test.query.explain;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@(protected)
+@(protected)
+public class Dvd {
+  @Id @GeneratedValue @DocumentId private Integer id;
+  private @Field String title;
+  private @Field String description;
+
+  protected Dvd() {
+  }
+
+  public Dvd(String title, String description) {
+    this.title = title;
+    this.description = description;
+  }
+
+  public Integer getId() {
+    return id;
+  }
+
+  public void setId(Integer id) {
+    this.id = id;
+  }
+
+  public String getTitle() {
+    return title;
+  }
+
+  public void setTitle(String title) {
+    this.title = title;
+  }
+
+  public String getDescription() {
+    return description;
+  }
+
+  public void setDescription(String description) {
+    this.description = description;
+  }
+}

Added: search/trunk/src/test/org/hibernate/search/test/query/explain/ExplanationTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/query/explain/ExplanationTest.java                  (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/query/explain/ExplanationTest.java  2008-07-17 00:15:47 UTC (rev 14940)
@@(protected) @@
+package org.hibernate.search.test.query.explain;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.FullTextQuery;
+import org.hibernate.Transaction;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.queryParser.MultiFieldQueryParser;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ExplanationTest extends SearchTestCase {
+  public void testExplanation() throws Exception {
+    FullTextSession s = Search.getFullTextSession( openSession() );
+    Transaction tx = s.beginTransaction();
+    Dvd dvd = new Dvd("The dark knight", "Batman returns with it best enomy the Jocker. The dark side of this movies shows up pretty quickly");
+    s.persist( dvd );
+    dvd = new Dvd("Wall-e", "The tiny little robot comes to Eartch after the dark times and tries to clean it");
+    s.persist( dvd );
+    tx.commit();
+    s.clear();
+
+    tx = s.beginTransaction();
+    Map<String, Float> boosts = new HashMap<String, Float>(2);
+    boosts.put( "title", new Float(4) );
+    boosts.put( "description", new Float(1) );
+    MultiFieldQueryParser parser = new MultiFieldQueryParser(new String[] {"title", "description"}, new StandardAnalyzer(), boosts);
+    Query luceneQuery = parser.parse( "dark" );
+    FullTextQuery ftQuery = s.createFullTextQuery( luceneQuery, Dvd.class )
+        .setProjection( FullTextQuery.DOCUMENT_ID, FullTextQuery.EXPLANATION, FullTextQuery.THIS );
+    @SuppressWarnings("unchecked") List<Object[]> results = ftQuery.list();
+    assertEquals( 2, results.size() );
+    for (Object[] result : results) {
+      assertEquals( ftQuery.explain( (Integer) result[0] ).toString(), result[1].toString() );
+      s.delete( result[2] );
+    }
+    tx.commit();
+    s.close();
+
+  }
+  protected Class[] getMappings() {
+    return new Class[] {
+        Dvd.class
+    };
+  }
+}

_______________________________________________
hibernate-commits mailing list
hibernate-commits@(protected)
https://lists.jboss.org/mailman/listinfo/hibernate-commits
©2008 gg3721.com - Jax Systems, LLC, U.S.A.