LUCENE-5409: fix ToParentBlockJoinQuery.rewrite to correctly preserve the origChildQuery after more than one iteration of rewrite

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1562124 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2014-01-28 16:59:06 +00:00
parent f15097b4e2
commit fd35f2609a
6 changed files with 87 additions and 21 deletions

View File

@ -207,6 +207,10 @@ Bug fixes
* LUCENE-5415: SpanMultiTermQueryWrapper didn't handle its boost in * LUCENE-5415: SpanMultiTermQueryWrapper didn't handle its boost in
hashcode/equals/tostring/rewrite. (Robert Muir) hashcode/equals/tostring/rewrite. (Robert Muir)
* LUCENE-5409: ToParentBlockJoinCollector.getTopGroups would fail to
return any groups when the joined query required more than one
rewrite step (Peng Cheng via Mike McCandless)
API Changes API Changes
* LUCENE-5339: The facet module was simplified/reworked to make the * LUCENE-5339: The facet module was simplified/reworked to make the

View File

@ -62,8 +62,9 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
* Default value is 1024. * Default value is 1024.
*/ */
public static void setMaxClauseCount(int maxClauseCount) { public static void setMaxClauseCount(int maxClauseCount) {
if (maxClauseCount < 1) if (maxClauseCount < 1) {
throw new IllegalArgumentException("maxClauseCount must be >= 1"); throw new IllegalArgumentException("maxClauseCount must be >= 1");
}
BooleanQuery.maxClauseCount = maxClauseCount; BooleanQuery.maxClauseCount = maxClauseCount;
} }
@ -138,8 +139,9 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
* @see #getMaxClauseCount() * @see #getMaxClauseCount()
*/ */
public void add(BooleanClause clause) { public void add(BooleanClause clause) {
if (clauses.size() >= maxClauseCount) if (clauses.size() >= maxClauseCount) {
throw new TooManyClauses(); throw new TooManyClauses();
}
clauses.add(clause); clauses.add(clause);
} }
@ -182,7 +184,9 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
BooleanClause c = clauses.get(i); BooleanClause c = clauses.get(i);
Weight w = c.getQuery().createWeight(searcher); Weight w = c.getQuery().createWeight(searcher);
weights.add(w); weights.add(w);
if (!c.isProhibited()) maxCoord++; if (!c.isProhibited()) {
maxCoord++;
}
} }
} }
@ -195,9 +199,10 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
for (int i = 0 ; i < weights.size(); i++) { for (int i = 0 ; i < weights.size(); i++) {
// call sumOfSquaredWeights for all clauses in case of side effects // call sumOfSquaredWeights for all clauses in case of side effects
float s = weights.get(i).getValueForNormalization(); // sum sub weights float s = weights.get(i).getValueForNormalization(); // sum sub weights
if (!clauses.get(i).isProhibited()) if (!clauses.get(i).isProhibited()) {
// only add to sum for non-prohibited clauses // only add to sum for non-prohibited clauses
sum += s; sum += s;
}
} }
sum *= getBoost() * getBoost(); // boost each sub-weight sum *= getBoost() * getBoost(); // boost each sub-weight
@ -258,8 +263,9 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
sumExpl.addDetail(r); sumExpl.addDetail(r);
fail = true; fail = true;
} }
if (c.getOccur() == Occur.SHOULD) if (c.getOccur() == Occur.SHOULD) {
shouldMatchCount++; shouldMatchCount++;
}
} else if (c.isRequired()) { } else if (c.isRequired()) {
Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")"); Explanation r = new Explanation(0.0f, "no match on required clause (" + c.getQuery().toString() + ")");
r.addDetail(e); r.addDetail(e);
@ -422,8 +428,9 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
} }
if (clone != null) { if (clone != null) {
return clone; // some clauses rewrote return clone; // some clauses rewrote
} else } else {
return this; // no clauses rewrote return this; // no clauses rewrote
}
} }
// inherit javadoc // inherit javadoc
@ -447,17 +454,18 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
@Override @Override
public String toString(String field) { public String toString(String field) {
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
boolean needParens=(getBoost() != 1.0) || (getMinimumNumberShouldMatch()>0) ; boolean needParens= getBoost() != 1.0 || getMinimumNumberShouldMatch() > 0;
if (needParens) { if (needParens) {
buffer.append("("); buffer.append("(");
} }
for (int i = 0 ; i < clauses.size(); i++) { for (int i = 0 ; i < clauses.size(); i++) {
BooleanClause c = clauses.get(i); BooleanClause c = clauses.get(i);
if (c.isProhibited()) if (c.isProhibited()) {
buffer.append("-"); buffer.append("-");
else if (c.isRequired()) } else if (c.isRequired()) {
buffer.append("+"); buffer.append("+");
}
Query subQuery = c.getQuery(); Query subQuery = c.getQuery();
if (subQuery != null) { if (subQuery != null) {
@ -472,8 +480,9 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
buffer.append("null"); buffer.append("null");
} }
if (i != clauses.size()-1) if (i != clauses.size()-1) {
buffer.append(" "); buffer.append(" ");
}
} }
if (needParens) { if (needParens) {
@ -485,8 +494,7 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
buffer.append(getMinimumNumberShouldMatch()); buffer.append(getMinimumNumberShouldMatch());
} }
if (getBoost() != 1.0f) if (getBoost() != 1.0f) {
{
buffer.append(ToStringUtils.boost(getBoost())); buffer.append(ToStringUtils.boost(getBoost()));
} }
@ -496,10 +504,11 @@ public class BooleanQuery extends Query implements Iterable<BooleanClause> {
/** Returns true iff <code>o</code> is equal to this. */ /** Returns true iff <code>o</code> is equal to this. */
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (!(o instanceof BooleanQuery)) if (!(o instanceof BooleanQuery)) {
return false; return false;
}
BooleanQuery other = (BooleanQuery)o; BooleanQuery other = (BooleanQuery)o;
return (this.getBoost() == other.getBoost()) return this.getBoost() == other.getBoost()
&& this.clauses.equals(other.clauses) && this.clauses.equals(other.clauses)
&& this.getMinimumNumberShouldMatch() == other.getMinimumNumberShouldMatch() && this.getMinimumNumberShouldMatch() == other.getMinimumNumberShouldMatch()
&& this.disableCoord == other.disableCoord; && this.disableCoord == other.disableCoord;

View File

@ -92,7 +92,7 @@ public class TestSubScorerFreqs extends LuceneTestCase {
public void setSubScorers(Scorer scorer, String relationship) { public void setSubScorers(Scorer scorer, String relationship) {
for (ChildScorer child : scorer.getChildren()) { for (ChildScorer child : scorer.getChildren()) {
if (relationships.contains(child.relationship)) { if (scorer instanceof AssertingScorer || relationships.contains(child.relationship)) {
setSubScorers(child.child, child.relationship); setSubScorers(child.child, child.relationship);
} }
} }
@ -180,16 +180,17 @@ public class TestSubScorerFreqs extends LuceneTestCase {
assertEquals(includeOptional ? 5 : 4, doc0.size()); assertEquals(includeOptional ? 5 : 4, doc0.size());
assertEquals(1.0F, doc0.get(aQuery), FLOAT_TOLERANCE); assertEquals(1.0F, doc0.get(aQuery), FLOAT_TOLERANCE);
assertEquals(4.0F, doc0.get(dQuery), FLOAT_TOLERANCE); assertEquals(4.0F, doc0.get(dQuery), FLOAT_TOLERANCE);
if (includeOptional) if (includeOptional) {
assertEquals(3.0F, doc0.get(cQuery), FLOAT_TOLERANCE); assertEquals(3.0F, doc0.get(cQuery), FLOAT_TOLERANCE);
}
Map<Query, Float> doc1 = c.docCounts.get(++i); Map<Query, Float> doc1 = c.docCounts.get(++i);
assertEquals(includeOptional ? 5 : 4, doc1.size()); assertEquals(includeOptional ? 5 : 4, doc1.size());
assertEquals(1.0F, doc1.get(aQuery), FLOAT_TOLERANCE); assertEquals(1.0F, doc1.get(aQuery), FLOAT_TOLERANCE);
assertEquals(1.0F, doc1.get(dQuery), FLOAT_TOLERANCE); assertEquals(1.0F, doc1.get(dQuery), FLOAT_TOLERANCE);
if (includeOptional) if (includeOptional) {
assertEquals(1.0F, doc1.get(cQuery), FLOAT_TOLERANCE); assertEquals(1.0F, doc1.get(cQuery), FLOAT_TOLERANCE);
}
} }
} }
} }

View File

@ -453,7 +453,7 @@ public class ToParentBlockJoinQuery extends Query {
public Query rewrite(IndexReader reader) throws IOException { public Query rewrite(IndexReader reader) throws IOException {
final Query childRewrite = childQuery.rewrite(reader); final Query childRewrite = childQuery.rewrite(reader);
if (childRewrite != childQuery) { if (childRewrite != childQuery) {
Query rewritten = new ToParentBlockJoinQuery(childQuery, Query rewritten = new ToParentBlockJoinQuery(origChildQuery,
childRewrite, childRewrite,
parentsFilter, parentsFilter,
scoreMode); scoreMode);

View File

@ -201,7 +201,7 @@ public class TestBlockJoin extends LuceneTestCase {
childDoc = s.doc(hits.scoreDocs[0].doc); childDoc = s.doc(hits.scoreDocs[0].doc);
//System.out.println("CHILD = " + childDoc + " docID=" + hits.scoreDocs[0].doc); //System.out.println("CHILD = " + childDoc + " docID=" + hits.scoreDocs[0].doc);
assertEquals("java", childDoc.get("skill")); assertEquals("java", childDoc.get("skill"));
assertEquals(2007, (childDoc.getField("year")).numericValue()); assertEquals(2007, childDoc.getField("year").numericValue());
assertEquals("Lisa", getParentDoc(r, parentsFilter, hits.scoreDocs[0].doc).get("name")); assertEquals("Lisa", getParentDoc(r, parentsFilter, hits.scoreDocs[0].doc).get("name"));
// Test with filter on child docs: // Test with filter on child docs:
@ -213,6 +213,54 @@ public class TestBlockJoin extends LuceneTestCase {
dir.close(); dir.close();
} }
public void testBugCausedByRewritingTwice() throws IOException {
final Directory dir = newDirectory();
final RandomIndexWriter w = new RandomIndexWriter(random(), dir);
final List<Document> docs = new ArrayList<Document>();
for (int i=0;i<10;i++) {
docs.clear();
docs.add(makeJob("ruby", i));
docs.add(makeJob("java", 2007));
docs.add(makeResume("Frank", "United States"));
w.addDocuments(docs);
}
IndexReader r = w.getReader();
w.close();
IndexSearcher s = newSearcher(r);
MultiTermQuery qc = NumericRangeQuery.newIntRange("year", 2007, 2007, true, true);
// Hacky: this causes the query to need 2 rewrite
// iterations:
qc.setRewriteMethod(MultiTermQuery.CONSTANT_SCORE_BOOLEAN_QUERY_REWRITE);
Filter parentsFilter = new FixedBitSetCachingWrapperFilter(new QueryWrapperFilter(new TermQuery(new Term("docType", "resume"))));
int h1 = qc.hashCode();
Query qw1 = qc.rewrite(r);
int h2 = qw1.hashCode();
Query qw2 = qw1.rewrite(r);
int h3 = qw2.hashCode();
assertTrue(h1 != h2);
assertTrue(h2 != h3);
assertTrue(h3 != h1);
ToParentBlockJoinQuery qp = new ToParentBlockJoinQuery(qc, parentsFilter, ScoreMode.Max);
ToParentBlockJoinCollector c = new ToParentBlockJoinCollector(Sort.RELEVANCE, 10, true, true);
s.search(qp, c);
TopGroups<Integer> groups = c.getTopGroups(qp, Sort.INDEXORDER, 0, 10, 0, true);
for (GroupDocs<Integer> group : groups.groups) {
assertEquals(1, group.totalHits);
}
r.close();
dir.close();
}
protected QueryWrapperFilter skill(String skill) { protected QueryWrapperFilter skill(String skill) {
return new QueryWrapperFilter(new TermQuery(new Term("skill", skill))); return new QueryWrapperFilter(new TermQuery(new Term("skill", skill)));
} }

View File

@ -151,7 +151,11 @@ public class AssertingScorer extends Scorer {
@Override @Override
public Collection<ChildScorer> getChildren() { public Collection<ChildScorer> getChildren() {
return in.getChildren(); // We cannot hide that we hold a single child, else
// collectors (e.g. ToParentBlockJoinCollector) that
// need to walk the scorer tree will miss/skip the
// Scorer we wrap:
return Collections.singletonList(new ChildScorer(in, "SHOULD"));
} }
@Override @Override