pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / backwards / src / test / org / apache / lucene / search / TestBooleanMinShouldMatch.java
diff --git a/lucene-java-3.5.0/lucene/backwards/src/test/org/apache/lucene/search/TestBooleanMinShouldMatch.java b/lucene-java-3.5.0/lucene/backwards/src/test/org/apache/lucene/search/TestBooleanMinShouldMatch.java
new file mode 100644 (file)
index 0000000..0bf05ad
--- /dev/null
@@ -0,0 +1,390 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.store.Directory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+
+import java.text.DecimalFormat;
+import java.util.Random;
+
+/** Test that BooleanQuery.setMinimumNumberShouldMatch works.
+ */
+public class TestBooleanMinShouldMatch extends LuceneTestCase {
+
+    private static Directory index;
+    private static IndexReader r;
+    private static IndexSearcher s;
+
+    @BeforeClass
+    public static void beforeClass() throws Exception {
+        String[] data = new String [] {
+            "A 1 2 3 4 5 6",
+            "Z       4 5 6",
+            null,
+            "B   2   4 5 6",
+            "Y     3   5 6",
+            null,
+            "C     3     6",
+            "X       4 5 6"
+        };
+
+        index = newDirectory();
+        RandomIndexWriter w = new RandomIndexWriter(random, index);
+
+        for (int i = 0; i < data.length; i++) {
+            Document doc = new Document();
+            doc.add(newField("id", String.valueOf(i), Field.Store.YES, Field.Index.NOT_ANALYZED));//Field.Keyword("id",String.valueOf(i)));
+            doc.add(newField("all", "all", Field.Store.YES, Field.Index.NOT_ANALYZED));//Field.Keyword("all","all"));
+            if (null != data[i]) {
+                doc.add(newField("data", data[i], Field.Store.YES, Field.Index.ANALYZED));//Field.Text("data",data[i]));
+            }
+            w.addDocument(doc);
+        }
+
+        r = w.getReader();
+        s = newSearcher(r);
+        w.close();
+//System.out.println("Set up " + getName());
+    }
+    
+    @AfterClass
+    public static void afterClass() throws Exception {
+      s.close();
+      s = null;
+      r.close();
+      r = null;
+      index.close();
+      index = null;
+    }
+
+
+    public void verifyNrHits(Query q, int expected) throws Exception {
+        ScoreDoc[] h = s.search(q, null, 1000).scoreDocs;
+        if (expected != h.length) {
+            printHits(getName(), h, s);
+        }
+        assertEquals("result count", expected, h.length);
+        QueryUtils.check(random, q,s);
+    }
+
+    public void testAllOptional() throws Exception {
+
+        BooleanQuery q = new BooleanQuery();
+        for (int i = 1; i <=4; i++) {
+            q.add(new TermQuery(new Term("data",""+i)), BooleanClause.Occur.SHOULD);//false, false);
+        }
+        q.setMinimumNumberShouldMatch(2); // match at least two of 4
+        verifyNrHits(q, 2);
+    }
+
+    public void testOneReqAndSomeOptional() throws Exception {
+
+        /* one required, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.SHOULD);//false, false);
+
+        q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
+
+        verifyNrHits(q, 5);
+    }
+
+    public void testSomeReqAndSomeOptional() throws Exception {
+
+        /* two required, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.SHOULD);//false, false);
+
+        q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
+
+        verifyNrHits(q, 5);
+    }
+
+    public void testOneProhibAndSomeOptional() throws Exception {
+
+        /* one prohibited, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+
+        q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testSomeProhibAndSomeOptional() throws Exception {
+
+        /* two prohibited, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+
+        q.setMinimumNumberShouldMatch(2); // 2 of 3 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testOneReqOneProhibAndSomeOptional() throws Exception {
+
+        /* one required, one prohibited, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);// true,  false);
+        q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
+
+        q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testSomeReqOneProhibAndSomeOptional() throws Exception {
+
+        /* two required, one prohibited, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all",  "all")), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
+
+        q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testOneReqSomeProhibAndSomeOptional() throws Exception {
+
+        /* one required, two prohibited, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+
+        q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testSomeReqSomeProhibAndSomeOptional() throws Exception {
+
+        /* two required, two prohibited, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all",  "all")), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+
+        q.setMinimumNumberShouldMatch(3); // 3 of 4 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testMinHigherThenNumOptional() throws Exception {
+
+        /* two required, two prohibited, some optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all",  "all")), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "5"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "4"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "1"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "C"  )), BooleanClause.Occur.MUST_NOT);//false, true );
+
+        q.setMinimumNumberShouldMatch(90); // 90 of 4 optional ?!?!?!
+
+        verifyNrHits(q, 0);
+    }
+
+    public void testMinEqualToNumOptional() throws Exception {
+
+        /* two required, two optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "6"  )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.SHOULD);//false, false);
+
+        q.setMinimumNumberShouldMatch(2); // 2 of 2 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testOneOptionalEqualToMin() throws Exception {
+
+        /* two required, one optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "3"  )), BooleanClause.Occur.SHOULD);//false, false);
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.MUST);//true,  false);
+
+        q.setMinimumNumberShouldMatch(1); // 1 of 1 optional 
+
+        verifyNrHits(q, 1);
+    }
+
+    public void testNoOptionalButMin() throws Exception {
+
+        /* two required, no optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
+        q.add(new TermQuery(new Term("data", "2"  )), BooleanClause.Occur.MUST);//true,  false);
+
+        q.setMinimumNumberShouldMatch(1); // 1 of 0 optional 
+
+        verifyNrHits(q, 0);
+    }
+
+    public void testNoOptionalButMin2() throws Exception {
+
+        /* one required, no optional */
+        BooleanQuery q = new BooleanQuery();
+        q.add(new TermQuery(new Term("all", "all" )), BooleanClause.Occur.MUST);//true,  false);
+
+        q.setMinimumNumberShouldMatch(1); // 1 of 0 optional 
+
+        verifyNrHits(q, 0);
+    }
+
+    public void testRandomQueries() throws Exception {
+      String field="data";
+      String[] vals = {"1","2","3","4","5","6","A","Z","B","Y","Z","X","foo"};
+      int maxLev=4;
+
+      // callback object to set a random setMinimumNumberShouldMatch
+      TestBoolean2.Callback minNrCB = new TestBoolean2.Callback() {
+        public void postCreate(BooleanQuery q) {
+          BooleanClause[] c =q.getClauses();
+          int opt=0;
+          for (int i=0; i<c.length;i++) {
+            if (c[i].getOccur() == BooleanClause.Occur.SHOULD) opt++;
+          }
+          q.setMinimumNumberShouldMatch(random.nextInt(opt+2));
+        }
+      };
+
+
+
+      // increase number of iterations for more complete testing      
+      int num = atLeast(10);
+      for (int i = 0; i < num; i++) {
+        int lev = random.nextInt(maxLev);
+        final long seed = random.nextLong();
+        BooleanQuery q1 = TestBoolean2.randBoolQuery(new Random(seed), true, lev, field, vals, null);
+        // BooleanQuery q2 = TestBoolean2.randBoolQuery(new Random(seed), lev, field, vals, minNrCB);
+        BooleanQuery q2 = TestBoolean2.randBoolQuery(new Random(seed), true, lev, field, vals, null);
+        // only set minimumNumberShouldMatch on the top level query since setting
+        // at a lower level can change the score.
+        minNrCB.postCreate(q2);
+
+        // Can't use Hits because normalized scores will mess things
+        // up.  The non-sorting version of search() that returns TopDocs
+        // will not normalize scores.
+        TopDocs top1 = s.search(q1,null,100);
+        TopDocs top2 = s.search(q2,null,100);
+        if (i < 100) {
+          QueryUtils.check(random, q1,s);
+          QueryUtils.check(random, q2,s);
+        }
+        // The constrained query
+        // should be a superset to the unconstrained query.
+        if (top2.totalHits > top1.totalHits) {
+          fail("Constrained results not a subset:\n"
+                        + CheckHits.topdocsString(top1,0,0)
+                        + CheckHits.topdocsString(top2,0,0)
+                        + "for query:" + q2.toString());
+        }
+
+        for (int hit=0; hit<top2.totalHits; hit++) {
+          int id = top2.scoreDocs[hit].doc;
+          float score = top2.scoreDocs[hit].score;
+          boolean found=false;
+          // find this doc in other hits
+          for (int other=0; other<top1.totalHits; other++) {
+            if (top1.scoreDocs[other].doc == id) {
+              found=true;
+              float otherScore = top1.scoreDocs[other].score;
+              // check if scores match
+              if (Math.abs(otherScore-score)>1.0e-6f) {
+                        fail("Doc " + id + " scores don't match\n"
+                + CheckHits.topdocsString(top1,0,0)
+                + CheckHits.topdocsString(top2,0,0)
+                + "for query:" + q2.toString());
+              }
+            }
+          }
+
+          // check if subset
+          if (!found) fail("Doc " + id + " not found\n"
+                + CheckHits.topdocsString(top1,0,0)
+                + CheckHits.topdocsString(top2,0,0)
+                + "for query:" + q2.toString());
+        }
+      }
+      // System.out.println("Total hits:"+tot);
+    }
+
+
+
+    protected void printHits(String test, ScoreDoc[] h, Searcher searcher) throws Exception {
+
+        System.err.println("------- " + test + " -------");
+
+        DecimalFormat f = new DecimalFormat("0.000000");
+
+        for (int i = 0; i < h.length; i++) {
+            Document d = searcher.doc(h[i].doc);
+            float score = h[i].score;
+            System.err.println("#" + i + ": " + f.format(score) + " - " +
+                               d.get("id") + " - " + d.get("data"));
+        }
+    }
+}