[mb-commits] r13653 - search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet

[email protected] Thu, 04 Apr 2013 09:53:56 +0000
Newsgroups gmane.comp.audio.musicbrainz.cvs
Message-ID <E1UNgrk-0000py-CL@wiley>
Author: ijabz
Date: 2013-04-04 09:53:55 +0000 (Thu, 04 Apr 2013)
New Revision: 13653

Modified:
   search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/DismaxQueryParser.java
Log:
SEARCH-280:Fixed fuzzy searches scoring better than exact matches.

Modified: search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/DismaxQueryParser.java
===================================================================
--- search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/DismaxQueryParser.java	2013-04-03 20:44:21 UTC (rev 13652)
+++ search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/DismaxQueryParser.java	2013-04-04 09:53:55 UTC (rev 13653)
@@ -31,11 +31,11 @@
 
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.Term;
+import org.apache.lucene.index.TermContext;
 import org.apache.lucene.queryparser.classic.ParseException;
 import org.apache.lucene.queryparser.classic.QueryParser;
 import org.apache.lucene.search.*;
 import org.apache.lucene.search.similarities.DefaultSimilarity;
-import org.apache.lucene.search.similarities.Similarity;
 import org.apache.lucene.search.similarities.TFIDFSimilarity;
 import org.musicbrainz.search.LuceneVersion;
 
@@ -55,7 +55,7 @@
     public DismaxQueryParser(org.apache.lucene.analysis.Analyzer analyzer) {
         dqp = new DisjunctionQueryParser(IMPOSSIBLE_FIELD_NAME, analyzer);
         //TODO FIXME
-        //dqp.setMultiTermRewriteMethod(new MultiTermUseIdfOfSearchTerm(100));
+        //dqp.setMultiTermRewriteMethod(new FuzzyTermRewrite(100));
     }
 
     /**
@@ -123,11 +123,11 @@
             aliases.put(field, dismaxAlias);
         }
 
-        // TODO FIXME _ Unable to create rewrite using original idf
         // Rewrite Method used by Prefix Search and Fuzzy Search, use idf of the original term
-        //MultiTermQuery.RewriteMethod fuzzyAndPrefixQueryRewriteMethod
-        //        = new MultiTermUseIdfOfSearchTerm(200);
+        MultiTermQuery.RewriteMethod prefixRewrite = new PrefixTermRewrite(200);
+        MultiTermQuery.RewriteMethod fuzzyRewrite = new FuzzyTermRewrite(200);
 
+
         protected boolean checkQuery(DisjunctionMaxQuery q, Query querySub, boolean quoted, DismaxAlias a, String f) {
             if (querySub != null) {
                 //if query was quoted but doesn't generate a phrase query we reject it
@@ -146,12 +146,11 @@
         }
 
         @Override
-        //TODO FIXME was using a FLOAT similarity value of 0.5 but now chnaged to integral
+        //TODO FIXME was using a FLOAT similarity value of 0.5 but now changed to integral
         protected Query getFuzzyQuery(String field, String termStr, float minSimilarity) {
             Term t = new Term(field, termStr);
             FuzzyQuery fq = new FuzzyQuery(t,  2, MIN_FIELD_LENGTH_TO_MAKE_FUZZY);
-            //TODO FIXME
-            //fq.setRewriteMethod(fuzzyAndPrefixQueryRewriteMethod);
+            fq.setRewriteMethod(fuzzyRewrite);
             return fq;
         }
 
@@ -216,20 +215,62 @@
          */
         protected Query newPrefixQuery(Term prefix){
             PrefixQuery query = new PrefixQuery(prefix);
-            //TODO FIXME
-            //query.setRewriteMethod(fuzzyAndPrefixQueryRewriteMethod);
+            query.setRewriteMethod(prefixRewrite);
             return query;
         }
     }
 
-    /*
-    TODO FIXME WAS Overriding methods that are now final
-    public static class MultiTermUseIdfOfSearchTerm<Q extends DisjunctionMaxQuery> extends TopTermsRewrite<Query> {
+    /**
+     * Fuzzy matches are rewritten to a DisjunctionMaxQuery instead of the more usual BooleanQuery so that
+     * if search term matches multiple fields we just take the best field rather summing all matches like a boolean
+     * query. The 0.1 for tiebreaker is to favour documents that contain all words rather than the same word in multiple
+     * fields.
+     *
+     * We use a constant score otherwise fuzzy matches could get higher scores then an exact match
+     *
+     * @param <Q>
+     */
+    public static class FuzzyTermRewrite<Q extends DisjunctionMaxQuery> extends TopTermsRewrite<Query> {
 
-    //public static final class MultiTermUseIdfOfSearchTerm extends TopTermsRewrite<BooleanQuery> {
+        public FuzzyTermRewrite(int size) {
+            super(size);
+        }
+
+        @Override
+        protected int getMaxSize() {
+            return BooleanQuery.getMaxClauseCount();
+        }
+
+        @Override
+        protected DisjunctionMaxQuery getTopLevelQuery() {
+            return new DisjunctionMaxQuery(0.1f);
+        }
+
+        @Override
+        protected void addClause(Query topLevel, Term term, int docCount, float boost, TermContext states) {
+            final Query tq = new ConstantScoreQuery(new TermQuery(term, states));
+            tq.setBoost(boost);
+            ((DisjunctionMaxQuery)topLevel).add(tq);
+        }
+    }
+
+    /**
+     *
+     * Prefix matches are rewritten to a DisjunctionMaxQuery instead of the more usual BooleanQuery so that
+     * if search term matches multiple fields we just take the best field rather summing all matches like a boolean
+     * query. The 0.1 for tiebreaker is to favour documents that contain all words rather than the same word in multiple
+     * fields.
+     *
+     * We set the idf the same as an exact match so that a wildcard match to a term which happens to be rarer than
+     * the exact term we were searching for does not get an unfairly high idf.
+     *
+     * @param <Q>
+     */
+    public static class PrefixTermRewrite<Q extends DisjunctionMaxQuery> extends TopTermsRewrite<Query> {
+
         private final TFIDFSimilarity similarity;
 
-        public MultiTermUseIdfOfSearchTerm(int size) {
+        public PrefixTermRewrite(int size) {
             super(size);
             this.similarity = new DefaultSimilarity();
 
@@ -246,8 +287,8 @@
         }
 
         @Override
-        protected void addClause(Query topLevel, Term term, float boost) {
-            final Query tq = new ConstantScoreQuery(new TermQuery(term));
+        protected void addClause(Query topLevel, Term term, int docCount, float boost, TermContext states) {
+            final Query tq = new ConstantScoreQuery(new TermQuery(term, states));
             tq.setBoost(boost);
             ((DisjunctionMaxQuery)topLevel).add(tq);
         }
@@ -256,19 +297,17 @@
                 throws IOException {
             float idf = 1f;
             float df;
-            if (query instanceof PrefixQuery)
+            PrefixQuery fq = (PrefixQuery) query;
+            df = reader.docFreq(fq.getPrefix());
+            if(df>=1)
             {
-                PrefixQuery fq = (PrefixQuery) query;
-                df = reader.docFreq(fq.getPrefix());
-                if(df>=1)
-                {
-                    //Same as idf value for search term, 0.5 acts as length norm
-                    idf = (float)Math.pow(similarity.idf((int) df, reader.numDocs()),2) * 0.5f;
-                }
+                //Same as idf value for search term, 0.5 acts as length norm
+                idf = (float)Math.pow(similarity.idf((int) df, reader.numDocs()),2) * 0.5f;
             }
             return idf;
         }
 
+        /*
         @Override
         public Query rewrite(final IndexReader reader, final MultiTermQuery query) throws IOException {
             DisjunctionMaxQuery  bq = (DisjunctionMaxQuery)super.rewrite(reader, query);
@@ -281,8 +320,9 @@
                 next.setBoost(next.getBoost() * idfBoost);
             }
             return bq;
-        }
+        } */
 
     }
-    */
+
+
 }
\ No newline at end of file