[hibernate-commits] Hibernate SVN: r14940 - in search/trunk/src: java/org/hibernate/search/engine and 5 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Wed Jul 16 20:15:47 EDT 2008
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)
@@ -3,6 +3,7 @@
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;
@@ -78,6 +79,16 @@
void disableFullTextFilter(String name);
/**
+ * Return the Lucene {@link org.apache.lucene.search.Explanation}
+ * 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)
@@ -36,6 +36,16 @@
*/
public String DOCUMENT_ID = "__HSearch_DocumentId";
/**
+ * Lucene {@link org.apache.lucene.search.Explanation} object describing the score computation for
+ * 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 {@link #DOCUMENT_ID}
+ * 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)
@@ -6,6 +6,8 @@
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;
@@ -16,10 +18,14 @@
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) {
@@ -56,6 +62,9 @@
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)
@@ -5,6 +5,7 @@
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;
@@ -89,4 +90,14 @@
*
*/
FullTextQuery setResultTransformer(ResultTransformer transformer);
+
+ /**
+ * Return the Lucene {@link org.apache.lucene.search.Explanation}
+ * 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)
@@ -19,6 +19,7 @@
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;
@@ -227,6 +228,10 @@
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)
@@ -25,6 +25,7 @@
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;
@@ -129,17 +130,17 @@
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 );
@@ -207,14 +208,13 @@
//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
@@ -238,18 +238,17 @@
//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()] ) );
@@ -274,6 +273,34 @@
}
}
+ 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.
*
@@ -281,13 +308,13 @@
* @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() {
@@ -600,7 +627,7 @@
else {
Hits hits;
try {
- hits = getHits( searcher );
+ hits = getQueryAndHits( searcher ).hits;
resultSize = hits.length();
}
catch (IOException e) {
@@ -720,4 +747,14 @@
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)
@@ -0,0 +1,52 @@
+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
+ */
+ at Entity
+ at Indexed
+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)
@@ -0,0 +1,55 @@
+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
+ };
+ }
+}
More information about the hibernate-commits
mailing list