Created
February 26, 2015 21:03
-
-
Save dakrone/d27b20ea695631157510 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java | |
index 209a6dc..d0f5df0 100644 | |
--- a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java | |
+++ b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java | |
@@ -165,6 +165,9 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> { | |
@Override | |
public Query rewrite(IndexReader reader) throws IOException { | |
+ if (clauses.size() == 0) { | |
+ return new MatchNoDocsQuery(); | |
+ } | |
if (minNrShouldMatch == 0 && clauses.size() == 1) { // optimize 1-clause queries | |
BooleanClause c = clauses.get(0); | |
if (!c.isProhibited()) { // just return clause | |
diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java | |
new file mode 100644 | |
index 0000000..c2aff7d | |
--- /dev/null | |
+++ b/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java | |
@@ -0,0 +1,150 @@ | |
+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 java.io.IOException; | |
+import java.util.Set; | |
+ | |
+import org.apache.lucene.index.IndexReader; | |
+import org.apache.lucene.index.LeafReaderContext; | |
+import org.apache.lucene.index.Term; | |
+import org.apache.lucene.util.Bits; | |
+import org.apache.lucene.util.ToStringUtils; | |
+ | |
+/** | |
+ * A query that matches no documents. | |
+ */ | |
+public class MatchNoDocsQuery extends Query { | |
+ | |
+ private class MatchNoScorer extends Scorer { | |
+ final float score; | |
+ | |
+ MatchNoScorer(IndexReader reader, Bits liveDocs, Weight w, float score) { | |
+ super(w); | |
+ this.score = score; | |
+ } | |
+ | |
+ @Override | |
+ public int docID() { | |
+ return -1; | |
+ } | |
+ | |
+ @Override | |
+ public int nextDoc() throws IOException { | |
+ return NO_MORE_DOCS; | |
+ } | |
+ | |
+ @Override | |
+ public float score() { | |
+ return score; | |
+ } | |
+ | |
+ @Override | |
+ public int freq() { | |
+ return 1; | |
+ } | |
+ | |
+ @Override | |
+ public int advance(int target) throws IOException { | |
+ return NO_MORE_DOCS; | |
+ } | |
+ | |
+ @Override | |
+ public long cost() { | |
+ // Cost of 0, because this immediately returns NO_MORE_DOCS | |
+ return 0; | |
+ } | |
+ } | |
+ | |
+ private class MatchNoDocsWeight extends Weight { | |
+ private float queryWeight; | |
+ private float queryNorm; | |
+ | |
+ public MatchNoDocsWeight(IndexSearcher searcher) { | |
+ super(MatchNoDocsQuery.this); | |
+ } | |
+ | |
+ @Override | |
+ public String toString() { | |
+ return "weight(" + MatchNoDocsQuery.this + ")"; | |
+ } | |
+ | |
+ @Override | |
+ public float getValueForNormalization() { | |
+ queryWeight = getBoost(); | |
+ return queryWeight * queryWeight; | |
+ } | |
+ | |
+ @Override | |
+ public void normalize(float queryNorm, float topLevelBoost) { | |
+ this.queryNorm = queryNorm * topLevelBoost; | |
+ queryWeight *= this.queryNorm; | |
+ } | |
+ | |
+ @Override | |
+ public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException { | |
+ return new MatchNoScorer(context.reader(), acceptDocs, this, queryWeight); | |
+ } | |
+ | |
+ @Override | |
+ public Explanation explain(LeafReaderContext context, int doc) { | |
+ // explain query weight | |
+ ComplexExplanation queryExpl = new ComplexExplanation | |
+ (true, queryWeight, "MatchNoDocsQuery, product of:"); | |
+ if (getBoost() != 1.0f) { | |
+ queryExpl.addDetail(new Explanation(getBoost(),"boost")); | |
+ } | |
+ queryExpl.addDetail(new Explanation(queryNorm,"queryNorm")); | |
+ queryExpl.setValue(0.0f); | |
+ queryExpl.setMatch(Boolean.FALSE); | |
+ return queryExpl; | |
+ } | |
+ } | |
+ | |
+ @Override | |
+ public Weight createWeight(IndexSearcher searcher, boolean needsScores) { | |
+ return new MatchNoDocsWeight(searcher); | |
+ } | |
+ | |
+ @Override | |
+ public void extractTerms(Set<Term> terms) { | |
+ } | |
+ | |
+ @Override | |
+ public String toString(String field) { | |
+ StringBuilder buffer = new StringBuilder(); | |
+ buffer.append("_none_"); | |
+ buffer.append(ToStringUtils.boost(getBoost())); | |
+ return buffer.toString(); | |
+ } | |
+ | |
+ @Override | |
+ public boolean equals(Object o) { | |
+ if (!(o instanceof MatchNoDocsQuery)) { | |
+ return false; | |
+ } | |
+ MatchNoDocsQuery other = (MatchNoDocsQuery) o; | |
+ return this.getBoost() == other.getBoost(); | |
+ } | |
+ | |
+ @Override | |
+ public int hashCode() { | |
+ // nocommit - I don't know where 0x1AA71190 comes from here, it may need to be changed | |
+ return Float.floatToIntBits(getBoost()) ^ 0x1AA71190; | |
+ } | |
+} | |
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMatchNoDocsQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestMatchNoDocsQuery.java | |
new file mode 100644 | |
index 0000000..6f8adfc | |
--- /dev/null | |
+++ b/lucene/core/src/test/org/apache/lucene/search/TestMatchNoDocsQuery.java | |
@@ -0,0 +1,85 @@ | |
+/* | |
+ * 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 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.index.DirectoryReader; | |
+import org.apache.lucene.index.IndexWriter; | |
+import org.apache.lucene.index.Term; | |
+import org.apache.lucene.index.IndexReader; | |
+import org.apache.lucene.store.Directory; | |
+ | |
+import org.apache.lucene.util.LuceneTestCase; | |
+ | |
+/** | |
+ * Tests MatchNoDocsQuery. | |
+ * | |
+ */ | |
+public class TestMatchNoDocsQuery extends LuceneTestCase { | |
+ private Analyzer analyzer; | |
+ | |
+ @Override | |
+ public void setUp() throws Exception { | |
+ super.setUp(); | |
+ analyzer = new MockAnalyzer(random()); | |
+ } | |
+ | |
+ public void testQuery() throws Exception { | |
+ Directory dir = newDirectory(); | |
+ IndexWriter iw = new IndexWriter(dir, newIndexWriterConfig(analyzer).setMaxBufferedDocs(2).setMergePolicy(newLogMergePolicy())); | |
+ addDoc("one", iw); | |
+ addDoc("two", iw); | |
+ addDoc("three four", iw); | |
+ IndexReader ir = DirectoryReader.open(iw, true); | |
+ | |
+ IndexSearcher is = newSearcher(ir); | |
+ ScoreDoc[] hits; | |
+ | |
+ hits = is.search(new MatchNoDocsQuery(), 1000).scoreDocs; | |
+ assertEquals(0, hits.length); | |
+ | |
+ // An empty BooleanQuery rewrites to MatchNoDocsQuery() | |
+ BooleanQuery bq = new BooleanQuery(); | |
+ Query rewritten = bq.rewrite(ir); | |
+ assertTrue(rewritten instanceof MatchNoDocsQuery); | |
+ hits = is.search(bq, 1000).scoreDocs; | |
+ assertEquals(0, hits.length); | |
+ | |
+ iw.close(); | |
+ ir.close(); | |
+ dir.close(); | |
+ } | |
+ | |
+ public void testEquals() { | |
+ Query q1 = new MatchNoDocsQuery(); | |
+ Query q2 = new MatchNoDocsQuery(); | |
+ assertTrue(q1.equals(q2)); | |
+ } | |
+ | |
+ private void addDoc(String text, IndexWriter iw) throws IOException { | |
+ Document doc = new Document(); | |
+ Field f = newTextField("key", text, Field.Store.YES); | |
+ doc.add(f); | |
+ iw.addDocument(doc); | |
+ } | |
+ | |
+} | |
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestWildcard.java b/lucene/core/src/test/org/apache/lucene/search/TestWildcard.java | |
index 3e8f8e7..8f4a5e2 100644 | |
--- a/lucene/core/src/test/org/apache/lucene/search/TestWildcard.java | |
+++ b/lucene/core/src/test/org/apache/lucene/search/TestWildcard.java | |
@@ -97,7 +97,7 @@ public class TestWildcard | |
} | |
/** | |
- * Tests if a WildcardQuery with an empty term is rewritten to an empty BooleanQuery | |
+ * Tests if a WildcardQuery with an empty term is rewritten to a MatchNoDocsQuery | |
*/ | |
public void testEmptyTerm() throws IOException { | |
Directory indexStore = getIndexStore("field", new String[]{"nowildcard", "nowildcardx"}); | |
@@ -108,8 +108,7 @@ public class TestWildcard | |
wq.setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE); | |
assertMatches(searcher, wq, 0); | |
Query q = searcher.rewrite(wq); | |
- assertTrue(q instanceof BooleanQuery); | |
- assertEquals(0, ((BooleanQuery) q).clauses().size()); | |
+ assertTrue(q instanceof MatchNoDocsQuery); | |
reader.close(); | |
indexStore.close(); | |
} | |
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java | |
index 5dff236..6f48d79 100644 | |
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java | |
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/simple/SimpleQueryParser.java | |
@@ -23,6 +23,7 @@ import org.apache.lucene.search.BooleanClause; | |
import org.apache.lucene.search.BooleanQuery; | |
import org.apache.lucene.search.FuzzyQuery; | |
import org.apache.lucene.search.MatchAllDocsQuery; | |
+import org.apache.lucene.search.MatchNoDocsQuery; | |
import org.apache.lucene.search.PrefixQuery; | |
import org.apache.lucene.search.Query; | |
import org.apache.lucene.util.QueryBuilder; | |
@@ -148,7 +149,11 @@ public class SimpleQueryParser extends QueryBuilder { | |
State state = new State(data, buffer, 0, data.length); | |
parseSubQuery(state); | |
- return state.top; | |
+ if (state.top == null) { | |
+ return new MatchNoDocsQuery(); | |
+ } else { | |
+ return state.top; | |
+ } | |
} | |
private void parseSubQuery(State state) { | |
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java | |
index 7b55a53..6bf7788 100644 | |
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java | |
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/simple/TestSimpleQueryParser.java | |
@@ -29,6 +29,7 @@ import org.apache.lucene.search.BooleanClause.Occur; | |
import org.apache.lucene.search.BooleanQuery; | |
import org.apache.lucene.search.FuzzyQuery; | |
import org.apache.lucene.search.MatchAllDocsQuery; | |
+import org.apache.lucene.search.MatchNoDocsQuery; | |
import org.apache.lucene.search.PhraseQuery; | |
import org.apache.lucene.search.PrefixQuery; | |
import org.apache.lucene.search.Query; | |
@@ -273,17 +274,19 @@ public class TestSimpleQueryParser extends LuceneTestCase { | |
} | |
public void testGarbageEmpty() throws Exception { | |
- assertNull(parse("")); | |
- assertNull(parse(" ")); | |
- assertNull(parse(" ")); | |
- assertNull(parse("\\ ")); | |
- assertNull(parse("\\ \\ ")); | |
- assertNull(parse("\"\"")); | |
- assertNull(parse("\" \"")); | |
- assertNull(parse("\" \"|\" \"")); | |
- assertNull(parse("(\" \"|\" \")")); | |
- assertNull(parse("\" \" \" \"")); | |
- assertNull(parse("(\" \" \" \")")); | |
+ MatchNoDocsQuery expected = new MatchNoDocsQuery(); | |
+ | |
+ assertEquals(expected, parse("")); | |
+ assertEquals(expected, parse(" ")); | |
+ assertEquals(expected, parse(" ")); | |
+ assertEquals(expected, parse("\\ ")); | |
+ assertEquals(expected, parse("\\ \\ ")); | |
+ assertEquals(expected, parse("\"\"")); | |
+ assertEquals(expected, parse("\" \"")); | |
+ assertEquals(expected, parse("\" \"|\" \"")); | |
+ assertEquals(expected, parse("(\" \"|\" \")")); | |
+ assertEquals(expected, parse("\" \" \" \"")); | |
+ assertEquals(expected, parse("(\" \" \" \")")); | |
} | |
public void testGarbageAND() throws Exception { | |
@@ -643,4 +646,4 @@ public class TestSimpleQueryParser extends LuceneTestCase { | |
parseKeyword(sb.toString(), TestUtil.nextInt(random(), 0, 1024)); // no exception | |
} | |
} | |
-} | |
\ No newline at end of file | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment