| /* |
| * 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. |
| */ |
| package org.apache.lucene.search; |
| |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.BitSet; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import com.carrotsearch.randomizedtesting.generators.RandomPicks; |
| import org.apache.lucene.analysis.Analyzer; |
| import org.apache.lucene.analysis.MockAnalyzer; |
| import org.apache.lucene.document.Document; |
| import org.apache.lucene.document.Field; |
| import org.apache.lucene.document.TextField; |
| import org.apache.lucene.index.DirectoryReader; |
| import org.apache.lucene.index.IndexReader; |
| import org.apache.lucene.index.IndexWriter; |
| import org.apache.lucene.index.IndexWriterConfig; |
| import org.apache.lucene.index.LeafReaderContext; |
| import org.apache.lucene.index.MultiReader; |
| import org.apache.lucene.index.RandomIndexWriter; |
| import org.apache.lucene.index.Term; |
| import org.apache.lucene.search.BooleanClause.Occur; |
| import org.apache.lucene.search.similarities.ClassicSimilarity; |
| import org.apache.lucene.search.spans.SpanQuery; |
| import org.apache.lucene.search.spans.SpanTermQuery; |
| import org.apache.lucene.store.Directory; |
| import org.apache.lucene.util.LuceneTestCase; |
| import org.apache.lucene.util.NamedThreadFactory; |
| import org.apache.lucene.util.TestUtil; |
| |
| public class TestBooleanQuery extends LuceneTestCase { |
| |
| public void testEquality() throws Exception { |
| BooleanQuery.Builder bq1 = new BooleanQuery.Builder(); |
| bq1.add(new TermQuery(new Term("field", "value1")), BooleanClause.Occur.SHOULD); |
| bq1.add(new TermQuery(new Term("field", "value2")), BooleanClause.Occur.SHOULD); |
| BooleanQuery.Builder nested1 = new BooleanQuery.Builder(); |
| nested1.add(new TermQuery(new Term("field", "nestedvalue1")), BooleanClause.Occur.SHOULD); |
| nested1.add(new TermQuery(new Term("field", "nestedvalue2")), BooleanClause.Occur.SHOULD); |
| bq1.add(nested1.build(), BooleanClause.Occur.SHOULD); |
| |
| BooleanQuery.Builder bq2 = new BooleanQuery.Builder(); |
| bq2.add(new TermQuery(new Term("field", "value1")), BooleanClause.Occur.SHOULD); |
| bq2.add(new TermQuery(new Term("field", "value2")), BooleanClause.Occur.SHOULD); |
| BooleanQuery.Builder nested2 = new BooleanQuery.Builder(); |
| nested2.add(new TermQuery(new Term("field", "nestedvalue1")), BooleanClause.Occur.SHOULD); |
| nested2.add(new TermQuery(new Term("field", "nestedvalue2")), BooleanClause.Occur.SHOULD); |
| bq2.add(nested2.build(), BooleanClause.Occur.SHOULD); |
| |
| assertEquals(bq1.build(), bq2.build()); |
| } |
| |
| public void testEqualityDoesNotDependOnOrder() { |
| TermQuery[] queries = new TermQuery[] { |
| new TermQuery(new Term("foo", "bar")), |
| new TermQuery(new Term("foo", "baz")) |
| }; |
| for (int iter = 0; iter < 10; ++iter) { |
| List<BooleanClause> clauses = new ArrayList<>(); |
| final int numClauses = random().nextInt(20); |
| for (int i = 0; i < numClauses; ++i) { |
| Query query = RandomPicks.randomFrom(random(), queries); |
| if (random().nextBoolean()) { |
| query = new BoostQuery(query, random().nextFloat()); |
| } |
| Occur occur = RandomPicks.randomFrom(random(), Occur.values()); |
| clauses.add(new BooleanClause(query, occur)); |
| } |
| |
| final int minShouldMatch = random().nextInt(5); |
| BooleanQuery.Builder bq1Builder = new BooleanQuery.Builder(); |
| bq1Builder.setMinimumNumberShouldMatch(minShouldMatch); |
| for (BooleanClause clause : clauses) { |
| bq1Builder.add(clause); |
| } |
| final BooleanQuery bq1 = bq1Builder.build(); |
| |
| Collections.shuffle(clauses, random()); |
| BooleanQuery.Builder bq2Builder = new BooleanQuery.Builder(); |
| bq2Builder.setMinimumNumberShouldMatch(minShouldMatch); |
| for (BooleanClause clause : clauses) { |
| bq2Builder.add(clause); |
| } |
| final BooleanQuery bq2 = bq2Builder.build(); |
| |
| QueryUtils.checkEqual(bq1, bq2); |
| } |
| } |
| |
| public void testEqualityOnDuplicateShouldClauses() { |
| BooleanQuery bq1 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(random().nextInt(2)) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.SHOULD) |
| .build(); |
| BooleanQuery bq2 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(bq1.getMinimumNumberShouldMatch()) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.SHOULD) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.SHOULD) |
| .build(); |
| QueryUtils.checkUnequal(bq1, bq2); |
| } |
| |
| public void testEqualityOnDuplicateMustClauses() { |
| BooleanQuery bq1 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(random().nextInt(2)) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.MUST) |
| .build(); |
| BooleanQuery bq2 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(bq1.getMinimumNumberShouldMatch()) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.MUST) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.MUST) |
| .build(); |
| QueryUtils.checkUnequal(bq1, bq2); |
| } |
| |
| public void testEqualityOnDuplicateFilterClauses() { |
| BooleanQuery bq1 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(random().nextInt(2)) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.FILTER) |
| .build(); |
| BooleanQuery bq2 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(bq1.getMinimumNumberShouldMatch()) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.FILTER) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.FILTER) |
| .build(); |
| QueryUtils.checkEqual(bq1, bq2); |
| } |
| |
| public void testEqualityOnDuplicateMustNotClauses() { |
| BooleanQuery bq1 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(random().nextInt(2)) |
| .add(new MatchAllDocsQuery(), Occur.MUST) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.FILTER) |
| .build(); |
| BooleanQuery bq2 = new BooleanQuery.Builder() |
| .setMinimumNumberShouldMatch(bq1.getMinimumNumberShouldMatch()) |
| .add(new MatchAllDocsQuery(), Occur.MUST) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.FILTER) |
| .add(new TermQuery(new Term("foo", "bar")), Occur.FILTER) |
| .build(); |
| QueryUtils.checkEqual(bq1, bq2); |
| } |
| |
| public void testHashCodeIsStable() { |
| BooleanQuery bq = new BooleanQuery.Builder() |
| .add(new TermQuery(new Term("foo", TestUtil.randomSimpleString(random()))), Occur.SHOULD) |
| .add(new TermQuery(new Term("foo", TestUtil.randomSimpleString(random()))), Occur.SHOULD) |
| .build(); |
| final int hashCode = bq.hashCode(); |
| assertEquals(hashCode, bq.hashCode()); |
| } |
| |
| public void testException() { |
| expectThrows(IllegalArgumentException.class, () -> { |
| BooleanQuery.setMaxClauseCount(0); |
| }); |
| } |
| |
| // LUCENE-1630 |
| public void testNullOrSubScorer() throws Throwable { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| doc.add(newTextField("field", "a b c d", Field.Store.NO)); |
| w.addDocument(doc); |
| |
| IndexReader r = w.getReader(); |
| IndexSearcher s = newSearcher(r); |
| // this test relies upon coord being the default implementation, |
| // otherwise scores are different! |
| s.setSimilarity(new ClassicSimilarity()); |
| |
| BooleanQuery.Builder q = new BooleanQuery.Builder(); |
| q.add(new TermQuery(new Term("field", "a")), BooleanClause.Occur.SHOULD); |
| |
| // PhraseQuery w/ no terms added returns a null scorer |
| PhraseQuery pq = new PhraseQuery("field", new String[0]); |
| q.add(pq, BooleanClause.Occur.SHOULD); |
| assertEquals(1, s.search(q.build(), 10).totalHits.value); |
| |
| // A required clause which returns null scorer should return null scorer to |
| // IndexSearcher. |
| q = new BooleanQuery.Builder(); |
| pq = new PhraseQuery("field", new String[0]); |
| q.add(new TermQuery(new Term("field", "a")), BooleanClause.Occur.SHOULD); |
| q.add(pq, BooleanClause.Occur.MUST); |
| assertEquals(0, s.search(q.build(), 10).totalHits.value); |
| |
| DisjunctionMaxQuery dmq = new DisjunctionMaxQuery( |
| Arrays.asList(new TermQuery(new Term("field", "a")), pq), |
| 1.0f); |
| assertEquals(1, s.search(dmq, 10).totalHits.value); |
| |
| r.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testDeMorgan() throws Exception { |
| Directory dir1 = newDirectory(); |
| RandomIndexWriter iw1 = new RandomIndexWriter(random(), dir1); |
| Document doc1 = new Document(); |
| doc1.add(newTextField("field", "foo bar", Field.Store.NO)); |
| iw1.addDocument(doc1); |
| IndexReader reader1 = iw1.getReader(); |
| iw1.close(); |
| |
| Directory dir2 = newDirectory(); |
| RandomIndexWriter iw2 = new RandomIndexWriter(random(), dir2); |
| Document doc2 = new Document(); |
| doc2.add(newTextField("field", "foo baz", Field.Store.NO)); |
| iw2.addDocument(doc2); |
| IndexReader reader2 = iw2.getReader(); |
| iw2.close(); |
| |
| BooleanQuery.Builder query = new BooleanQuery.Builder(); // Query: +foo -ba* |
| query.add(new TermQuery(new Term("field", "foo")), BooleanClause.Occur.MUST); |
| WildcardQuery wildcardQuery = new WildcardQuery(new Term("field", "ba*")); |
| wildcardQuery.setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_REWRITE); |
| query.add(wildcardQuery, BooleanClause.Occur.MUST_NOT); |
| |
| MultiReader multireader = new MultiReader(reader1, reader2); |
| IndexSearcher searcher = newSearcher(multireader); |
| assertEquals(0, searcher.search(query.build(), 10).totalHits.value); |
| |
| final ExecutorService es = Executors.newCachedThreadPool(new NamedThreadFactory("NRT search threads")); |
| searcher = new IndexSearcher(multireader, es); |
| if (VERBOSE) |
| System.out.println("rewritten form: " + searcher.rewrite(query.build())); |
| assertEquals(0, searcher.search(query.build(), 10).totalHits.value); |
| es.shutdown(); |
| es.awaitTermination(1, TimeUnit.SECONDS); |
| |
| multireader.close(); |
| reader1.close(); |
| reader2.close(); |
| dir1.close(); |
| dir2.close(); |
| } |
| |
| public void testBS2DisjunctionNextVsAdvance() throws Exception { |
| final Directory d = newDirectory(); |
| final RandomIndexWriter w = new RandomIndexWriter(random(), d); |
| final int numDocs = atLeast(300); |
| for(int docUpto=0;docUpto<numDocs;docUpto++) { |
| String contents = "a"; |
| if (random().nextInt(20) <= 16) { |
| contents += " b"; |
| } |
| if (random().nextInt(20) <= 8) { |
| contents += " c"; |
| } |
| if (random().nextInt(20) <= 4) { |
| contents += " d"; |
| } |
| if (random().nextInt(20) <= 2) { |
| contents += " e"; |
| } |
| if (random().nextInt(20) <= 1) { |
| contents += " f"; |
| } |
| Document doc = new Document(); |
| doc.add(new TextField("field", contents, Field.Store.NO)); |
| w.addDocument(doc); |
| } |
| w.forceMerge(1); |
| final IndexReader r = w.getReader(); |
| final IndexSearcher s = newSearcher(r); |
| w.close(); |
| |
| for(int iter=0;iter<10*RANDOM_MULTIPLIER;iter++) { |
| if (VERBOSE) { |
| System.out.println("iter=" + iter); |
| } |
| final List<String> terms = new ArrayList<>(Arrays.asList("a", "b", "c", "d", "e", "f")); |
| final int numTerms = TestUtil.nextInt(random(), 1, terms.size()); |
| while(terms.size() > numTerms) { |
| terms.remove(random().nextInt(terms.size())); |
| } |
| |
| if (VERBOSE) { |
| System.out.println(" terms=" + terms); |
| } |
| |
| final BooleanQuery.Builder q = new BooleanQuery.Builder(); |
| for(String term : terms) { |
| q.add(new BooleanClause(new TermQuery(new Term("field", term)), BooleanClause.Occur.SHOULD)); |
| } |
| |
| Weight weight = s.createWeight(s.rewrite(q.build()), ScoreMode.COMPLETE, 1); |
| |
| Scorer scorer = weight.scorer(s.leafContexts.get(0)); |
| |
| // First pass: just use .nextDoc() to gather all hits |
| final List<ScoreDoc> hits = new ArrayList<>(); |
| while(scorer.iterator().nextDoc() != DocIdSetIterator.NO_MORE_DOCS) { |
| hits.add(new ScoreDoc(scorer.docID(), scorer.score())); |
| } |
| |
| if (VERBOSE) { |
| System.out.println(" " + hits.size() + " hits"); |
| } |
| |
| // Now, randomly next/advance through the list and |
| // verify exact match: |
| for(int iter2=0;iter2<10;iter2++) { |
| |
| weight = s.createWeight(s.rewrite(q.build()), ScoreMode.COMPLETE, 1); |
| scorer = weight.scorer(s.leafContexts.get(0)); |
| |
| if (VERBOSE) { |
| System.out.println(" iter2=" + iter2); |
| } |
| |
| int upto = -1; |
| while(upto < hits.size()) { |
| final int nextUpto; |
| final int nextDoc; |
| final int left = hits.size() - upto; |
| if (left == 1 || random().nextBoolean()) { |
| // next |
| nextUpto = 1+upto; |
| nextDoc = scorer.iterator().nextDoc(); |
| } else { |
| // advance |
| int inc = TestUtil.nextInt(random(), 1, left - 1); |
| nextUpto = inc + upto; |
| nextDoc = scorer.iterator().advance(hits.get(nextUpto).doc); |
| } |
| |
| if (nextUpto == hits.size()) { |
| assertEquals(DocIdSetIterator.NO_MORE_DOCS, nextDoc); |
| } else { |
| final ScoreDoc hit = hits.get(nextUpto); |
| assertEquals(hit.doc, nextDoc); |
| // Test for precise float equality: |
| assertTrue("doc " + hit.doc + " has wrong score: expected=" + hit.score + " actual=" + scorer.score(), hit.score == scorer.score()); |
| } |
| upto = nextUpto; |
| } |
| } |
| } |
| |
| r.close(); |
| d.close(); |
| } |
| |
| // LUCENE-4477 / LUCENE-4401: |
| public void testBooleanSpanQuery() throws Exception { |
| boolean failed = false; |
| int hits = 0; |
| Directory directory = newDirectory(); |
| Analyzer indexerAnalyzer = new MockAnalyzer(random()); |
| |
| IndexWriterConfig config = new IndexWriterConfig(indexerAnalyzer); |
| IndexWriter writer = new IndexWriter(directory, config); |
| String FIELD = "content"; |
| Document d = new Document(); |
| d.add(new TextField(FIELD, "clockwork orange", Field.Store.YES)); |
| writer.addDocument(d); |
| writer.close(); |
| |
| IndexReader indexReader = DirectoryReader.open(directory); |
| IndexSearcher searcher = newSearcher(indexReader); |
| |
| BooleanQuery.Builder query = new BooleanQuery.Builder(); |
| SpanQuery sq1 = new SpanTermQuery(new Term(FIELD, "clockwork")); |
| SpanQuery sq2 = new SpanTermQuery(new Term(FIELD, "clckwork")); |
| query.add(sq1, BooleanClause.Occur.SHOULD); |
| query.add(sq2, BooleanClause.Occur.SHOULD); |
| TopScoreDocCollector collector = TopScoreDocCollector.create(1000, Integer.MAX_VALUE); |
| searcher.search(query.build(), collector); |
| hits = collector.topDocs().scoreDocs.length; |
| for (ScoreDoc scoreDoc : collector.topDocs().scoreDocs){ |
| System.out.println(scoreDoc.doc); |
| } |
| indexReader.close(); |
| assertEquals("Bug in boolean query composed of span queries", failed, false); |
| assertEquals("Bug in boolean query composed of span queries", hits, 1); |
| directory.close(); |
| } |
| |
| public void testMinShouldMatchLeniency() throws Exception { |
| Directory dir = newDirectory(); |
| IndexWriter w = new IndexWriter(dir, newIndexWriterConfig(new MockAnalyzer(random()))); |
| Document doc = new Document(); |
| doc.add(newTextField("field", "a b c d", Field.Store.NO)); |
| w.addDocument(doc); |
| IndexReader r = DirectoryReader.open(w); |
| IndexSearcher s = newSearcher(r); |
| BooleanQuery.Builder bq = new BooleanQuery.Builder(); |
| bq.add(new TermQuery(new Term("field", "a")), BooleanClause.Occur.SHOULD); |
| bq.add(new TermQuery(new Term("field", "b")), BooleanClause.Occur.SHOULD); |
| |
| // No doc can match: BQ has only 2 clauses and we are asking for minShouldMatch=4 |
| bq.setMinimumNumberShouldMatch(4); |
| assertEquals(0, s.search(bq.build(), 1).totalHits.value); |
| r.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| private static BitSet getMatches(IndexSearcher searcher, Query query) throws IOException { |
| BitSet set = new BitSet(); |
| searcher.search(query, new SimpleCollector() { |
| int docBase = 0; |
| @Override |
| public ScoreMode scoreMode() { |
| return ScoreMode.COMPLETE_NO_SCORES; |
| } |
| @Override |
| protected void doSetNextReader(LeafReaderContext context) |
| throws IOException { |
| super.doSetNextReader(context); |
| docBase = context.docBase; |
| } |
| @Override |
| public void collect(int doc) throws IOException { |
| set.set(docBase + doc); |
| } |
| }); |
| return set; |
| } |
| |
| public void testFILTERClauseBehavesLikeMUST() throws IOException { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| Field f = newTextField("field", "a b c d", Field.Store.NO); |
| doc.add(f); |
| w.addDocument(doc); |
| f.setStringValue("b d"); |
| w.addDocument(doc); |
| f.setStringValue("d"); |
| w.addDocument(doc); |
| w.commit(); |
| |
| DirectoryReader reader = w.getReader(); |
| final IndexSearcher searcher = new IndexSearcher(reader); |
| |
| for (List<String> requiredTerms : Arrays.<List<String>>asList( |
| Arrays.asList("a", "d"), |
| Arrays.asList("a", "b", "d"), |
| Arrays.asList("d"), |
| Arrays.asList("e"), |
| Arrays.asList())) { |
| final BooleanQuery.Builder bq1 = new BooleanQuery.Builder(); |
| final BooleanQuery.Builder bq2 = new BooleanQuery.Builder(); |
| for (String term : requiredTerms) { |
| final Query q = new TermQuery(new Term("field", term)); |
| bq1.add(q, Occur.MUST); |
| bq2.add(q, Occur.FILTER); |
| } |
| |
| final BitSet matches1 = getMatches(searcher, bq1.build()); |
| final BitSet matches2 = getMatches(searcher, bq2.build()); |
| assertEquals(matches1, matches2); |
| } |
| |
| reader.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| private void assertSameScoresWithoutFilters(IndexSearcher searcher, BooleanQuery bq) throws IOException { |
| final BooleanQuery.Builder bq2Builder = new BooleanQuery.Builder(); |
| for (BooleanClause c : bq) { |
| if (c.getOccur() != Occur.FILTER) { |
| bq2Builder.add(c); |
| } |
| } |
| bq2Builder.setMinimumNumberShouldMatch(bq.getMinimumNumberShouldMatch()); |
| BooleanQuery bq2 = bq2Builder.build(); |
| |
| final AtomicBoolean matched = new AtomicBoolean(); |
| searcher.search(bq, new SimpleCollector() { |
| int docBase; |
| Scorable scorer; |
| |
| @Override |
| protected void doSetNextReader(LeafReaderContext context) |
| throws IOException { |
| super.doSetNextReader(context); |
| docBase = context.docBase; |
| } |
| |
| @Override |
| public ScoreMode scoreMode() { |
| return ScoreMode.COMPLETE; |
| } |
| |
| @Override |
| public void setScorer(Scorable scorer) throws IOException { |
| this.scorer = scorer; |
| } |
| |
| @Override |
| public void collect(int doc) throws IOException { |
| final float actualScore = scorer.score(); |
| final float expectedScore = searcher.explain(bq2, docBase + doc).getValue().floatValue(); |
| assertEquals(expectedScore, actualScore, 10e-5); |
| matched.set(true); |
| } |
| }); |
| assertTrue(matched.get()); |
| } |
| |
| public void testFilterClauseDoesNotImpactScore() throws IOException { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| Field f = newTextField("field", "a b c d", Field.Store.NO); |
| doc.add(f); |
| w.addDocument(doc); |
| f.setStringValue("b d"); |
| w.addDocument(doc); |
| f.setStringValue("a d"); |
| w.addDocument(doc); |
| w.commit(); |
| |
| DirectoryReader reader = w.getReader(); |
| final IndexSearcher searcher = new IndexSearcher(reader); |
| |
| BooleanQuery.Builder qBuilder = new BooleanQuery.Builder(); |
| BooleanQuery q = qBuilder.build(); |
| qBuilder.add(new TermQuery(new Term("field", "a")), Occur.FILTER); |
| |
| // With a single clause, we will rewrite to the underlying |
| // query. Make sure that it returns null scores |
| assertSameScoresWithoutFilters(searcher, qBuilder.build()); |
| |
| // Now with two clauses, we will get a conjunction scorer |
| // Make sure it returns null scores |
| qBuilder.add(new TermQuery(new Term("field", "b")), Occur.FILTER); |
| q = qBuilder.build(); |
| assertSameScoresWithoutFilters(searcher, q); |
| |
| // Now with a scoring clause, we need to make sure that |
| // the boolean scores are the same as those from the term |
| // query |
| qBuilder.add(new TermQuery(new Term("field", "c")), Occur.SHOULD); |
| q = qBuilder.build(); |
| assertSameScoresWithoutFilters(searcher, q); |
| |
| // FILTER and empty SHOULD |
| qBuilder = new BooleanQuery.Builder(); |
| qBuilder.add(new TermQuery(new Term("field", "a")), Occur.FILTER); |
| qBuilder.add(new TermQuery(new Term("field", "e")), Occur.SHOULD); |
| q = qBuilder.build(); |
| assertSameScoresWithoutFilters(searcher, q); |
| |
| // mix of FILTER and MUST |
| qBuilder = new BooleanQuery.Builder(); |
| qBuilder.add(new TermQuery(new Term("field", "a")), Occur.FILTER); |
| qBuilder.add(new TermQuery(new Term("field", "d")), Occur.MUST); |
| q = qBuilder.build(); |
| assertSameScoresWithoutFilters(searcher, q); |
| |
| // FILTER + minShouldMatch |
| qBuilder = new BooleanQuery.Builder(); |
| qBuilder.add(new TermQuery(new Term("field", "b")), Occur.FILTER); |
| qBuilder.add(new TermQuery(new Term("field", "a")), Occur.SHOULD); |
| qBuilder.add(new TermQuery(new Term("field", "d")), Occur.SHOULD); |
| qBuilder.setMinimumNumberShouldMatch(1); |
| q = qBuilder.build(); |
| assertSameScoresWithoutFilters(searcher, q); |
| |
| reader.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testConjunctionPropagatesApproximations() throws IOException { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| Field f = newTextField("field", "a b c", Field.Store.NO); |
| doc.add(f); |
| w.addDocument(doc); |
| w.commit(); |
| |
| DirectoryReader reader = w.getReader(); |
| // not LuceneTestCase.newSearcher to not have the asserting wrappers |
| // and do instanceof checks |
| final IndexSearcher searcher = new IndexSearcher(reader); |
| searcher.setQueryCache(null); // to still have approximations |
| |
| PhraseQuery pq = new PhraseQuery("field", "a", "b"); |
| |
| BooleanQuery.Builder q = new BooleanQuery.Builder(); |
| q.add(pq, Occur.MUST); |
| q.add(new TermQuery(new Term("field", "c")), Occur.FILTER); |
| |
| final Weight weight = searcher.createWeight(searcher.rewrite(q.build()), ScoreMode.COMPLETE, 1); |
| final Scorer scorer = weight.scorer(searcher.getIndexReader().leaves().get(0)); |
| assertTrue(scorer instanceof ConjunctionScorer); |
| assertNotNull(scorer.twoPhaseIterator()); |
| |
| reader.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testDisjunctionPropagatesApproximations() throws IOException { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| Field f = newTextField("field", "a b c", Field.Store.NO); |
| doc.add(f); |
| w.addDocument(doc); |
| w.commit(); |
| |
| DirectoryReader reader = w.getReader(); |
| final IndexSearcher searcher = new IndexSearcher(reader); |
| searcher.setQueryCache(null); // to still have approximations |
| |
| PhraseQuery pq = new PhraseQuery("field", "a", "b"); |
| |
| BooleanQuery.Builder q = new BooleanQuery.Builder(); |
| q.add(pq, Occur.SHOULD); |
| q.add(new TermQuery(new Term("field", "c")), Occur.SHOULD); |
| |
| final Weight weight = searcher.createWeight(searcher.rewrite(q.build()), ScoreMode.COMPLETE, 1); |
| final Scorer scorer = weight.scorer(reader.leaves().get(0)); |
| assertTrue(scorer instanceof DisjunctionScorer); |
| assertNotNull(scorer.twoPhaseIterator()); |
| |
| reader.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testBoostedScorerPropagatesApproximations() throws IOException { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| Field f = newTextField("field", "a b c", Field.Store.NO); |
| doc.add(f); |
| w.addDocument(doc); |
| w.commit(); |
| |
| DirectoryReader reader = w.getReader(); |
| // not LuceneTestCase.newSearcher to not have the asserting wrappers |
| // and do instanceof checks |
| final IndexSearcher searcher = new IndexSearcher(reader); |
| searcher.setQueryCache(null); // to still have approximations |
| |
| PhraseQuery pq = new PhraseQuery("field", "a", "b"); |
| |
| BooleanQuery.Builder q = new BooleanQuery.Builder(); |
| q.add(pq, Occur.SHOULD); |
| q.add(new TermQuery(new Term("field", "d")), Occur.SHOULD); |
| |
| final Weight weight = searcher.createWeight(searcher.rewrite(q.build()), ScoreMode.COMPLETE, 1); |
| final Scorer scorer = weight.scorer(searcher.getIndexReader().leaves().get(0)); |
| assertTrue(scorer instanceof PhraseScorer); |
| assertNotNull(scorer.twoPhaseIterator()); |
| |
| reader.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testExclusionPropagatesApproximations() throws IOException { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| Field f = newTextField("field", "a b c", Field.Store.NO); |
| doc.add(f); |
| w.addDocument(doc); |
| w.commit(); |
| |
| DirectoryReader reader = w.getReader(); |
| final IndexSearcher searcher = new IndexSearcher(reader); |
| searcher.setQueryCache(null); // to still have approximations |
| |
| PhraseQuery pq = new PhraseQuery("field", "a", "b"); |
| |
| BooleanQuery.Builder q = new BooleanQuery.Builder(); |
| q.add(pq, Occur.SHOULD); |
| q.add(new TermQuery(new Term("field", "c")), Occur.MUST_NOT); |
| |
| final Weight weight = searcher.createWeight(searcher.rewrite(q.build()), ScoreMode.COMPLETE, 1); |
| final Scorer scorer = weight.scorer(reader.leaves().get(0)); |
| assertTrue(scorer instanceof ReqExclScorer); |
| assertNotNull(scorer.twoPhaseIterator()); |
| |
| reader.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testReqOptPropagatesApproximations() throws IOException { |
| Directory dir = newDirectory(); |
| RandomIndexWriter w = new RandomIndexWriter(random(), dir); |
| Document doc = new Document(); |
| Field f = newTextField("field", "a b c", Field.Store.NO); |
| doc.add(f); |
| w.addDocument(doc); |
| w.commit(); |
| |
| DirectoryReader reader = w.getReader(); |
| final IndexSearcher searcher = new IndexSearcher(reader); |
| searcher.setQueryCache(null); // to still have approximations |
| |
| PhraseQuery pq = new PhraseQuery("field", "a", "b"); |
| |
| BooleanQuery.Builder q = new BooleanQuery.Builder(); |
| q.add(pq, Occur.MUST); |
| q.add(new TermQuery(new Term("field", "c")), Occur.SHOULD); |
| |
| final Weight weight = searcher.createWeight(searcher.rewrite(q.build()), ScoreMode.COMPLETE, 1); |
| final Scorer scorer = weight.scorer(reader.leaves().get(0)); |
| assertTrue(scorer instanceof ReqOptSumScorer); |
| assertNotNull(scorer.twoPhaseIterator()); |
| |
| reader.close(); |
| w.close(); |
| dir.close(); |
| } |
| |
| public void testToString() { |
| BooleanQuery.Builder bq = new BooleanQuery.Builder(); |
| bq.add(new TermQuery(new Term("field", "a")), Occur.SHOULD); |
| bq.add(new TermQuery(new Term("field", "b")), Occur.MUST); |
| bq.add(new TermQuery(new Term("field", "c")), Occur.MUST_NOT); |
| bq.add(new TermQuery(new Term("field", "d")), Occur.FILTER); |
| assertEquals("a +b -c #d", bq.build().toString("field")); |
| } |
| |
| |
| |
| public void testQueryVisitor() throws IOException { |
| Term a = new Term("f", "a"); |
| Term b = new Term("f", "b"); |
| Term c = new Term("f", "c"); |
| Term d = new Term("f", "d"); |
| BooleanQuery.Builder bqBuilder = new BooleanQuery.Builder(); |
| bqBuilder.add(new TermQuery(a), Occur.SHOULD); |
| bqBuilder.add(new TermQuery(b), Occur.MUST); |
| bqBuilder.add(new TermQuery(c), Occur.FILTER); |
| bqBuilder.add(new TermQuery(d), Occur.MUST_NOT); |
| BooleanQuery bq = bqBuilder.build(); |
| |
| bq.visit(new QueryVisitor() { |
| |
| Term expected; |
| |
| @Override |
| public QueryVisitor getSubVisitor(Occur occur, Query parent) { |
| switch (occur) { |
| case SHOULD: |
| expected = a; |
| break; |
| case MUST: |
| expected = b; |
| break; |
| case FILTER: |
| expected = c; |
| break; |
| case MUST_NOT: |
| expected = d; |
| break; |
| default: |
| throw new IllegalStateException(); |
| } |
| return this; |
| } |
| |
| @Override |
| public void consumeTerms(Query query, Term... terms) { |
| assertEquals(expected, terms[0]); |
| } |
| }); |
| } |
| } |