LUCENE-9050: MultiTermIntervalsSource should call visitLeaf() in visit (#1024)

MultiTermIntervalsSource had an empty visit() implementation, which meant 
that query visitors would be unaware of its presence in a query tree.
This commit is contained in:
Alan Woodward 2019-11-22 10:29:00 +00:00 committed by GitHub
parent 2d1e67c8b4
commit 6aa52b2c4d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 4 deletions

View File

@ -122,6 +122,9 @@ Bug Fixes
* LUCENE-9030: Fix WordnetSynonymParser behaviour so it behaves similar to
SolrSynonymParser. (Christoph Buescher via Alan Woodward)
* LUCENE-9050: MultiTermIntervalsSource.visit() was not calling back to its
visitor. (Alan Woodward)
* LUCENE-9054: Fix reproduceJenkinsFailures.py to not overwrite junit XML files when retrying (hossman)
Other

View File

@ -96,7 +96,7 @@ class MultiTermIntervalsSource extends IntervalsSource {
@Override
public void visit(String field, QueryVisitor visitor) {
visitor.visitLeaf(new IntervalQuery(field, this));
}
@Override

View File

@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.Objects;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.MatchesIterator;
import org.apache.lucene.search.QueryVisitor;
@ -146,7 +147,7 @@ class OffsetIntervalsSource extends IntervalsSource {
@Override
public void visit(String field, QueryVisitor visitor) {
in.visit(field, visitor);
in.visit(field, visitor.getSubVisitor(BooleanClause.Occur.MUST, new IntervalQuery(field, this)));
}
@Override

View File

@ -18,7 +18,11 @@
package org.apache.lucene.queries.intervals;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.CharArraySet;
@ -36,9 +40,13 @@ import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.ReaderUtil;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchesIterator;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryVisitor;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.IOUtils;
@ -140,6 +148,38 @@ public class TestIntervals extends LuceneTestCase {
assertEquals(expectedMatchCount, matchedDocs);
}
private void checkVisits(IntervalsSource source, int expectedVisitCount, String... expectedTerms) {
Set<String> actualTerms = new HashSet<>();
int[] visitedSources = new int[1];
source.visit("field", new QueryVisitor() {
@Override
public void consumeTerms(Query query, Term... terms) {
visitedSources[0]++;
actualTerms.addAll(Arrays.stream(terms).map(Term::text).collect(Collectors.toList()));
}
@Override
public void visitLeaf(Query query) {
visitedSources[0]++;
super.visitLeaf(query);
}
@Override
public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) {
visitedSources[0]++;
return super.getSubVisitor(occur, parent);
}
});
Set<String> expectedSet = new HashSet<>(Arrays.asList(expectedTerms));
expectedSet.removeAll(actualTerms);
actualTerms.removeAll(Arrays.asList(expectedTerms));
assertEquals(expectedVisitCount, visitedSources[0]);
assertTrue("Unexpected terms collected: " + actualTerms, actualTerms.isEmpty());
assertTrue("Missing expected terms: " + expectedSet, expectedSet.isEmpty());
}
private MatchesIterator getMatches(IntervalsSource source, int doc, String field) throws IOException {
int ord = ReaderUtil.subIndex(doc, searcher.getIndexReader().leaves());
LeafReaderContext ctx = searcher.getIndexReader().leaves().get(ord);
@ -194,6 +234,8 @@ public class TestIntervals extends LuceneTestCase {
assertFalse(mi.next());
assertEquals(1, source.minExtent());
checkVisits(source, 1, "porridge");
}
public void testOrderedNearIntervals() throws IOException {
@ -221,6 +263,8 @@ public class TestIntervals extends LuceneTestCase {
assertFalse(mi.next());
assertEquals(2, source.minExtent());
checkVisits(source, 3, "pease", "hot");
}
public void testPhraseIntervals() throws IOException {
@ -244,6 +288,8 @@ public class TestIntervals extends LuceneTestCase {
assertMatch(mi, 6, 7, 41, 55);
assertEquals(2, source.minExtent());
checkVisits(source, 3, "pease", "porridge");
}
public void testUnorderedNearIntervals() throws IOException {
@ -272,6 +318,8 @@ public class TestIntervals extends LuceneTestCase {
});
assertEquals(2, source.minExtent());
checkVisits(source, 3, "pease", "hot");
}
public void testIntervalDisjunction() throws IOException {
@ -293,6 +341,8 @@ public class TestIntervals extends LuceneTestCase {
assertFalse(mi.next());
assertEquals(1, source.minExtent());
checkVisits(source, 4, "pease", "hot", "notMatching");
}
public void testCombinationDisjunction() throws IOException {
@ -307,6 +357,8 @@ public class TestIntervals extends LuceneTestCase {
});
assertEquals(2, source.minExtent());
checkVisits(source, 5, "alph", "sacred", "measureless");
}
public void testNesting() throws IOException {
@ -371,6 +423,8 @@ public class TestIntervals extends LuceneTestCase {
{ 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 18, 18 },
{}
});
checkVisits(before, 7, "pease", "porridge", "hot", "cold");
}
public void testNesting2() throws IOException {
@ -435,9 +489,10 @@ public class TestIntervals extends LuceneTestCase {
{ 0, 3 },
{}
});
checkIntervals(Intervals.unorderedNoOverlaps(
IntervalsSource source = Intervals.unorderedNoOverlaps(
Intervals.term("porridge"),
Intervals.unordered(Intervals.term("pease"), Intervals.term("porridge"))), "field1", 3, new int[][]{
Intervals.unordered(Intervals.term("pease"), Intervals.term("porridge")));
checkIntervals(source, "field1", 3, new int[][]{
{},
{ 1, 4, 4, 7 },
{ 1, 4, 4, 7 },
@ -445,6 +500,8 @@ public class TestIntervals extends LuceneTestCase {
{ 1, 4, 4, 7 },
{}
});
// automatic rewrites mean that we end up with 11 sources to visit
checkVisits(source, 11, "porridge", "pease");
}
public void testContainedBy() throws IOException {
@ -475,6 +532,8 @@ public class TestIntervals extends LuceneTestCase {
assertFalse(subs.next());
assertFalse(mi.next());
assertEquals(1, source.minExtent());
checkVisits(source, 5, "porridge", "pease", "cold");
}
public void testContaining() throws IOException {
@ -770,6 +829,8 @@ public class TestIntervals extends LuceneTestCase {
});
assertEquals("Automaton [p*] expanded to too many terms (limit 1)", e.getMessage());
}
checkVisits(Intervals.prefix(new BytesRef("p")), 1);
}
public void testWildcard() throws IOException {
@ -795,6 +856,8 @@ public class TestIntervals extends LuceneTestCase {
}
});
assertEquals("Automaton [?ot] expanded to too many terms (limit 1)", e.getMessage());
checkVisits(Intervals.wildcard(new BytesRef("p??")), 1);
}
public void testWrappedFilters() throws IOException {