diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index bbef9710e3..8a8d4ca61a 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -5,6 +5,16 @@ http://s.apache.org/luceneversions ======================= Lucene 8.1.0 ======================= +API Changes + +* LUCENE-3041: A query introspection API has been added. Queries should + implement a visit() method, taking a QueryVisitor, and either pass the + visitor down to any child queries, or call a visitX() or consumeX() method + on it. All locations in the code that called Weight.extractTerms() + have been changed to use this API, and the extractTerms() method has + been deprecated. (Alan Woodward, Simon Willnauer, David Smiley, Luca + Cavanna) + Bug fixes * LUCENE-8712: Polygon2D does not detect crossings through segment edges. diff --git a/lucene/classification/src/java/org/apache/lucene/classification/utils/NearestFuzzyQuery.java b/lucene/classification/src/java/org/apache/lucene/classification/utils/NearestFuzzyQuery.java index c39a3f517c..24c822745b 100644 --- a/lucene/classification/src/java/org/apache/lucene/classification/utils/NearestFuzzyQuery.java +++ b/lucene/classification/src/java/org/apache/lucene/classification/utils/NearestFuzzyQuery.java @@ -39,6 +39,7 @@ import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.FuzzyTermsEnum; import org.apache.lucene.search.MaxNonCompetitiveBoostAttribute; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TermQuery; import org.apache.lucene.util.AttributeSource; import org.apache.lucene.util.BytesRef; @@ -330,4 +331,9 @@ public class NearestFuzzyQuery extends Query { Objects.equals(fieldVals, other.fieldVals); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + } diff --git a/lucene/core/src/java/org/apache/lucene/document/FeatureQuery.java b/lucene/core/src/java/org/apache/lucene/document/FeatureQuery.java index efc2d00e5c..4dce6bd031 100644 --- a/lucene/core/src/java/org/apache/lucene/document/FeatureQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/FeatureQuery.java @@ -33,6 +33,7 @@ import org.apache.lucene.search.Explanation; import org.apache.lucene.search.ImpactsDISI; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -174,6 +175,13 @@ final class FeatureQuery extends Query { }; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(fieldName)) { + visitor.visitLeaf(this); + } + } + @Override public String toString(String field) { return "FeatureQuery(field=" + fieldName + ", feature=" + featureName + ", function=" + function + ")"; diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java index f34089ef62..180caa5148 100644 --- a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java @@ -27,6 +27,7 @@ import org.apache.lucene.search.ConstantScoreScorer; import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -95,6 +96,13 @@ final class LatLonDocValuesBoxQuery extends Query { return h; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new ConstantScoreWeight(this, boost) { diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java index 5fd192463c..5ea137b15a 100644 --- a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java @@ -27,6 +27,7 @@ import org.apache.lucene.search.ConstantScoreScorer; import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -54,6 +55,13 @@ final class LatLonDocValuesDistanceQuery extends Query { this.radiusMeters = radiusMeters; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public String toString(String field) { StringBuilder sb = new StringBuilder(); diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceFeatureQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceFeatureQuery.java index 3f30deae74..4b1d3f26f2 100644 --- a/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceFeatureQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceFeatureQuery.java @@ -35,6 +35,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.ScorerSupplier; @@ -64,6 +65,13 @@ final class LatLonPointDistanceFeatureQuery extends Query { this.pivotDistance = pivotDistance; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public final boolean equals(Object o) { return sameClassAs(o) && diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java index ca9ff2200e..969a1336f5 100644 --- a/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java @@ -32,6 +32,7 @@ import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.ScorerSupplier; @@ -71,6 +72,13 @@ final class LatLonPointDistanceQuery extends Query { this.radiusMeters = radiusMeters; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { Rectangle box = Rectangle.fromPointDistance(latitude, longitude, radiusMeters); diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java index 2e4d98de9f..e425d16845 100644 --- a/lucene/core/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java @@ -33,6 +33,7 @@ import org.apache.lucene.search.ConstantScoreScorer; import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -75,6 +76,13 @@ final class LatLonPointInPolygonQuery extends Query { // TODO: we could also compute the maximal inner bounding box, to make relations faster to compute? } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/core/src/java/org/apache/lucene/document/LongDistanceFeatureQuery.java b/lucene/core/src/java/org/apache/lucene/document/LongDistanceFeatureQuery.java index 480cfce443..f820746725 100644 --- a/lucene/core/src/java/org/apache/lucene/document/LongDistanceFeatureQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/LongDistanceFeatureQuery.java @@ -32,6 +32,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.ScorerSupplier; @@ -75,6 +76,13 @@ final class LongDistanceFeatureQuery extends Query { return h; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public String toString(String field) { return getClass().getSimpleName() + "(field=" + field + ",origin=" + origin + ",pivotDistance=" + pivotDistance + ")"; diff --git a/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java b/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java index 6b235fe194..ac21a431f6 100644 --- a/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/RangeFieldQuery.java @@ -31,10 +31,11 @@ import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; +import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.Weight; -import org.apache.lucene.search.ScoreMode; import org.apache.lucene.util.DocIdSetBuilder; import org.apache.lucene.util.FutureArrays; @@ -261,6 +262,13 @@ abstract class RangeFieldQuery extends Query { } } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new ConstantScoreWeight(this, boost) { diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java index 771af5c927..60222c300e 100644 --- a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java @@ -30,6 +30,7 @@ import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -67,6 +68,13 @@ abstract class SortedNumericDocValuesRangeQuery extends Query { return h; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public String toString(String field) { StringBuilder b = new StringBuilder(); diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java index e5d365f381..88792babe5 100644 --- a/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/SortedSetDocValuesRangeQuery.java @@ -30,6 +30,7 @@ import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -78,6 +79,13 @@ abstract class SortedSetDocValuesRangeQuery extends Query { return h; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public String toString(String field) { StringBuilder b = new StringBuilder(); diff --git a/lucene/core/src/java/org/apache/lucene/search/AutomatonQuery.java b/lucene/core/src/java/org/apache/lucene/search/AutomatonQuery.java index 7fb155d78e..a4a53442ea 100644 --- a/lucene/core/src/java/org/apache/lucene/search/AutomatonQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/AutomatonQuery.java @@ -151,7 +151,14 @@ public class AutomatonQuery extends MultiTermQuery { buffer.append("}"); return buffer.toString(); } - + + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField())) { + visitor.visitLeaf(this); + } + } + /** Returns the automaton used to create this query */ public Automaton getAutomaton() { return automaton; diff --git a/lucene/core/src/java/org/apache/lucene/search/BlendedTermQuery.java b/lucene/core/src/java/org/apache/lucene/search/BlendedTermQuery.java index 8f85e25b44..00ba5a13fd 100644 --- a/lucene/core/src/java/org/apache/lucene/search/BlendedTermQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/BlendedTermQuery.java @@ -25,8 +25,8 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermStates; import org.apache.lucene.index.TermState; +import org.apache.lucene.index.TermStates; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.InPlaceMergeSorter; @@ -294,6 +294,15 @@ public final class BlendedTermQuery extends Query { return rewriteMethod.rewrite(termQueries); } + @Override + public void visit(QueryVisitor visitor) { + Term[] termsToVisit = Arrays.stream(terms).filter(t -> visitor.acceptField(t.field())).toArray(Term[]::new); + if (termsToVisit.length > 0) { + QueryVisitor v = visitor.getSubVisitor(Occur.SHOULD, this); + v.consumeTerms(this, termsToVisit); + } + } + private static TermStates adjustFrequencies(IndexReaderContext readerContext, TermStates ctx, int artificialDf, long artificialTtf) throws IOException { List leaves = readerContext.leaves(); 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 f52df9fb9c..87105b0e82 100644 --- a/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/BooleanQuery.java @@ -451,6 +451,18 @@ public class BooleanQuery extends Query implements Iterable { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + for (BooleanClause.Occur occur : clauseSets.keySet()) { + if (clauseSets.get(occur).size() > 0) { + QueryVisitor v = visitor.getSubVisitor(occur, this); + for (Query q : clauseSets.get(occur)) { + q.visit(v); + } + } + } + } + /** Prints a user-readable version of this query. */ @Override public String toString(String field) { diff --git a/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java b/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java index 4e4649cb71..5f0436335c 100644 --- a/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/BoostQuery.java @@ -104,6 +104,11 @@ public final class BoostQuery extends Query { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + query.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + @Override public String toString(String field) { StringBuilder builder = new StringBuilder(); diff --git a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java index 5c9ed19f89..9ad256d59f 100644 --- a/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/ConstantScoreQuery.java @@ -64,6 +64,11 @@ public final class ConstantScoreQuery extends Query { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + query.visit(visitor.getSubVisitor(BooleanClause.Occur.FILTER, this)); + } + /** We return this as our {@link BulkScorer} so that if the CSQ * wraps a query with its own optimized top-level * scorer (e.g. BooleanScorer) we can use that diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java index 43b42b575c..390c523df1 100644 --- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMaxQuery.java @@ -26,8 +26,8 @@ import java.util.List; import java.util.Objects; import java.util.Set; -import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; /** @@ -237,6 +237,14 @@ public final class DisjunctionMaxQuery extends Query implements Iterable return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, this); + for (Query q : disjuncts) { + q.visit(v); + } + } + /** Prettyprint us. * @param field the field to which we are applied * @return a string that shows what we do, of the form "(disjunct1 | disjunct2 | ... | disjunctn)^boost" diff --git a/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java b/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java index 4b9c97d6ae..bca34c2b07 100644 --- a/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/DocValuesFieldExistsQuery.java @@ -59,6 +59,13 @@ public final class DocValuesFieldExistsQuery extends Query { return "DocValuesFieldExistsQuery [field=" + this.field + "]"; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) { return new ConstantScoreWeight(this, boost) { diff --git a/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java b/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java index d328b42b24..b1a7ef711d 100644 --- a/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java +++ b/lucene/core/src/java/org/apache/lucene/search/DocValuesRewriteMethod.java @@ -70,6 +70,13 @@ public final class DocValuesRewriteMethod extends MultiTermQuery.RewriteMethod { /** Returns the field name for this query */ public final String getField() { return query.getField(); } + + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(query.getField())) { + visitor.visitLeaf(this); + } + } @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/core/src/java/org/apache/lucene/search/FuzzyQuery.java b/lucene/core/src/java/org/apache/lucene/search/FuzzyQuery.java index 3c1eacd80e..344f921f64 100644 --- a/lucene/core/src/java/org/apache/lucene/search/FuzzyQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/FuzzyQuery.java @@ -24,6 +24,7 @@ import org.apache.lucene.index.Term; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.util.AttributeSource; +import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.LevenshteinAutomata; /** Implements the fuzzy search query. The similarity measurement @@ -146,6 +147,21 @@ public class FuzzyQuery extends MultiTermQuery { return transpositions; } + /** + * Expert: Constructs an equivalent Automaton accepting terms matched by this query + */ + public Automaton toAutomaton() { + return FuzzyTermsEnum.buildAutomaton(term.text(), prefixLength, transpositions, maxEdits); + } + + @Override + public void visit(QueryVisitor visitor) { + // TODO find some way of consuming Automata + if (visitor.acceptField(term.field())) { + visitor.visitLeaf(this); + } + } + @Override protected TermsEnum getTermsEnum(Terms terms, AttributeSource atts) throws IOException { if (maxEdits == 0 || prefixLength >= term.text().length()) { // can only match if it's exact @@ -153,7 +169,7 @@ public class FuzzyQuery extends MultiTermQuery { } return new FuzzyTermsEnum(terms, atts, getTerm(), maxEdits, prefixLength, transpositions); } - + /** * Returns the pattern term. */ @@ -173,7 +189,7 @@ public class FuzzyQuery extends MultiTermQuery { buffer.append(Integer.toString(maxEdits)); return buffer.toString(); } - + @Override public int hashCode() { final int prime = 31; diff --git a/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java b/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java index a6d56e7ce8..47592203b3 100644 --- a/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java +++ b/lucene/core/src/java/org/apache/lucene/search/FuzzyTermsEnum.java @@ -17,6 +17,9 @@ package org.apache.lucene.search; +import java.io.IOException; +import java.util.Arrays; + import org.apache.lucene.index.BaseTermsEnum; import org.apache.lucene.index.ImpactsEnum; import org.apache.lucene.index.PostingsEnum; @@ -35,9 +38,6 @@ import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.CompiledAutomaton; import org.apache.lucene.util.automaton.LevenshteinAutomata; -import java.io.IOException; -import java.util.Arrays; - /** Subclass of TermsEnum for enumerating all terms that are similar * to the specified filter term. * @@ -111,11 +111,7 @@ public final class FuzzyTermsEnum extends BaseTermsEnum { this.term = term; // convert the string into a utf32 int[] representation for fast comparisons - final String utf16 = term.text(); - this.termText = new int[utf16.codePointCount(0, utf16.length())]; - for (int cp, i = 0, j = 0; i < utf16.length(); i += Character.charCount(cp)) { - termText[j++] = cp = utf16.codePointAt(i); - } + this.termText = stringToUTF32(term.text()); this.termLength = termText.length; this.dfaAtt = atts.addAttribute(LevenshteinAutomataAttribute.class); @@ -133,16 +129,10 @@ public final class FuzzyTermsEnum extends BaseTermsEnum { CompiledAutomaton[] prevAutomata = dfaAtt.automata(); if (prevAutomata == null) { prevAutomata = new CompiledAutomaton[maxEdits+1]; - - LevenshteinAutomata builder = - new LevenshteinAutomata(UnicodeUtil.newString(termText, realPrefixLength, termText.length - realPrefixLength), transpositions); - - String prefix = UnicodeUtil.newString(termText, 0, realPrefixLength); + Automaton[] automata = buildAutomata(termText, prefixLength, transpositions, maxEdits); for (int i = 0; i <= maxEdits; i++) { - Automaton a = builder.toAutomaton(i, prefix); - prevAutomata[i] = new CompiledAutomaton(a, true, false); + prevAutomata[i] = new CompiledAutomaton(automata[i], true, false); } - // first segment computes the automata, and we share with subsequent segments via this Attribute: dfaAtt.setAutomata(prevAutomata); } @@ -152,6 +142,46 @@ public final class FuzzyTermsEnum extends BaseTermsEnum { bottomTerm = maxBoostAtt.getCompetitiveTerm(); bottomChanged(null); } + + /** + * Builds a binary Automaton to match a fuzzy term + * @param text the term to match + * @param prefixLength length of a required common prefix + * @param transpositions {@code true} if transpositions should count as a single edit + * @param maxEdits the maximum edit distance of matching terms + */ + public static Automaton buildAutomaton(String text, int prefixLength, boolean transpositions, int maxEdits) { + int[] termText = stringToUTF32(text); + Automaton[] automata = buildAutomata(termText, prefixLength, transpositions, maxEdits); + return automata[automata.length - 1]; + } + + private static int[] stringToUTF32(String text) { + int[] termText = new int[text.codePointCount(0, text.length())]; + for (int cp, i = 0, j = 0; i < text.length(); i += Character.charCount(cp)) { + termText[j++] = cp = text.codePointAt(i); + } + return termText; + } + + private static Automaton[] buildAutomata(int[] termText, int prefixLength, boolean transpositions, int maxEdits) { + if (maxEdits < 0 || maxEdits > LevenshteinAutomata.MAXIMUM_SUPPORTED_DISTANCE) { + throw new IllegalArgumentException("max edits must be 0.." + LevenshteinAutomata.MAXIMUM_SUPPORTED_DISTANCE + ", inclusive; got: " + maxEdits); + } + if (prefixLength < 0) { + throw new IllegalArgumentException("prefixLength cannot be less than 0"); + } + Automaton[] automata = new Automaton[maxEdits + 1]; + int termLength = termText.length; + prefixLength = Math.min(prefixLength, termLength); + String suffix = UnicodeUtil.newString(termText, prefixLength, termText.length - prefixLength); + LevenshteinAutomata builder = new LevenshteinAutomata(suffix, transpositions); + String prefix = UnicodeUtil.newString(termText, 0, prefixLength); + for (int i = 0; i <= maxEdits; i++) { + automata[i] = builder.toAutomaton(i, prefix); + } + return automata; + } /** * return an automata-based enum for matching up to editDistance from diff --git a/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java b/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java index d69421ec4d..0b03546733 100644 --- a/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/IndexOrDocValuesQuery.java @@ -109,6 +109,13 @@ public final class IndexOrDocValuesQuery extends Query { return this; } + @Override + public void visit(QueryVisitor visitor) { + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.MUST, this); + indexQuery.visit(v); + dvQuery.visit(v); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { final Weight indexWeight = indexQuery.createWeight(searcher, scoreMode, boost); diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java index 80e8d3255b..ca414c287a 100644 --- a/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/MatchAllDocsQuery.java @@ -89,4 +89,9 @@ public final class MatchAllDocsQuery extends Query { public int hashCode() { return classHash(); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java b/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java index 525a183954..7ec0010684 100644 --- a/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/MatchNoDocsQuery.java @@ -65,6 +65,11 @@ public class MatchNoDocsQuery extends Query { }; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public String toString(String field) { return "MatchNoDocsQuery(\"" + reason + "\")"; diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java index c8d22baec9..a006325316 100644 --- a/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/MultiPhraseQuery.java @@ -203,6 +203,18 @@ public class MultiPhraseQuery extends Query { } } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field) == false) { + return; + } + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.MUST, this); + for (Term[] terms : termArrays) { + QueryVisitor sv = v.getSubVisitor(BooleanClause.Occur.SHOULD, this); + sv.consumeTerms(this, terms); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { final Map termStates = new HashMap<>(); diff --git a/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java b/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java index 9c3721117a..2d7ac9b4bd 100644 --- a/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java +++ b/lucene/core/src/java/org/apache/lucene/search/MultiTermQueryConstantScoreWrapper.java @@ -231,4 +231,11 @@ final class MultiTermQueryConstantScoreWrapper extends }; } + + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField())) { + query.visit(visitor.getSubVisitor(Occur.FILTER, this)); + } + } } diff --git a/lucene/core/src/java/org/apache/lucene/search/NGramPhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/NGramPhraseQuery.java index db997d37e7..3f86afa40d 100644 --- a/lucene/core/src/java/org/apache/lucene/search/NGramPhraseQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/NGramPhraseQuery.java @@ -77,6 +77,11 @@ public class NGramPhraseQuery extends Query { return builder.build(); } + @Override + public void visit(QueryVisitor visitor) { + phraseQuery.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java b/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java index 8c868858df..984a9a8371 100644 --- a/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/NormsFieldExistsQuery.java @@ -61,6 +61,13 @@ public final class NormsFieldExistsQuery extends Query { return "NormsFieldExistsQuery [field=" + this.field + "]"; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new ConstantScoreWeight(this, boost) { diff --git a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java index 8f042716a6..5f4d102171 100644 --- a/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/PhraseQuery.java @@ -255,6 +255,9 @@ public class PhraseQuery extends Query { */ public int getSlop() { return slop; } + /** Returns the field this query applies to */ + public String getField() { return field; } + /** Returns the list of terms in this phrase. */ public Term[] getTerms() { return terms; @@ -284,6 +287,15 @@ public class PhraseQuery extends Query { } } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field) == false) { + return; + } + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.MUST, this); + v.consumeTerms(this, terms); + } + static class PostingsAndFreq implements Comparable { final PostingsEnum postings; final int position; diff --git a/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java b/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java index b6d047e282..ad9e25857c 100644 --- a/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java @@ -105,6 +105,13 @@ public abstract class PointInSetQuery extends Query { sortedPackedPointsHashCode = sortedPackedPoints.hashCode(); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java b/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java index ca61d51067..57c87086aa 100644 --- a/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java @@ -99,6 +99,13 @@ public abstract class PointRangeQuery extends Query { } } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/core/src/java/org/apache/lucene/search/Query.java b/lucene/core/src/java/org/apache/lucene/search/Query.java index 54de63fc02..4767aeb9e0 100644 --- a/lucene/core/src/java/org/apache/lucene/search/Query.java +++ b/lucene/core/src/java/org/apache/lucene/search/Query.java @@ -74,6 +74,14 @@ public abstract class Query { return this; } + /** + * Recurse through the query tree, visiting any child queries + * @param visitor a QueryVisitor to be called by each query in the tree + */ + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + /** * Override and implement query instance equivalence properly in a subclass. * This is required so that {@link QueryCache} works properly. diff --git a/lucene/core/src/java/org/apache/lucene/search/QueryVisitor.java b/lucene/core/src/java/org/apache/lucene/search/QueryVisitor.java new file mode 100644 index 0000000000..5635f7d5d2 --- /dev/null +++ b/lucene/core/src/java/org/apache/lucene/search/QueryVisitor.java @@ -0,0 +1,94 @@ +/* + * 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.util.Arrays; +import java.util.Set; + +import org.apache.lucene.index.Term; + +/** + * Allows recursion through a query tree + * + * @see Query#visit(QueryVisitor) + */ +public abstract class QueryVisitor { + + /** + * Called by leaf queries that match on specific terms + * + * @param query the leaf query + * @param terms the terms the query will match on + */ + public void consumeTerms(Query query, Term... terms) { } + + // TODO it would be nice to have a way to consume 'classes' of Terms from + // things like AutomatonQuery + + /** + * Called by leaf queries that do not match on terms + * @param query the query + */ + public void visitLeaf(Query query) { } + + /** + * Whether or not terms from this field are of interest to the visitor + * + * Implement this to avoid collecting terms from heavy queries such as {@link TermInSetQuery} + * that are not running on fields of interest + */ + public boolean acceptField(String field) { + return true; + } + + /** + * Pulls a visitor instance for visiting child clauses of a query + * + * The default implementation returns {@code this}, unless {@code occur} is equal + * to {@link BooleanClause.Occur#MUST_NOT} in which case it returns + * {@link #EMPTY_VISITOR} + * + * @param occur the relationship between the parent and its children + * @param parent the query visited + */ + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { + if (occur == BooleanClause.Occur.MUST_NOT) { + return EMPTY_VISITOR; + } + return this; + } + + /** + * Builds a {@code QueryVisitor} instance that collects all terms that may match a query + * @param termSet a {@code Set} to add collected terms to + */ + public static QueryVisitor termCollector(Set termSet) { + return new QueryVisitor() { + @Override + public void consumeTerms(Query query, Term... terms) { + termSet.addAll(Arrays.asList(terms)); + } + }; + } + + /** + * A QueryVisitor implementation that does nothing + */ + public static final QueryVisitor EMPTY_VISITOR = new QueryVisitor() {}; + +} diff --git a/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java b/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java index ae35931d37..e3e6d8bc95 100644 --- a/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/SynonymQuery.java @@ -53,7 +53,9 @@ import org.apache.lucene.util.PriorityQueue; * term frequencies for the document. */ public final class SynonymQuery extends Query { + private final TermAndBoost terms[]; + private final String field; /** * A builder for {@link SynonymQuery}. @@ -101,7 +103,7 @@ public final class SynonymQuery extends Query { */ public SynonymQuery build() { Collections.sort(terms); - return new SynonymQuery(terms.toArray(new TermAndBoost[0])); + return new SynonymQuery(terms.toArray(new TermAndBoost[0]), field); } } @@ -131,6 +133,7 @@ public final class SynonymQuery extends Query { } } Arrays.sort(this.terms); + this.field = field; } /** @@ -138,8 +141,9 @@ public final class SynonymQuery extends Query { *

* The terms must all have the same field. */ - private SynonymQuery(TermAndBoost[] terms) { + private SynonymQuery(TermAndBoost[] terms, String field) { this.terms = Objects.requireNonNull(terms); + this.field = field; } public List getTerms() { @@ -191,6 +195,16 @@ public final class SynonymQuery extends Query { return this; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field) == false) { + return; + } + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, this); + Term[] ts = Arrays.stream(terms).map(t -> t.term).toArray(Term[]::new); + v.consumeTerms(this, ts); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { if (scoreMode.needsScores()) { diff --git a/lucene/core/src/java/org/apache/lucene/search/TermInSetQuery.java b/lucene/core/src/java/org/apache/lucene/search/TermInSetQuery.java index 814bea2638..e956196100 100644 --- a/lucene/core/src/java/org/apache/lucene/search/TermInSetQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/TermInSetQuery.java @@ -33,8 +33,8 @@ import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.PrefixCodedTerms; import org.apache.lucene.index.PrefixCodedTerms.TermIterator; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermStates; import org.apache.lucene.index.TermState; +import org.apache.lucene.index.TermStates; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.BooleanClause.Occur; @@ -122,6 +122,20 @@ public class TermInSetQuery extends Query implements Accountable { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field) == false) { + return; + } + QueryVisitor v = visitor.getSubVisitor(Occur.SHOULD, this); + List terms = new ArrayList<>(); + TermIterator iterator = termData.iterator(); + for (BytesRef term = iterator.next(); term != null; term = iterator.next()) { + terms.add(new Term(field, BytesRef.deepCopyOf(term))); + } + v.consumeTerms(this, terms.toArray(new Term[0])); + } + @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java index 1eebbce6e9..4b99f74d1c 100644 --- a/lucene/core/src/java/org/apache/lucene/search/TermQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/TermQuery.java @@ -205,6 +205,13 @@ public class TermQuery extends Query { return new TermWeight(searcher, scoreMode, boost, termState); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(term.field())) { + visitor.consumeTerms(this, term); + } + } + /** Prints a user-readable version of this query. */ @Override public String toString(String field) { diff --git a/lucene/core/src/java/org/apache/lucene/search/Weight.java b/lucene/core/src/java/org/apache/lucene/search/Weight.java index d2fac4ccdd..ef092c641e 100644 --- a/lucene/core/src/java/org/apache/lucene/search/Weight.java +++ b/lucene/core/src/java/org/apache/lucene/search/Weight.java @@ -66,7 +66,10 @@ public abstract class Weight implements SegmentCacheable { * {@link Weight} was created with {@code needsScores == true} then this * method will only extract terms which are used for scoring, otherwise it * will extract all terms which are used for matching. + * + * @deprecated Use {@link Query#visit(QueryVisitor)} with {@link QueryVisitor#termCollector(Set)} */ + @Deprecated public abstract void extractTerms(Set terms); /** diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java index 4a4c4fbae9..118fc06eba 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/FieldMaskingSpanQuery.java @@ -17,14 +17,16 @@ package org.apache.lucene.search.spans; +import java.io.IOException; +import java.util.Objects; + import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; -import java.io.IOException; -import java.util.Objects; - /** *

Wrapper to allow {@link SpanQuery} objects participate in composite * single-field SpanQueries by 'lying' about their search field. That is, @@ -104,6 +106,13 @@ public final class FieldMaskingSpanQuery extends SpanQuery { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + maskedQuery.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + } + @Override public String toString(String field) { StringBuilder buffer = new StringBuilder(); diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanBoostQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanBoostQuery.java index 9556959a3e..fec57922fb 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanBoostQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanBoostQuery.java @@ -21,9 +21,11 @@ import java.io.IOException; import java.util.Objects; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; /** @@ -93,6 +95,13 @@ public final class SpanBoostQuery extends SpanQuery { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField())) { + query.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + } + @Override public String toString(String field) { StringBuilder builder = new StringBuilder(); diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanContainQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanContainQuery.java index 23c1e2b829..700bed1dab 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanContainQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanContainQuery.java @@ -17,18 +17,20 @@ package org.apache.lucene.search.spans; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Map; +import java.util.Objects; +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.index.TermStates; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import org.apache.lucene.search.QueryVisitor; abstract class SpanContainQuery extends SpanQuery implements Cloneable { @@ -128,6 +130,15 @@ abstract class SpanContainQuery extends SpanQuery implements Cloneable { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField())) { + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.MUST, this); + big.visit(v); + little.visit(v); + } + } + @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java index 088e73092d..4f692ae68f 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanMultiTermQueryWrapper.java @@ -29,6 +29,7 @@ import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.ScoringRewrite; import org.apache.lucene.search.TopTermsRewrite; @@ -121,7 +122,14 @@ public class SpanMultiTermQueryWrapper extends SpanQue public Query rewrite(IndexReader reader) throws IOException { return rewriteMethod.rewrite(reader, query); } - + + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(query.getField())) { + query.visit(visitor.getSubVisitor(Occur.MUST, this)); + } + } + @Override public int hashCode() { return classHash() * 31 + query.hashCode(); diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanNearQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanNearQuery.java index 17b9e51513..1b729c0890 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanNearQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanNearQuery.java @@ -31,8 +31,10 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermStates; import org.apache.lucene.index.Terms; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; @@ -265,6 +267,17 @@ public class SpanNearQuery extends SpanQuery implements Cloneable { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField()) == false) { + return; + } + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.MUST, this); + for (SpanQuery clause : clauses) { + clause.visit(v); + } + } + @Override public boolean equals(Object other) { return sameClassAs(other) && @@ -301,6 +314,11 @@ public class SpanNearQuery extends SpanQuery implements Cloneable { return field; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public String toString(String field) { return "SpanGap(" + field + ":" + width + ")"; diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java index 6c56df3abe..094c33de64 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanNotQuery.java @@ -26,9 +26,11 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermStates; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TwoPhaseIterator; @@ -209,7 +211,16 @@ public final class SpanNotQuery extends SpanQuery { } return super.rewrite(reader); } - /** Returns true iff o is equal to this. */ + + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField())) { + include.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + exclude.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST_NOT, this)); + } + } + + /** Returns true iff o is equal to this. */ @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java index 849edaa30e..0c4417e38a 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanOrQuery.java @@ -28,11 +28,13 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermStates; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.DisiPriorityQueue; import org.apache.lucene.search.DisiWrapper; import org.apache.lucene.search.DisjunctionDISIApproximation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.search.Weight; @@ -88,6 +90,17 @@ public final class SpanOrQuery extends SpanQuery { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField()) == false) { + return; + } + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, this); + for (SpanQuery q : clauses) { + q.visit(v); + } + } + @Override public String toString(String field) { StringBuilder buffer = new StringBuilder(); diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java index 099b627e1e..c71a56fffa 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanPositionCheckQuery.java @@ -26,8 +26,10 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermStates; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.spans.FilterSpans.AcceptStatus; @@ -126,6 +128,13 @@ public abstract class SpanPositionCheckQuery extends SpanQuery implements Clonea return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(getField())) { + match.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + } + /** Returns true iff other is equal to this. */ @Override public boolean equals(Object other) { diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java b/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java index 42e73f2be2..7fb2f5fffb 100644 --- a/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java +++ b/lucene/core/src/java/org/apache/lucene/search/spans/SpanTermQuery.java @@ -28,11 +28,12 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermStates; import org.apache.lucene.index.TermState; +import org.apache.lucene.index.TermStates; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; /** Matches spans containing a term. @@ -84,6 +85,13 @@ public class SpanTermQuery extends SpanQuery { return new SpanTermWeight(context, searcher, scoreMode.needsScores() ? Collections.singletonMap(term, context) : null, boost); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(term.field())) { + visitor.consumeTerms(this, term); + } + } + public class SpanTermWeight extends SpanWeight { final TermStates termStates; diff --git a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java index a9d84cfb4c..85ca09245f 100644 --- a/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java +++ b/lucene/core/src/test/org/apache/lucene/search/JustCompileSearch.java @@ -153,6 +153,11 @@ final class JustCompileSearch { throw new UnsupportedOperationException(UNSUPPORTED_MSG); } + @Override + public void visit(QueryVisitor visitor) { + throw new UnsupportedOperationException(UNSUPPORTED_MSG); + } + @Override public boolean equals(Object obj) { throw new UnsupportedOperationException(UNSUPPORTED_MSG); diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBoolean2ScorerSupplier.java b/lucene/core/src/test/org/apache/lucene/search/TestBoolean2ScorerSupplier.java index ea2fd4c9e8..5ad709177d 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestBoolean2ScorerSupplier.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestBoolean2ScorerSupplier.java @@ -24,14 +24,13 @@ import java.util.EnumMap; import java.util.Map; import java.util.Set; +import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause.Occur; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.TestUtil; -import com.carrotsearch.randomizedtesting.generators.RandomPicks; - public class TestBoolean2ScorerSupplier extends LuceneTestCase { private static class FakeWeight extends Weight { diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java index 1b5da1b13a..99f36ac7ee 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanQuery.java @@ -22,14 +22,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; 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; @@ -52,8 +51,6 @@ import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.NamedThreadFactory; import org.apache.lucene.util.TestUtil; -import com.carrotsearch.randomizedtesting.generators.RandomPicks; - public class TestBooleanQuery extends LuceneTestCase { public void testEquality() throws Exception { @@ -754,7 +751,9 @@ public class TestBooleanQuery extends LuceneTestCase { assertEquals("a +b -c #d", bq.build().toString("field")); } - public void testExtractTerms() throws IOException { + + + public void testQueryVisitor() throws IOException { Term a = new Term("f", "a"); Term b = new Term("f", "b"); Term c = new Term("f", "c"); @@ -764,15 +763,37 @@ public class TestBooleanQuery extends LuceneTestCase { bqBuilder.add(new TermQuery(b), Occur.MUST); bqBuilder.add(new TermQuery(c), Occur.FILTER); bqBuilder.add(new TermQuery(d), Occur.MUST_NOT); - IndexSearcher searcher = new IndexSearcher(new MultiReader()); BooleanQuery bq = bqBuilder.build(); - Set scoringTerms = new HashSet<>(); - searcher.createWeight(searcher.rewrite(bq), ScoreMode.COMPLETE, 1).extractTerms(scoringTerms); - assertEquals(new HashSet<>(Arrays.asList(a, b)), scoringTerms); + bq.visit(new QueryVisitor() { + + Term expected; - Set matchingTerms = new HashSet<>(); - searcher.createWeight(searcher.rewrite(bq), ScoreMode.COMPLETE_NO_SCORES, 1).extractTerms(matchingTerms); - assertEquals(new HashSet<>(Arrays.asList(a, b, c)), matchingTerms); + @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]); + } + }); } } diff --git a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java index 75fdd019e9..91293276a3 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestBooleanScorer.java @@ -118,6 +118,11 @@ public class TestBooleanScorer extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public boolean equals(Object obj) { return this == obj; diff --git a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java index 3e15070b69..3750e3a198 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestConstantScoreQuery.java @@ -157,6 +157,11 @@ public class TestConstantScoreQuery extends LuceneTestCase { return in.createWeight(searcher, scoreMode, boost); } + @Override + public void visit(QueryVisitor visitor) { + in.visit(visitor); + } + @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java b/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java index 2e3a4f1aee..ee1fe50831 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestLRUQueryCache.java @@ -378,6 +378,11 @@ public class TestLRUQueryCache extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public boolean equals(Object other) { return sameClassAs(other) && @@ -972,6 +977,11 @@ public class TestLRUQueryCache extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "BadQuery"; @@ -1326,6 +1336,11 @@ public class TestLRUQueryCache extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "NoCacheQuery"; @@ -1410,6 +1425,11 @@ public class TestLRUQueryCache extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public boolean equals(Object other) { return sameClassAs(other); @@ -1494,6 +1514,11 @@ public class TestLRUQueryCache extends LuceneTestCase { }; } + + @Override + public void visit(QueryVisitor visitor) { + + } } public void testDocValuesUpdatesDontBreakCache() throws IOException { diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMultiTermQueryRewrites.java b/lucene/core/src/test/org/apache/lucene/search/TestMultiTermQueryRewrites.java index 44f8d8bd2c..e24e62b265 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestMultiTermQueryRewrites.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestMultiTermQueryRewrites.java @@ -183,6 +183,11 @@ public class TestMultiTermQueryRewrites extends LuceneTestCase { public String toString(String field) { return "dummy"; } + + @Override + public void visit(QueryVisitor visitor) { + + } }; mtq.setRewriteMethod(method); final Query q1 = searcher.rewrite(mtq); diff --git a/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java b/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java index 03f78ef875..3cdbb95a71 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestNeedsScores.java @@ -121,6 +121,11 @@ public class TestNeedsScores extends LuceneTestCase { } } + @Override + public void visit(QueryVisitor visitor) { + in.visit(visitor); + } + @Override public int hashCode() { final int prime = 31; diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPrefixRandom.java b/lucene/core/src/test/org/apache/lucene/search/TestPrefixRandom.java index f332a36a3a..818037992f 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestPrefixRandom.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestPrefixRandom.java @@ -108,6 +108,11 @@ public class TestPrefixRandom extends LuceneTestCase { return field.toString() + ":" + prefix.toString(); } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public boolean equals(Object obj) { if (super.equals(obj) == false) { diff --git a/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java b/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java index 2e9565358d..c507b6dae2 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestQueryRescorer.java @@ -499,6 +499,11 @@ public class TestQueryRescorer extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "FixedScoreQuery " + idToNum.length + " ids; reverse=" + reverse; diff --git a/lucene/core/src/test/org/apache/lucene/search/TestQueryVisitor.java b/lucene/core/src/test/org/apache/lucene/search/TestQueryVisitor.java new file mode 100644 index 0000000000..f1a4310547 --- /dev/null +++ b/lucene/core/src/test/org/apache/lucene/search/TestQueryVisitor.java @@ -0,0 +1,333 @@ +/* + * 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.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.lucene.index.Term; +import org.apache.lucene.search.spans.SpanNearQuery; +import org.apache.lucene.search.spans.SpanQuery; +import org.apache.lucene.search.spans.SpanTermQuery; +import org.apache.lucene.util.LuceneTestCase; + +import static org.hamcrest.CoreMatchers.equalTo; + +public class TestQueryVisitor extends LuceneTestCase { + + private static final Query query = new BooleanQuery.Builder() + .add(new TermQuery(new Term("field1", "t1")), BooleanClause.Occur.MUST) + .add(new BooleanQuery.Builder() + .add(new TermQuery(new Term("field1", "tm2")), BooleanClause.Occur.SHOULD) + .add(new BoostQuery(new TermQuery(new Term("field1", "tm3")), 2), BooleanClause.Occur.SHOULD) + .build(), BooleanClause.Occur.MUST) + .add(new BoostQuery(new PhraseQuery.Builder() + .add(new Term("field1", "term4")) + .add(new Term("field1", "term5")) + .build(), 3), BooleanClause.Occur.MUST) + .add(new SpanNearQuery(new SpanQuery[]{ + new SpanTermQuery(new Term("field1", "term6")), + new SpanTermQuery(new Term("field1", "term7")) + }, 2, true), BooleanClause.Occur.MUST) + .add(new TermQuery(new Term("field1", "term8")), BooleanClause.Occur.MUST_NOT) + .add(new PrefixQuery(new Term("field1", "term9")), BooleanClause.Occur.SHOULD) + .add(new BoostQuery(new BooleanQuery.Builder() + .add(new BoostQuery(new TermQuery(new Term("field2", "term10")), 3), BooleanClause.Occur.MUST) + .build(), 2), BooleanClause.Occur.SHOULD) + .build(); + + public void testExtractTermsEquivalent() { + Set terms = new HashSet<>(); + Set expected = new HashSet<>(Arrays.asList( + new Term("field1", "t1"), new Term("field1", "tm2"), + new Term("field1", "tm3"), new Term("field1", "term4"), + new Term("field1", "term5"), new Term("field1", "term6"), + new Term("field1", "term7"), new Term("field2", "term10") + )); + query.visit(QueryVisitor.termCollector(terms)); + assertThat(terms, equalTo(expected)); + } + + public void extractAllTerms() { + Set terms = new HashSet<>(); + QueryVisitor visitor = new QueryVisitor() { + @Override + public void consumeTerms(Query query, Term... ts) { + terms.addAll(Arrays.asList(ts)); + } + @Override + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { + return this; + } + }; + Set expected = new HashSet<>(Arrays.asList( + new Term("field1", "t1"), new Term("field1", "tm2"), + new Term("field1", "tm3"), new Term("field1", "term4"), + new Term("field1", "term5"), new Term("field1", "term6"), + new Term("field1", "term7"), new Term("field1", "term8"), + new Term("field2", "term10") + )); + query.visit(visitor); + assertThat(terms, equalTo(expected)); + } + + public void extractTermsFromField() { + final Set actual = new HashSet<>(); + Set expected = new HashSet<>(Arrays.asList(new Term("field2", "term10"))); + query.visit(new QueryVisitor(){ + @Override + public boolean acceptField(String field) { + return "field2".equals(field); + } + @Override + public void consumeTerms(Query query, Term... terms) { + actual.addAll(Arrays.asList(terms)); + } + }); + assertThat(actual, equalTo(expected)); + } + + static class BoostedTermExtractor extends QueryVisitor { + + final float boost; + final Map termsToBoosts; + + BoostedTermExtractor(float boost, Map termsToBoosts) { + this.boost = boost; + this.termsToBoosts = termsToBoosts; + } + + @Override + public void consumeTerms(Query query, Term... terms) { + for (Term term : terms) { + termsToBoosts.put(term, boost); + } + } + + @Override + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { + if (parent instanceof BoostQuery) { + return new BoostedTermExtractor(boost * ((BoostQuery)parent).getBoost(), termsToBoosts); + } + return super.getSubVisitor(occur, parent); + } + } + + public void testExtractTermsAndBoosts() { + Map termsToBoosts = new HashMap<>(); + query.visit(new BoostedTermExtractor(1, termsToBoosts)); + Map expected = new HashMap<>(); + expected.put(new Term("field1", "t1"), 1f); + expected.put(new Term("field1", "tm2"), 1f); + expected.put(new Term("field1", "tm3"), 2f); + expected.put(new Term("field1", "term4"), 3f); + expected.put(new Term("field1", "term5"), 3f); + expected.put(new Term("field1", "term6"), 1f); + expected.put(new Term("field1", "term7"), 1f); + expected.put(new Term("field2", "term10"), 6f); + assertThat(termsToBoosts, equalTo(expected)); + } + + public void testLeafQueryTypeCounts() { + Map, Integer> queryCounts = new HashMap<>(); + query.visit(new QueryVisitor() { + + private void countQuery(Query q) { + queryCounts.compute(q.getClass(), (query, i) -> { + if (i == null) { + return 1; + } + return i + 1; + }); + } + + @Override + public void consumeTerms(Query query, Term... terms) { + countQuery(query); + } + + @Override + public void visitLeaf(Query query) { + countQuery(query); + } + + }); + assertEquals(4, queryCounts.get(TermQuery.class).intValue()); + assertEquals(1, queryCounts.get(PhraseQuery.class).intValue()); + } + + static abstract class QueryNode extends QueryVisitor { + + final List children = new ArrayList<>(); + + abstract int getWeight(); + abstract void collectTerms(Set terms); + abstract boolean nextTermSet(); + + @Override + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { + if (occur == BooleanClause.Occur.MUST || occur == BooleanClause.Occur.FILTER) { + QueryNode n = new ConjunctionNode(); + children.add(n); + return n; + } + if (occur == BooleanClause.Occur.MUST_NOT) { + return QueryVisitor.EMPTY_VISITOR; + } + if (parent instanceof BooleanQuery) { + BooleanQuery bq = (BooleanQuery) parent; + if (bq.getClauses(BooleanClause.Occur.MUST).size() > 0 || bq.getClauses(BooleanClause.Occur.FILTER).size() > 0) { + return QueryVisitor.EMPTY_VISITOR; + } + } + DisjunctionNode n = new DisjunctionNode(); + children.add(n); + return n; + } + } + + static class TermNode extends QueryNode { + + final Term term; + + TermNode(Term term) { + this.term = term; + } + + @Override + int getWeight() { + return term.text().length(); + } + + @Override + void collectTerms(Set terms) { + terms.add(term); + } + + @Override + boolean nextTermSet() { + return false; + } + + @Override + public String toString() { + return "TERM(" + term.toString() + ")"; + } + } + + static class ConjunctionNode extends QueryNode { + + @Override + int getWeight() { + children.sort(Comparator.comparingInt(QueryNode::getWeight)); + return children.get(0).getWeight(); + } + + @Override + void collectTerms(Set terms) { + children.sort(Comparator.comparingInt(QueryNode::getWeight)); + children.get(0).collectTerms(terms); + } + + @Override + boolean nextTermSet() { + children.sort(Comparator.comparingInt(QueryNode::getWeight)); + if (children.get(0).nextTermSet()) { + return true; + } + if (children.size() == 1) { + return false; + } + children.remove(0); + return true; + } + + @Override + public void consumeTerms(Query query, Term... terms) { + for (Term term : terms) { + children.add(new TermNode(term)); + } + } + + @Override + public String toString() { + return children.stream().map(QueryNode::toString).collect(Collectors.joining(",", "AND(", ")")); + } + } + + static class DisjunctionNode extends QueryNode { + + @Override + int getWeight() { + children.sort(Comparator.comparingInt(QueryNode::getWeight).reversed()); + return children.get(0).getWeight(); + } + + @Override + void collectTerms(Set terms) { + for (QueryNode child : children) { + child.collectTerms(terms); + } + } + + @Override + boolean nextTermSet() { + boolean next = false; + for (QueryNode child : children) { + next |= child.nextTermSet(); + } + return next; + } + + @Override + public void consumeTerms(Query query, Term... terms) { + for (Term term : terms) { + children.add(new TermNode(term)); + } + } + + @Override + public String toString() { + return children.stream().map(QueryNode::toString).collect(Collectors.joining(",", "OR(", ")")); + } + } + + public void testExtractMatchingTermSet() { + QueryNode extractor = new ConjunctionNode(); + query.visit(extractor); + Set minimumTermSet = new HashSet<>(); + extractor.collectTerms(minimumTermSet); + + Set expected1 = new HashSet<>(Collections.singletonList(new Term("field1", "t1"))); + assertThat(minimumTermSet, equalTo(expected1)); + assertTrue(extractor.nextTermSet()); + Set expected2 = new HashSet<>(Arrays.asList(new Term("field1", "tm2"), new Term("field1", "tm3"))); + minimumTermSet.clear(); + extractor.collectTerms(minimumTermSet); + assertThat(minimumTermSet, equalTo(expected2)); + } + +} diff --git a/lucene/core/src/test/org/apache/lucene/search/TestRegexpRandom2.java b/lucene/core/src/test/org/apache/lucene/search/TestRegexpRandom2.java index 78a4352534..9c2810c5bc 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestRegexpRandom2.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestRegexpRandom2.java @@ -40,9 +40,9 @@ import org.apache.lucene.util.CharsRefBuilder; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.TestUtil; import org.apache.lucene.util.UnicodeUtil; +import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.AutomatonTestUtil; import org.apache.lucene.util.automaton.CharacterRunAutomaton; -import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.RegExp; /** @@ -138,6 +138,11 @@ public class TestRegexpRandom2 extends LuceneTestCase { return field.toString() + automaton.toString(); } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public boolean equals(Object obj) { if (super.equals(obj) == false) { diff --git a/lucene/core/src/test/org/apache/lucene/search/TestScorerPerf.java b/lucene/core/src/test/org/apache/lucene/search/TestScorerPerf.java index 60363d2024..b0a367a404 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestScorerPerf.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestScorerPerf.java @@ -162,7 +162,12 @@ public class TestScorerPerf extends LuceneTestCase { } }; } - + + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "randomBitSetFilter"; diff --git a/lucene/core/src/test/org/apache/lucene/search/TestSortRandom.java b/lucene/core/src/test/org/apache/lucene/search/TestSortRandom.java index 136026e040..9de337044a 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestSortRandom.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestSortRandom.java @@ -257,6 +257,11 @@ public class TestSortRandom extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "RandomFilter(density=" + density + ")"; diff --git a/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java b/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java index c8176237d1..74112e8cff 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestUsageTrackingFilterCachingPolicy.java @@ -132,6 +132,11 @@ public class TestUsageTrackingFilterCachingPolicy extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + } } diff --git a/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java b/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java index 7d1b2cb739..2a48cb3b1e 100644 --- a/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java +++ b/lucene/core/src/test/org/apache/lucene/search/TestWANDScorer.java @@ -354,6 +354,11 @@ public class TestWANDScorer extends LuceneTestCase { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new FilterWeight(query.createWeight(searcher, scoreMode, boost)) { diff --git a/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java b/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java index 3244c1d5ef..42e4c3c134 100644 --- a/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java +++ b/lucene/core/src/test/org/apache/lucene/search/spans/JustCompileSearchSpans.java @@ -20,6 +20,7 @@ package org.apache.lucene.search.spans; import java.io.IOException; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; /** @@ -98,6 +99,11 @@ final class JustCompileSearchSpans { throw new UnsupportedOperationException(UNSUPPORTED_MSG); } + @Override + public void visit(QueryVisitor visitor) { + throw new UnsupportedOperationException(UNSUPPORTED_MSG); + } + @Override public String toString(String field) { throw new UnsupportedOperationException(UNSUPPORTED_MSG); diff --git a/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java b/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java index f72ea664b9..4005ae5749 100644 --- a/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java +++ b/lucene/core/src/test/org/apache/lucene/search/spans/TestFieldMaskingSpanQuery.java @@ -17,6 +17,9 @@ package org.apache.lucene.search.spans; +import java.util.HashSet; +import java.util.Set; + import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; @@ -27,6 +30,7 @@ import org.apache.lucene.search.CheckHits; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryUtils; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.similarities.TFIDFSimilarity; import org.apache.lucene.store.Directory; @@ -34,9 +38,6 @@ import org.apache.lucene.util.LuceneTestCase; import org.junit.AfterClass; import org.junit.BeforeClass; -import java.util.HashSet; -import java.util.Set; - import static org.apache.lucene.search.spans.SpanTestUtil.assertFinished; import static org.apache.lucene.search.spans.SpanTestUtil.assertNext; @@ -143,7 +144,7 @@ public class TestFieldMaskingSpanQuery extends LuceneTestCase { QueryUtils.checkEqual(q, qr); Set terms = new HashSet<>(); - qr.createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, 1f).extractTerms(terms); + qr.visit(QueryVisitor.termCollector(terms)); assertEquals(1, terms.size()); } @@ -163,7 +164,7 @@ public class TestFieldMaskingSpanQuery extends LuceneTestCase { QueryUtils.checkUnequal(q, qr); Set terms = new HashSet<>(); - qr.createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, 1f).extractTerms(terms); + qr.visit(QueryVisitor.termCollector(terms)); assertEquals(2, terms.size()); } @@ -177,7 +178,7 @@ public class TestFieldMaskingSpanQuery extends LuceneTestCase { QueryUtils.checkEqual(q, qr); HashSet set = new HashSet<>(); - qr.createWeight(searcher, ScoreMode.COMPLETE, 1f).extractTerms(set); + qr.visit(QueryVisitor.termCollector(set)); assertEquals(2, set.size()); } diff --git a/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java b/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java index 1ea2613ac3..badfe81f7d 100644 --- a/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java +++ b/lucene/facet/src/java/org/apache/lucene/facet/DrillDownQuery.java @@ -30,6 +30,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TermQuery; /** @@ -149,6 +150,11 @@ public final class DrillDownQuery extends Query { return getBooleanQuery().toString(field); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + private BooleanQuery getBooleanQuery() { BooleanQuery.Builder bq = new BooleanQuery.Builder(); if (baseQuery != null) { diff --git a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java index 82c642a05c..259e98c737 100644 --- a/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java +++ b/lucene/facet/src/java/org/apache/lucene/facet/DrillSidewaysQuery.java @@ -33,6 +33,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -78,6 +79,11 @@ class DrillSidewaysQuery extends Query { return new DrillSidewaysQuery(newQuery, drillDownCollector, drillSidewaysCollectors, drillDownQueries, scoreSubDocsAtOnce); } } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java b/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java index afc4c4fd61..ad0ba67dd8 100644 --- a/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java +++ b/lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java @@ -28,6 +28,7 @@ import org.apache.lucene.search.DoubleValues; import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -141,6 +142,11 @@ public final class DoubleRange extends Range { return "Filter(" + range.toString() + ")"; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Query rewrite(IndexReader reader) throws IOException { if (fastMatchQuery != null) { diff --git a/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java b/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java index eed293f3f9..f69ba76612 100644 --- a/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java +++ b/lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java @@ -28,6 +28,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LongValues; import org.apache.lucene.search.LongValuesSource; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -133,6 +134,11 @@ public final class LongRange extends Range { return "Filter(" + range.toString() + ")"; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Query rewrite(IndexReader reader) throws IOException { if (fastMatchQuery != null) { diff --git a/lucene/facet/src/test/org/apache/lucene/facet/TestDrillSideways.java b/lucene/facet/src/test/org/apache/lucene/facet/TestDrillSideways.java index 288f6cc509..6dc790d6a1 100644 --- a/lucene/facet/src/test/org/apache/lucene/facet/TestDrillSideways.java +++ b/lucene/facet/src/test/org/apache/lucene/facet/TestDrillSideways.java @@ -52,6 +52,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; @@ -749,6 +750,11 @@ public class TestDrillSideways extends FacetTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "drillSidewaysTestFilter"; diff --git a/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java b/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java index 2408286782..441675c9e4 100644 --- a/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java +++ b/lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java @@ -53,6 +53,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LongValuesSource; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -714,6 +715,11 @@ public class TestRangeFacetCounts extends FacetTestCase { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + in.visit(visitor); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { final Weight in = this.in.createWeight(searcher, scoreMode, boost); diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/QueryTermExtractor.java b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/QueryTermExtractor.java index 9d3784ae11..0b5738f492 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/QueryTermExtractor.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/QueryTermExtractor.java @@ -15,19 +15,20 @@ * limitations under the License. */ package org.apache.lucene.search.highlight; + import java.io.IOException; import java.util.HashSet; -import java.util.Iterator; +import java.util.Set; +import java.util.function.Predicate; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.MultiReader; import org.apache.lucene.index.Term; import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; -import org.apache.lucene.search.ScoreMode; +import org.apache.lucene.search.QueryVisitor; /** * Utility class used to extract the terms used in a query, plus any weights. @@ -100,10 +101,10 @@ public final class QueryTermExtractor * @param fieldName The fieldName used to filter query terms * @return an array of the terms used in a query, plus their weights. */ - public static final WeightedTerm[] getTerms(Query query, boolean prohibited, String fieldName) - { - HashSet terms=new HashSet<>(); - getTerms(query, 1f, terms,prohibited,fieldName); + public static WeightedTerm[] getTerms(Query query, boolean prohibited, String fieldName) { + HashSet terms = new HashSet<>(); + Predicate fieldSelector = fieldName == null ? f -> true : fieldName::equals; + query.visit(new BoostedTermExtractor(1, terms, prohibited, fieldSelector)); return terms.toArray(new WeightedTerm[0]); } @@ -119,50 +120,45 @@ public final class QueryTermExtractor return getTerms(query,prohibited,null); } - private static final void getTerms(Query query, float boost, HashSet terms, boolean prohibited, String fieldName) { - try { - if (query instanceof BoostQuery) { - BoostQuery boostQuery = (BoostQuery) query; - getTerms(boostQuery.getQuery(), boost * boostQuery.getBoost(), terms, prohibited, fieldName); - } else if (query instanceof BooleanQuery) - getTermsFromBooleanQuery((BooleanQuery) query, boost, terms, prohibited, fieldName); - else { - HashSet nonWeightedTerms = new HashSet<>(); - try { - EMPTY_INDEXSEARCHER.createWeight(EMPTY_INDEXSEARCHER.rewrite(query), ScoreMode.COMPLETE_NO_SCORES, 1).extractTerms(nonWeightedTerms); - } catch (IOException bogus) { - throw new RuntimeException("Should not happen on an empty index", bogus); - } - for (Iterator iter = nonWeightedTerms.iterator(); iter.hasNext(); ) { - Term term = iter.next(); - if ((fieldName == null) || (term.field().equals(fieldName))) { - terms.add(new WeightedTerm(boost, term.text())); - } - } + private static class BoostedTermExtractor extends QueryVisitor { + + final float boost; + final Set terms; + final boolean includeProhibited; + final Predicate fieldSelector; + + private BoostedTermExtractor(float boost, Set terms, boolean includeProhibited, + Predicate fieldSelector) { + this.boost = boost; + this.terms = terms; + this.includeProhibited = includeProhibited; + this.fieldSelector = fieldSelector; + } + + @Override + public boolean acceptField(String field) { + return fieldSelector.test(field); + } + + @Override + public void consumeTerms(Query query, Term... terms) { + for (Term term : terms) { + this.terms.add(new WeightedTerm(boost, term.text())); } - } catch (UnsupportedOperationException ignore) { - //this is non-fatal for our purposes } - } - /** - * extractTerms is currently the only query-independent means of introspecting queries but it only reveals - * a list of terms for that query - not the boosts each individual term in that query may or may not have. - * "Container" queries such as BooleanQuery should be unwrapped to get at the boost info held - * in each child element. - * Some discussion around this topic here: - * http://www.gossamer-threads.com/lists/lucene/java-dev/34208?search_string=introspection;#34208 - * Unfortunately there seemed to be limited interest in requiring all Query objects to implement - * something common which would allow access to child queries so what follows here are query-specific - * implementations for accessing embedded query elements. - */ - private static final void getTermsFromBooleanQuery(BooleanQuery query, float boost, HashSet terms, boolean prohibited, String fieldName) - { - for (BooleanClause clause : query) - { - if (prohibited || clause.getOccur()!=BooleanClause.Occur.MUST_NOT) - getTerms(clause.getQuery(), boost, terms, prohibited, fieldName); + @Override + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { + if (parent instanceof BoostQuery) { + float newboost = boost * ((BoostQuery)parent).getBoost(); + return new BoostedTermExtractor(newboost, terms, includeProhibited, fieldSelector); + } + if (occur == BooleanClause.Occur.MUST_NOT && includeProhibited == false) { + return QueryVisitor.EMPTY_VISITOR; + } + return this; } + } } diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java index bc026210c2..ee0a1875a9 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/highlight/WeightedSpanTermExtractor.java @@ -51,6 +51,7 @@ import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.SynonymQuery; import org.apache.lucene.search.TermQuery; @@ -308,10 +309,10 @@ public class WeightedSpanTermExtractor { for (final String field : fieldNames) { final SpanQuery rewrittenQuery = (SpanQuery) spanQuery.rewrite(getLeafContext().reader()); queries.put(field, rewrittenQuery); - rewrittenQuery.createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, boost).extractTerms(nonWeightedTerms); + rewrittenQuery.visit(QueryVisitor.termCollector(nonWeightedTerms)); } } else { - spanQuery.createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, boost).extractTerms(nonWeightedTerms); + spanQuery.visit(QueryVisitor.termCollector(nonWeightedTerms)); } List spanPositions = new ArrayList<>(); @@ -378,7 +379,7 @@ public class WeightedSpanTermExtractor { protected void extractWeightedTerms(Map terms, Query query, float boost) throws IOException { Set nonWeightedTerms = new HashSet<>(); final IndexSearcher searcher = new IndexSearcher(getLeafContext()); - searcher.createWeight(searcher.rewrite(query), ScoreMode.COMPLETE_NO_SCORES, 1).extractTerms(nonWeightedTerms); + searcher.rewrite(query).visit(QueryVisitor.termCollector(nonWeightedTerms)); for (final Term queryTerm : nonWeightedTerms) { diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MemoryIndexOffsetStrategy.java b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MemoryIndexOffsetStrategy.java index 30dbdd4da4..1f4455f4bd 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MemoryIndexOffsetStrategy.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MemoryIndexOffsetStrategy.java @@ -19,10 +19,8 @@ package org.apache.lucene.search.uhighlight; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.function.Function; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.FilteringTokenFilter; @@ -30,7 +28,6 @@ import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.memory.MemoryIndex; -import org.apache.lucene.search.Query; import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.util.automaton.Automata; import org.apache.lucene.util.automaton.CharacterRunAutomaton; @@ -47,21 +44,19 @@ public class MemoryIndexOffsetStrategy extends AnalysisOffsetStrategy { private final LeafReader memIndexLeafReader; private final CharacterRunAutomaton preMemIndexFilterAutomaton; - public MemoryIndexOffsetStrategy(UHComponents components, Analyzer analyzer, - Function> multiTermQueryRewrite) { + public MemoryIndexOffsetStrategy(UHComponents components, Analyzer analyzer) { super(components, analyzer); boolean storePayloads = components.getPhraseHelper().hasPositionSensitivity(); // might be needed memoryIndex = new MemoryIndex(true, storePayloads);//true==store offsets memIndexLeafReader = (LeafReader) memoryIndex.createSearcher().getIndexReader(); // appears to be re-usable // preFilter for MemoryIndex - preMemIndexFilterAutomaton = buildCombinedAutomaton(components, multiTermQueryRewrite); + preMemIndexFilterAutomaton = buildCombinedAutomaton(components); } /** * Build one {@link CharacterRunAutomaton} matching any term the query might match. */ - private static CharacterRunAutomaton buildCombinedAutomaton(UHComponents components, - Function> multiTermQueryRewrite) { + private static CharacterRunAutomaton buildCombinedAutomaton(UHComponents components) { List allAutomata = new ArrayList<>(); if (components.getTerms().length > 0) { allAutomata.add(new CharacterRunAutomaton(Automata.makeStringUnion(Arrays.asList(components.getTerms())))); @@ -69,7 +64,7 @@ public class MemoryIndexOffsetStrategy extends AnalysisOffsetStrategy { Collections.addAll(allAutomata, components.getAutomata()); for (SpanQuery spanQuery : components.getPhraseHelper().getSpanQueries()) { Collections.addAll(allAutomata, - MultiTermHighlighting.extractAutomata(spanQuery, components.getFieldMatcher(), true, multiTermQueryRewrite));//true==lookInSpan + MultiTermHighlighting.extractAutomata(spanQuery, components.getFieldMatcher(), true));//true==lookInSpan } if (allAutomata.size() == 1) { diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MultiTermHighlighting.java b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MultiTermHighlighting.java index 57d5afad07..d079599d82 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MultiTermHighlighting.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/MultiTermHighlighting.java @@ -17,33 +17,20 @@ package org.apache.lucene.search.uhighlight; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.List; -import java.util.function.Function; import java.util.function.Predicate; -import org.apache.lucene.queries.function.FunctionScoreQuery; import org.apache.lucene.search.AutomatonQuery; import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.BoostQuery; -import org.apache.lucene.search.ConstantScoreQuery; -import org.apache.lucene.search.DisjunctionMaxQuery; import org.apache.lucene.search.FuzzyQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.spans.SpanBoostQuery; -import org.apache.lucene.search.spans.SpanMultiTermQueryWrapper; -import org.apache.lucene.search.spans.SpanNearQuery; -import org.apache.lucene.search.spans.SpanNotQuery; -import org.apache.lucene.search.spans.SpanOrQuery; -import org.apache.lucene.search.spans.SpanPositionCheckQuery; +import org.apache.lucene.search.QueryVisitor; +import org.apache.lucene.search.spans.SpanQuery; import org.apache.lucene.util.UnicodeUtil; import org.apache.lucene.util.automaton.Automata; import org.apache.lucene.util.automaton.Automaton; import org.apache.lucene.util.automaton.ByteRunAutomaton; import org.apache.lucene.util.automaton.CharacterRunAutomaton; -import org.apache.lucene.util.automaton.LevenshteinAutomata; import org.apache.lucene.util.automaton.Operations; /** @@ -51,7 +38,7 @@ import org.apache.lucene.util.automaton.Operations; * * @lucene.internal */ -class MultiTermHighlighting { +final class MultiTermHighlighting { private MultiTermHighlighting() { } @@ -59,138 +46,117 @@ class MultiTermHighlighting { * Extracts MultiTermQueries that match the provided field predicate. * Returns equivalent automata that will match terms. */ - public static CharacterRunAutomaton[] extractAutomata(Query query, - Predicate fieldMatcher, - boolean lookInSpan, - Function> preRewriteFunc) { - // TODO Lucene needs a Query visitor API! LUCENE-3041 - - List list = new ArrayList<>(); - Collection customSubQueries = preRewriteFunc.apply(query); - if (customSubQueries != null) { - for (Query sub : customSubQueries) { - list.addAll(Arrays.asList(extractAutomata(sub, fieldMatcher, lookInSpan, preRewriteFunc))); - } - } else if (query instanceof BooleanQuery) { - for (BooleanClause clause : (BooleanQuery) query) { - if (!clause.isProhibited()) { - list.addAll(Arrays.asList(extractAutomata(clause.getQuery(), fieldMatcher, lookInSpan, preRewriteFunc))); - } - } - } else if (query instanceof ConstantScoreQuery) { - list.addAll(Arrays.asList(extractAutomata(((ConstantScoreQuery) query).getQuery(), fieldMatcher, lookInSpan, - preRewriteFunc))); - } else if (query instanceof BoostQuery) { - list.addAll(Arrays.asList(extractAutomata(((BoostQuery) query).getQuery(), fieldMatcher, lookInSpan, - preRewriteFunc))); - } else if (query instanceof FunctionScoreQuery) { - list.addAll(Arrays.asList(extractAutomata(((FunctionScoreQuery) query).getWrappedQuery(), fieldMatcher, - lookInSpan, preRewriteFunc))); - } else if (query instanceof DisjunctionMaxQuery) { - for (Query sub : ((DisjunctionMaxQuery) query).getDisjuncts()) { - list.addAll(Arrays.asList(extractAutomata(sub, fieldMatcher, lookInSpan, preRewriteFunc))); - } - } else if (lookInSpan && query instanceof SpanOrQuery) { - for (Query sub : ((SpanOrQuery) query).getClauses()) { - list.addAll(Arrays.asList(extractAutomata(sub, fieldMatcher, lookInSpan, preRewriteFunc))); - } - } else if (lookInSpan && query instanceof SpanNearQuery) { - for (Query sub : ((SpanNearQuery) query).getClauses()) { - list.addAll(Arrays.asList(extractAutomata(sub, fieldMatcher, lookInSpan, preRewriteFunc))); - } - } else if (lookInSpan && query instanceof SpanNotQuery) { - list.addAll(Arrays.asList(extractAutomata(((SpanNotQuery) query).getInclude(), fieldMatcher, lookInSpan, - preRewriteFunc))); - } else if (lookInSpan && query instanceof SpanPositionCheckQuery) { - list.addAll(Arrays.asList(extractAutomata(((SpanPositionCheckQuery) query).getMatch(), fieldMatcher, lookInSpan, - preRewriteFunc))); - } else if (lookInSpan && query instanceof SpanBoostQuery) { - list.addAll(Arrays.asList(extractAutomata(((SpanBoostQuery) query).getQuery(), fieldMatcher, lookInSpan, - preRewriteFunc))); - } else if (lookInSpan && query instanceof SpanMultiTermQueryWrapper) { - list.addAll(Arrays.asList(extractAutomata(((SpanMultiTermQueryWrapper) query).getWrappedQuery(), - fieldMatcher, lookInSpan, preRewriteFunc))); - } else if (query instanceof FuzzyQuery) { - final FuzzyQuery fq = (FuzzyQuery) query; - if (fieldMatcher.test(fq.getField())) { - String utf16 = fq.getTerm().text(); - int termText[] = new int[utf16.codePointCount(0, utf16.length())]; - for (int cp, i = 0, j = 0; i < utf16.length(); i += Character.charCount(cp)) { - termText[j++] = cp = utf16.codePointAt(i); - } - int termLength = termText.length; - int prefixLength = Math.min(fq.getPrefixLength(), termLength); - String suffix = UnicodeUtil.newString(termText, prefixLength, termText.length - prefixLength); - LevenshteinAutomata builder = new LevenshteinAutomata(suffix, fq.getTranspositions()); - String prefix = UnicodeUtil.newString(termText, 0, prefixLength); - Automaton automaton = builder.toAutomaton(fq.getMaxEdits(), prefix); - list.add(new CharacterRunAutomaton(automaton) { - @Override - public String toString() { - return fq.toString(); - } - }); + static CharacterRunAutomaton[] extractAutomata(Query query, Predicate fieldMatcher, boolean lookInSpan) { + + AutomataCollector collector = new AutomataCollector(lookInSpan, fieldMatcher); + query.visit(collector); + return collector.runAutomata.toArray(new CharacterRunAutomaton[0]); + } + + private static class AutomataCollector extends QueryVisitor { + + List runAutomata = new ArrayList<>(); + final boolean lookInSpan; + final Predicate fieldMatcher; + + private AutomataCollector(boolean lookInSpan, Predicate fieldMatcher) { + this.lookInSpan = lookInSpan; + this.fieldMatcher = fieldMatcher; + } + + @Override + public boolean acceptField(String field) { + return fieldMatcher.test(field); + } + + @Override + public QueryVisitor getSubVisitor(BooleanClause.Occur occur, Query parent) { + if (lookInSpan == false && parent instanceof SpanQuery) { + return QueryVisitor.EMPTY_VISITOR; } - } else if (query instanceof AutomatonQuery) { - final AutomatonQuery aq = (AutomatonQuery) query; - if (fieldMatcher.test(aq.getField())) { + return super.getSubVisitor(occur, parent); + } - if (aq.isAutomatonBinary() == false) { // note: is the case for WildcardQuery, RegexpQuery - list.add(new CharacterRunAutomaton(aq.getAutomaton()) { + @Override + public void visitLeaf(Query query) { + if (query instanceof AutomatonQuery) { + AutomatonQuery aq = (AutomatonQuery) query; + if (aq.isAutomatonBinary() == false) { + // WildcardQuery, RegexpQuery + runAutomata.add(new CharacterRunAutomaton(aq.getAutomaton()) { @Override public String toString() { - return aq.toString(); + return query.toString(); } }); - } else { // note: is the case for PrefixQuery, TermRangeQuery - // byte oriented automaton: - list.add(new CharacterRunAutomaton(Automata.makeEmpty()) { // empty here is bogus just to satisfy API - // TODO can we get access to the aq.compiledAutomaton.runAutomaton ? - ByteRunAutomaton byteRunAutomaton = - new ByteRunAutomaton(aq.getAutomaton(), true, Operations.DEFAULT_MAX_DETERMINIZED_STATES); - - @Override - public boolean run(char[] chars, int offset, int length) { - int state = 0; - final int maxIdx = offset + length; - for (int i = offset; i < maxIdx; i++) { - final int code = chars[i]; - int b; - // UTF16 to UTF8 (inlined logic from UnicodeUtil.UTF16toUTF8 ) - if (code < 0x80) { - state = byteRunAutomaton.step(state, code); - if (state == -1) return false; - } else if (code < 0x800) { - b = (0xC0 | (code >> 6)); - state = byteRunAutomaton.step(state, b); - if (state == -1) return false; - b = (0x80 | (code & 0x3F)); - state = byteRunAutomaton.step(state, b); - if (state == -1) return false; - } else { - // more complex - byte[] utf8Bytes = new byte[4 * (maxIdx - i)]; - int utf8Len = UnicodeUtil.UTF16toUTF8(chars, i, maxIdx - i, utf8Bytes); - for (int utfIdx = 0; utfIdx < utf8Len; utfIdx++) { - state = byteRunAutomaton.step(state, utf8Bytes[utfIdx] & 0xFF); - if (state == -1) return false; - } - break; - } - } - return byteRunAutomaton.isAccept(state); - } - + } + else { + runAutomata.add(binaryToCharRunAutomaton(aq.getAutomaton(), query.toString())); + } + } + else if (query instanceof FuzzyQuery) { + FuzzyQuery fq = (FuzzyQuery) query; + if (fq.getMaxEdits() == 0 || fq.getPrefixLength() >= fq.getTerm().text().length()) { + consumeTerms(query, fq.getTerm()); + } + else { + runAutomata.add(new CharacterRunAutomaton(fq.toAutomaton()){ @Override public String toString() { - return aq.toString(); + return query.toString(); } }); } - } } - return list.toArray(new CharacterRunAutomaton[list.size()]); + } + private static CharacterRunAutomaton binaryToCharRunAutomaton(Automaton binaryAutomaton, String description) { + return new CharacterRunAutomaton(Automata.makeEmpty()) { // empty here is bogus just to satisfy API + // TODO can we get access to the aq.compiledAutomaton.runAutomaton ? + ByteRunAutomaton byteRunAutomaton = + new ByteRunAutomaton(binaryAutomaton, true, Operations.DEFAULT_MAX_DETERMINIZED_STATES); + + @Override + public String toString() { + return description; + } + + @Override + public boolean run(char[] chars, int offset, int length) { + int state = 0; + final int maxIdx = offset + length; + for (int i = offset; i < maxIdx; i++) { + final int code = chars[i]; + int b; + // UTF16 to UTF8 (inlined logic from UnicodeUtil.UTF16toUTF8 ) + if (code < 0x80) { + state = byteRunAutomaton.step(state, code); + if (state == -1) return false; + } else if (code < 0x800) { + b = (0xC0 | (code >> 6)); + state = byteRunAutomaton.step(state, b); + if (state == -1) return false; + b = (0x80 | (code & 0x3F)); + state = byteRunAutomaton.step(state, b); + if (state == -1) return false; + } else { + // more complex + byte[] utf8Bytes = new byte[4 * (maxIdx - i)]; + int utf8Len = UnicodeUtil.UTF16toUTF8(chars, i, maxIdx - i, utf8Bytes); + for (int utfIdx = 0; utfIdx < utf8Len; utfIdx++) { + state = byteRunAutomaton.step(state, utf8Bytes[utfIdx] & 0xFF); + if (state == -1) return false; + } + break; + } + } + return byteRunAutomaton.isAccept(state); + } + }; + } + + + } diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/OffsetsEnum.java b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/OffsetsEnum.java index 0e6a221675..d3998833f4 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/OffsetsEnum.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/OffsetsEnum.java @@ -23,14 +23,13 @@ import java.util.HashMap; import java.util.List; import java.util.Objects; import java.util.PriorityQueue; -import java.util.TreeSet; import java.util.function.Supplier; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.Term; import org.apache.lucene.search.MatchesIterator; import org.apache.lucene.search.Query; -import org.apache.lucene.search.ScoreMode; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefBuilder; import org.apache.lucene.util.IOUtils; @@ -245,30 +244,24 @@ public abstract class OffsetsEnum implements Comparable, Closeable * See {@link Passage#getMatchTerms()}. */ private BytesRef queryToTerm(Query query) { // compute an approximate BytesRef term of a Query. We cache this since we're likely to see the same query again. - // Our approach is to call extractTerms and visit each term in order, concatenating them with an adjoining space. + // Our approach is to visit each matching term in order, concatenating them with an adjoining space. // If we don't have any (perhaps due to an MTQ like a wildcard) then we fall back on the toString() of the query. return queryToTermMap.computeIfAbsent(query, (Query q) -> { - try { - BytesRefBuilder bytesRefBuilder = new BytesRefBuilder(); - UnifiedHighlighter.EMPTY_INDEXSEARCHER - .createWeight(UnifiedHighlighter.EMPTY_INDEXSEARCHER.rewrite(q), ScoreMode.COMPLETE_NO_SCORES, 1f) - .extractTerms(new TreeSet() { - @Override - public boolean add(Term term) { + BytesRefBuilder bytesRefBuilder = new BytesRefBuilder(); + q.visit(new QueryVisitor() { + @Override + public void consumeTerms(Query query, Term... terms) { + for (Term term : terms) { if (bytesRefBuilder.length() > 0) { bytesRefBuilder.append((byte) ' '); } bytesRefBuilder.append(term.bytes()); - return true; } - }); - if (bytesRefBuilder.length() > 0) { - return bytesRefBuilder.get(); } - } catch (IOException e) {//ignore - // go to fallback... + }); + if (bytesRefBuilder.length() > 0) { + return bytesRefBuilder.get(); } - // fallback: (likely a MultiTermQuery) return new BytesRef(q.toString()); }); diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/PhraseHelper.java b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/PhraseHelper.java index ab439d09a2..47b770dfca 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/PhraseHelper.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/PhraseHelper.java @@ -24,7 +24,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.TreeSet; import java.util.function.Function; import java.util.function.Predicate; @@ -40,6 +39,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -97,20 +97,6 @@ public class PhraseHelper { boolean[] mustRewriteHolder = {false}; // boolean wrapped in 1-ary array so it's mutable from inner class - // When we call Weight.extractTerms, we do it on clauses that are NOT position sensitive. - // We only want the to track a Set of bytes for the Term, not Term class with field part. - Set extractPosInsensitiveTermsTarget = new TreeSet() { - @Override - public boolean add(Term term) { - // don't call super.add; we don't actually use the superclass - if (fieldMatcher.test(term.field())) { - return positionInsensitiveTerms.add(term.bytes()); - } else { - return false; - } - } - }; - // For TermQueries or other position insensitive queries, collect the Terms. // For other Query types, WSTE will convert to an equivalent SpanQuery. NOT extracting position spans here. new WeightedSpanTermExtractor(field) { @@ -147,10 +133,19 @@ public class PhraseHelper { // called on Query types that are NOT position sensitive, e.g. TermQuery @Override - protected void extractWeightedTerms(Map terms, Query query, float boost) - throws IOException { - query.createWeight(UnifiedHighlighter.EMPTY_INDEXSEARCHER, ScoreMode.COMPLETE_NO_SCORES, boost) - .extractTerms(extractPosInsensitiveTermsTarget); + protected void extractWeightedTerms(Map terms, Query query, float boost) { + query.visit(new QueryVisitor() { + @Override + public boolean acceptField(String field) { + return fieldMatcher.test(field); + } + @Override + public void consumeTerms(Query query, Term... terms) { + for (Term term : terms) { + positionInsensitiveTerms.add(term.bytes()); + } + } + }); } // called on SpanQueries. Some other position-sensitive queries like PhraseQuery are converted beforehand diff --git a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/UnifiedHighlighter.java b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/UnifiedHighlighter.java index 7dcac30626..f4b1ab1b7b 100644 --- a/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/UnifiedHighlighter.java +++ b/lucene/highlighter/src/java/org/apache/lucene/search/uhighlight/UnifiedHighlighter.java @@ -54,8 +54,8 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.Weight; import org.apache.lucene.search.spans.SpanQuery; @@ -140,11 +140,11 @@ public class UnifiedHighlighter { private int cacheFieldValCharsThreshold = DEFAULT_CACHE_CHARS_THRESHOLD; /** - * Calls {@link Weight#extractTerms(Set)} on an empty index for the query. + * Extracts matching terms after rewriting against an empty index */ protected static Set extractTerms(Query query) throws IOException { Set queryTerms = new HashSet<>(); - EMPTY_INDEXSEARCHER.createWeight(EMPTY_INDEXSEARCHER.rewrite(query), ScoreMode.COMPLETE_NO_SCORES, 1).extractTerms(queryTerms); + EMPTY_INDEXSEARCHER.rewrite(query).visit(QueryVisitor.termCollector(queryTerms)); return queryTerms; } @@ -816,7 +816,7 @@ public class UnifiedHighlighter { || highlightFlags.contains(HighlightFlag.WEIGHT_MATCHES); // Weight.Matches will find all return highlightFlags.contains(HighlightFlag.MULTI_TERM_QUERY) - ? MultiTermHighlighting.extractAutomata(query, getFieldMatcher(field), lookInSpan, this::preMultiTermQueryRewrite) + ? MultiTermHighlighting.extractAutomata(query, getFieldMatcher(field), lookInSpan) : ZERO_LEN_AUTOMATA_ARRAY; } @@ -863,7 +863,7 @@ public class UnifiedHighlighter { //skip using a memory index since it's pure term filtering return new TokenStreamOffsetStrategy(components, getIndexAnalyzer()); } else { - return new MemoryIndexOffsetStrategy(components, getIndexAnalyzer(), this::preMultiTermQueryRewrite); + return new MemoryIndexOffsetStrategy(components, getIndexAnalyzer()); } case NONE_NEEDED: return NoOpOffsetStrategy.INSTANCE; @@ -902,19 +902,6 @@ public class UnifiedHighlighter { return null; } - /** - * When dealing with multi term queries / span queries, we may need to handle custom queries that aren't supported - * by the default automata extraction in {@code MultiTermHighlighting}. This can be overridden to return a collection - * of queries if appropriate, or null if nothing to do. If query is not custom, simply returning null will allow the - * default rules to apply. - * - * @param query Query to be highlighted - * @return A Collection of Query object(s) if needst o be rewritten, otherwise null. - */ - protected Collection preMultiTermQueryRewrite(Query query) { - return null; - } - private DocIdSetIterator asDocIdSetIterator(int[] sortedDocIds) { return new DocIdSetIterator() { int idx = -1; diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java index 314cc160ba..3c34270305 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/HighlighterTest.java @@ -72,6 +72,7 @@ import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PhraseQuery.Builder; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; @@ -255,6 +256,11 @@ public class HighlighterTest extends BaseTokenStreamTestCase implements Formatte return query; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return null; diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java index a54687dbfb..2553a80875 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/highlight/custom/HighlightCustomQueryTest.java @@ -16,6 +16,11 @@ */ package org.apache.lucene.search.highlight.custom; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; + import org.apache.lucene.analysis.CannedTokenStream; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.analysis.MockTokenFilter; @@ -25,6 +30,7 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.highlight.Highlighter; import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; @@ -35,11 +41,6 @@ import org.apache.lucene.search.highlight.WeightedSpanTerm; import org.apache.lucene.search.highlight.WeightedSpanTermExtractor; import org.apache.lucene.util.LuceneTestCase; -import java.io.IOException; -import java.util.Collections; -import java.util.Map; -import java.util.Objects; - /** * Tests the extensibility of {@link WeightedSpanTermExtractor} and * {@link QueryScorer} in a user defined package @@ -175,6 +176,11 @@ public class HighlightCustomQueryTest extends LuceneTestCase { return new TermQuery(term); } + @Override + public void visit(QueryVisitor visitor) { + visitor.consumeTerms(this, term); + } + @Override public int hashCode() { return classHash() + Objects.hashCode(term); diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterMTQ.java b/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterMTQ.java index 44d6f7b0d6..34fd46f7fd 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterMTQ.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterMTQ.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.List; import java.util.Objects; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; @@ -50,6 +49,7 @@ import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Sort; @@ -999,15 +999,7 @@ public class TestUnifiedHighlighterMTQ extends LuceneTestCase { iw.close(); IndexSearcher searcher = newSearcher(ir); - UnifiedHighlighter highlighter = new UnifiedHighlighter(searcher, indexAnalyzer) { - @Override - protected List preMultiTermQueryRewrite(Query query) { - if (query instanceof MyWrapperSpanQuery) { - return Collections.singletonList(((MyWrapperSpanQuery) query).originalQuery); - } - return null; - } - }; + UnifiedHighlighter highlighter = new UnifiedHighlighter(searcher, indexAnalyzer); int docId = searcher.search(new TermQuery(new Term("id", "id")), 1).scoreDocs[0].doc; @@ -1051,6 +1043,11 @@ public class TestUnifiedHighlighterMTQ extends LuceneTestCase { return originalQuery.createWeight(searcher, scoreMode, boost); } + @Override + public void visit(QueryVisitor visitor) { + originalQuery.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + @Override public Query rewrite(IndexReader reader) throws IOException { Query newOriginalQuery = originalQuery.rewrite(reader); diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterStrictPhrases.java b/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterStrictPhrases.java index a6d5d01566..4c05e1b9f6 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterStrictPhrases.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/uhighlight/TestUnifiedHighlighterStrictPhrases.java @@ -42,6 +42,7 @@ import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Sort; @@ -588,6 +589,11 @@ public class TestUnifiedHighlighterStrictPhrases extends LuceneTestCase { public int hashCode() { return wrapped.hashCode(); } + + @Override + public void visit(QueryVisitor visitor) { + wrapped.visit(visitor); + } } // Ported from LUCENE-5455 (fixed in LUCENE-8121). Also see LUCENE-2287. diff --git a/lucene/highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java b/lucene/highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java index eb888f6e89..135d8a6aae 100644 --- a/lucene/highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java +++ b/lucene/highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java @@ -15,6 +15,7 @@ * limitations under the License. */ package org.apache.lucene.search.vectorhighlight; + import java.io.IOException; import java.util.ArrayList; import java.util.HashSet; @@ -29,6 +30,7 @@ import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.PrefixQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermRangeQuery; @@ -926,6 +928,12 @@ public class FieldQueryTest extends AbstractTestCase { public String toString(String field) { return "DummyQuery"; } + + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public boolean equals(Object o) { throw new AssertionError(); diff --git a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java index 8247f81352..6bfdd1b52c 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java @@ -17,7 +17,6 @@ package org.apache.lucene.search.join; import java.io.IOException; -import java.util.Set; import org.apache.lucene.index.DocValues; import org.apache.lucene.index.LeafReaderContext; @@ -29,6 +28,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.search.Weight; @@ -60,6 +60,11 @@ final class GlobalOrdinalsQuery extends Query { this.indexReaderContextId = indexReaderContextId; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, org.apache.lucene.search.ScoreMode scoreMode, float boost) throws IOException { if (searcher.getTopReaderContext().id() != indexReaderContextId) { @@ -107,9 +112,6 @@ final class GlobalOrdinalsQuery extends Query { this.approximationWeight = approximationWeight; } - @Override - public void extractTerms(Set terms) {} - @Override public Explanation explain(LeafReaderContext context, int doc) throws IOException { SortedDocValues values = DocValues.getSorted(context.reader(), joinField); diff --git a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java index cdcf070cc7..4cdf59b424 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java @@ -17,7 +17,6 @@ package org.apache.lucene.search.join; import java.io.IOException; -import java.util.Set; import org.apache.lucene.index.DocValues; import org.apache.lucene.index.LeafReaderContext; @@ -29,6 +28,7 @@ import org.apache.lucene.search.Explanation; import org.apache.lucene.search.FilterWeight; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.search.Weight; @@ -65,6 +65,11 @@ final class GlobalOrdinalsWithScoreQuery extends Query { this.indexReaderContextId = indexReaderContextId; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, org.apache.lucene.search.ScoreMode scoreMode, float boost) throws IOException { if (searcher.getTopReaderContext().id() != indexReaderContextId) { @@ -125,9 +130,6 @@ final class GlobalOrdinalsWithScoreQuery extends Query { super(query, approximationWeight); } - @Override - public void extractTerms(Set terms) {} - @Override public Explanation explain(LeafReaderContext context, int doc) throws IOException { SortedDocValues values = DocValues.getSorted(context.reader(), joinField); diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ParentChildrenBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ParentChildrenBlockJoinQuery.java index 7ce1c29459..47d1aa4ff0 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/ParentChildrenBlockJoinQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/ParentChildrenBlockJoinQuery.java @@ -28,6 +28,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BitSet; @@ -83,6 +84,11 @@ public class ParentChildrenBlockJoinQuery extends Query { return "ParentChildrenBlockJoinQuery (" + childQuery + ")"; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Query rewrite(IndexReader reader) throws IOException { final Query childRewrite = childQuery.rewrite(reader); diff --git a/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java index 80810e263d..01a8221590 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java @@ -42,6 +42,7 @@ import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.PointInSetQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BitSetIterator; @@ -119,6 +120,13 @@ abstract class PointInSetIncludingScoreQuery extends Query { sortedPackedPointsHashCode = sortedPackedPoints.hashCode(); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public final Weight createWeight(IndexSearcher searcher, org.apache.lucene.search.ScoreMode scoreMode, float boost) throws IOException { return new Weight(this) { diff --git a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java index 43ddd52839..1ae0ef26f2 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java @@ -30,6 +30,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BitSetIterator; @@ -71,6 +72,13 @@ class TermsIncludingScoreQuery extends Query { return String.format(Locale.ROOT, "TermsIncludingScoreQuery{field=%s;fromQuery=%s}", toField, fromQuery); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(toField)) { + visitor.visitLeaf(this); + } + } + @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java index 3ff0a5c9ab..53b9c40144 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java @@ -24,6 +24,7 @@ import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.util.AttributeSource; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefHash; @@ -59,6 +60,11 @@ class TermsQuery extends MultiTermQuery { this.indexReaderContextId = indexReaderContextId; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override protected TermsEnum getTermsEnum(Terms terms, AttributeSource atts) throws IOException { if (this.terms.size() == 0) { diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java index ff8059cc5f..11cc786fb4 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/ToChildBlockJoinQuery.java @@ -20,13 +20,15 @@ import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Locale; + import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.search.FilterWeight; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; +import org.apache.lucene.search.FilterWeight; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BitSet; @@ -64,6 +66,11 @@ public class ToChildBlockJoinQuery extends Query { this.parentsFilter = parentsFilter; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, org.apache.lucene.search.ScoreMode scoreMode, float boost) throws IOException { return new ToChildBlockJoinWeight(this, parentQuery.createWeight(searcher, scoreMode, boost), parentsFilter, scoreMode.needsScores()); diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java index 03bb5b95b2..c42cf39134 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentBlockJoinQuery.java @@ -31,6 +31,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Matches; import org.apache.lucene.search.MatchesUtils; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.ScorerSupplier; import org.apache.lucene.search.TwoPhaseIterator; @@ -85,6 +86,11 @@ public class ToParentBlockJoinQuery extends Query { this.scoreMode = scoreMode; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, org.apache.lucene.search.ScoreMode weightScoreMode, float boost) throws IOException { return new BlockJoinWeight(this, childQuery.createWeight(searcher, weightScoreMode, boost), parentsFilter, weightScoreMode.needsScores() ? scoreMode : ScoreMode.None); diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java index 1eb1adbf0d..d1f32fd8f3 100644 --- a/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java +++ b/lucene/join/src/test/org/apache/lucene/search/join/TestBlockJoin.java @@ -29,6 +29,7 @@ import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; @@ -63,8 +64,6 @@ import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.TestUtil; -import com.carrotsearch.randomizedtesting.generators.RandomPicks; - public class TestBlockJoin extends LuceneTestCase { // One resume... diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java b/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java index 59309fa07d..5e87683c6b 100644 --- a/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java +++ b/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java @@ -32,6 +32,8 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; +import com.carrotsearch.randomizedtesting.generators.RandomNumbers; +import com.carrotsearch.randomizedtesting.generators.RandomPicks; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.analysis.MockTokenizer; import org.apache.lucene.document.Document; @@ -77,9 +79,6 @@ import org.apache.lucene.util.TestUtil; import org.apache.lucene.util.packed.PackedInts; import org.junit.Test; -import com.carrotsearch.randomizedtesting.generators.RandomNumbers; -import com.carrotsearch.randomizedtesting.generators.RandomPicks; - public class TestJoinUtil extends LuceneTestCase { public void testSimple() throws Exception { @@ -557,7 +556,12 @@ public class TestJoinUtil extends LuceneTestCase { }; } - @Override + @Override + public void visit(QueryVisitor visitor) { + + } + + @Override public String toString(String field) { return fieldQuery.toString(field); } diff --git a/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java b/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java index db17a90464..af2597da5a 100644 --- a/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java +++ b/lucene/misc/src/test/org/apache/lucene/search/TestDiversifiedTopDocsCollector.java @@ -477,6 +477,11 @@ public class TestDiversifiedTopDocsCollector extends LuceneTestCase { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + query.visit(visitor); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { if (scoreMode.needsScores() == false) { @@ -520,12 +525,12 @@ public class TestDiversifiedTopDocsCollector extends LuceneTestCase { } }; } - + @Override public void extractTerms(Set terms) { inner.extractTerms(terms); } - + @Override public Explanation explain(LeafReaderContext context, int doc) throws IOException { Scorer s = scorer(context); diff --git a/lucene/queries/src/java/org/apache/lucene/queries/CommonTermsQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/CommonTermsQuery.java index 10c232ed45..5c361cf7e9 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/CommonTermsQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/CommonTermsQuery.java @@ -33,6 +33,7 @@ import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TermQuery; /** @@ -129,7 +130,16 @@ public class CommonTermsQuery extends Query { collectTermStates(reader, leaves, contextArray, queryTerms); return buildQuery(maxDoc, contextArray, queryTerms); } - + + @Override + public void visit(QueryVisitor visitor) { + Term[] selectedTerms = terms.stream().filter(t -> visitor.acceptField(t.field())).toArray(Term[]::new); + if (selectedTerms.length > 0) { + QueryVisitor v = visitor.getSubVisitor(Occur.SHOULD, this); + v.consumeTerms(this, selectedTerms); + } + } + protected int calcLowFreqMinimumNumberShouldMatch(int numOptional) { return minNrShouldMatch(lowFreqMinNrShouldMatch, numOptional); } diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionMatchQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionMatchQuery.java index 38cf3f604d..39a51c1db1 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionMatchQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionMatchQuery.java @@ -29,6 +29,7 @@ import org.apache.lucene.search.DoubleValues; import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -61,6 +62,11 @@ public final class FunctionMatchQuery extends Query { return "FunctionMatchQuery(" + source.toString() + ")"; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { DoubleValuesSource vs = source.rewrite(searcher); diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java index f996306a72..5ed6e237ef 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionQuery.java @@ -27,6 +27,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -86,6 +87,11 @@ public class FunctionQuery extends Query { } } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + protected class AllScorer extends Scorer { final IndexReader reader; final FunctionWeight weight; @@ -150,7 +156,6 @@ public class FunctionQuery extends Query { return new FunctionQuery.FunctionWeight(searcher, boost); } - /** Prints a user-readable version of this query. */ @Override public String toString(String field) diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java index 315f6502a1..dfc22d3dc1 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionRangeQuery.java @@ -26,6 +26,7 @@ import org.apache.lucene.index.Term; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; @@ -114,6 +115,11 @@ public class FunctionRangeQuery extends Query { return classHash() ^ Objects.hash(valueSource, lowerVal, upperVal, includeLower, includeUpper); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new FunctionRangeWeight(searcher); diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionScoreQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionScoreQuery.java index 29d6dd9f41..b469845300 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionScoreQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/function/FunctionScoreQuery.java @@ -24,6 +24,7 @@ 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.search.BooleanClause; import org.apache.lucene.search.DoubleValues; import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.Explanation; @@ -31,6 +32,7 @@ import org.apache.lucene.search.FilterScorer; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Matches; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -119,6 +121,11 @@ public final class FunctionScoreQuery extends Query { return new FunctionScoreQuery(rewritten, source); } + @Override + public void visit(QueryVisitor visitor) { + in.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + @Override public String toString(String field) { return "FunctionScoreQuery(" + in.toString(field) + ", scored by " + source.toString() + ")"; diff --git a/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java index 9f3310c7a2..976660ea43 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/mlt/MoreLikeThisQuery.java @@ -16,17 +16,18 @@ */ package org.apache.lucene.queries.mlt; +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; +import java.util.Objects; +import java.util.Set; + import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.Query; - -import java.io.IOException; -import java.io.StringReader; -import java.util.Arrays; -import java.util.Set; -import java.util.Objects; +import org.apache.lucene.search.QueryVisitor; /** * A simple wrapper for MoreLikeThis for use in scenarios where a Query object is required eg @@ -179,4 +180,9 @@ public class MoreLikeThisQuery extends Query { Arrays.equals(moreLikeFields, other.moreLikeFields) && Objects.equals(stopWords, other.stopWords); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/lucene/queries/src/java/org/apache/lucene/queries/payloads/PayloadScoreQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/payloads/PayloadScoreQuery.java index bd5d927c62..aac7510eb2 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/payloads/PayloadScoreQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/payloads/PayloadScoreQuery.java @@ -26,10 +26,12 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermStates; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafSimScorer; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.spans.FilterSpans; import org.apache.lucene.search.spans.SpanCollector; @@ -86,6 +88,11 @@ public class PayloadScoreQuery extends SpanQuery { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + wrappedQuery.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + @Override public String toString(String field) { diff --git a/lucene/queries/src/java/org/apache/lucene/queries/payloads/SpanPayloadCheckQuery.java b/lucene/queries/src/java/org/apache/lucene/queries/payloads/SpanPayloadCheckQuery.java index a9d3bfb2da..0874722667 100644 --- a/lucene/queries/src/java/org/apache/lucene/queries/payloads/SpanPayloadCheckQuery.java +++ b/lucene/queries/src/java/org/apache/lucene/queries/payloads/SpanPayloadCheckQuery.java @@ -15,6 +15,7 @@ * limitations under the License. */ package org.apache.lucene.queries.payloads; + import java.io.IOException; import java.util.List; import java.util.Map; @@ -27,9 +28,11 @@ import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermStates; import org.apache.lucene.index.Terms; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafSimScorer; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.spans.FilterSpans; import org.apache.lucene.search.spans.FilterSpans.AcceptStatus; @@ -77,6 +80,13 @@ public class SpanPayloadCheckQuery extends SpanQuery { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(match.getField())) { + match.visit(visitor.getSubVisitor(BooleanClause.Occur.MUST, this)); + } + } + /** * Weight that pulls its Spans using a PayloadSpanCollector */ diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/complexPhrase/ComplexPhraseQueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/complexPhrase/ComplexPhraseQueryParser.java index ffe0066ed1..9a4043d1d8 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/complexPhrase/ComplexPhraseQueryParser.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/complexPhrase/ComplexPhraseQueryParser.java @@ -36,6 +36,7 @@ import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.MultiTermQuery.RewriteMethod; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.SynonymQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.spans.SpanBoostQuery; @@ -253,6 +254,11 @@ public class ComplexPhraseQueryParser extends QueryParser { } } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Query rewrite(IndexReader reader) throws IOException { final Query contents = this.contents[0]; diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/DistanceRewriteQuery.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/DistanceRewriteQuery.java index 1972a8ecf9..adbbd0a580 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/DistanceRewriteQuery.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/DistanceRewriteQuery.java @@ -15,10 +15,12 @@ * limitations under the License. */ package org.apache.lucene.queryparser.surround.query; + import java.io.IOException; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; class DistanceRewriteQuery extends RewriteQuery { @@ -33,5 +35,11 @@ class DistanceRewriteQuery extends RewriteQuery { public Query rewrite(IndexReader reader) throws IOException { return srndQuery.getSpanNearQuery(reader, fieldName, qf); } + + @Override + public void visit(QueryVisitor visitor) { + // TODO implement this + visitor.visitLeaf(this); + } } diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/SimpleTermRewriteQuery.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/SimpleTermRewriteQuery.java index 3e0e4aa131..50203a8af9 100644 --- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/SimpleTermRewriteQuery.java +++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/surround/query/SimpleTermRewriteQuery.java @@ -15,15 +15,17 @@ * limitations under the License. */ package org.apache.lucene.queryparser.surround.query; + import java.io.IOException; -import java.util.List; import java.util.ArrayList; +import java.util.List; import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.BooleanClause; -import org.apache.lucene.index.Term; +import org.apache.lucene.search.QueryVisitor; class SimpleTermRewriteQuery extends RewriteQuery { @@ -50,5 +52,11 @@ class SimpleTermRewriteQuery extends RewriteQuery { /* luceneSubQueries all have default weight */ luceneSubQueries, BooleanClause.Occur.SHOULD); /* OR the subquery terms */ } + + @Override + public void visit(QueryVisitor visitor) { + // TODO: implement this + visitor.visitLeaf(this); + } } diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiAnalyzer.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiAnalyzer.java index 85a5f3942e..15dd3c5b3a 100644 --- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiAnalyzer.java +++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/classic/TestMultiAnalyzer.java @@ -19,12 +19,18 @@ package org.apache.lucene.queryparser.classic; import java.io.IOException; import java.util.Objects; -import org.apache.lucene.analysis.*; +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.BaseTokenStreamTestCase; +import org.apache.lucene.analysis.MockTokenizer; +import org.apache.lucene.analysis.TokenFilter; +import org.apache.lucene.analysis.TokenStream; +import org.apache.lucene.analysis.Tokenizer; +import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.OffsetAttribute; import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute; -import org.apache.lucene.analysis.tokenattributes.CharTermAttribute; import org.apache.lucene.analysis.tokenattributes.TypeAttribute; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; /** * Test QueryParser's ability to deal with Analyzers that return more @@ -263,7 +269,12 @@ public class TestMultiAnalyzer extends BaseTokenStreamTestCase { return q.toString(f); } - @Override + @Override + public void visit(QueryVisitor visitor) { + q.visit(visitor); + } + + @Override public boolean equals(Object other) { return sameClassAs(other) && Objects.equals(q, ((DumbQueryWrapper) other).q); diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeQuery.java index 538f4a309e..124a7a7e90 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeQuery.java @@ -31,6 +31,7 @@ import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.ScorerSupplier; @@ -85,6 +86,13 @@ abstract class LatLonShapeQuery extends Query { return r; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public final Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/sandbox/src/java/org/apache/lucene/sandbox/queries/FuzzyLikeThisQuery.java b/lucene/sandbox/src/java/org/apache/lucene/sandbox/queries/FuzzyLikeThisQuery.java index a7898f7ae0..a0e1b5ddf3 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/sandbox/queries/FuzzyLikeThisQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/sandbox/queries/FuzzyLikeThisQuery.java @@ -41,6 +41,7 @@ import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.FuzzyTermsEnum; import org.apache.lucene.search.MaxNonCompetitiveBoostAttribute; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.similarities.ClassicSimilarity; import org.apache.lucene.search.similarities.TFIDFSimilarity; @@ -272,6 +273,11 @@ public class FuzzyLikeThisQuery extends Query } } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Query rewrite(IndexReader reader) throws IOException { @@ -330,7 +336,7 @@ public class FuzzyLikeThisQuery extends Query // booleans with a minimum-should-match of NumFields-1? return bq.build(); } - + //Holds info for a fuzzy term variant - initially score is set to edit distance (for ranking best // term variants) then is reset with IDF for use in ranking against all other // terms/fields diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/BM25FQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/BM25FQuery.java index 1a80351082..b7c7a285bf 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/BM25FQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/BM25FQuery.java @@ -223,6 +223,15 @@ public final class BM25FQuery extends Query { return this; } + @Override + public void visit(QueryVisitor visitor) { + Term[] selectedTerms = Arrays.stream(fieldTerms).filter(t -> visitor.acceptField(t.field())).toArray(Term[]::new); + if (selectedTerms.length > 0) { + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, this); + v.consumeTerms(this, selectedTerms); + } + } + private BooleanQuery rewriteToBoolean() { // rewrite to a simple disjunction if the score is not needed. BooleanQuery.Builder bq = new BooleanQuery.Builder(); diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/CoveringQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/CoveringQuery.java index fd89888bb8..84a275353b 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/CoveringQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/CoveringQuery.java @@ -109,6 +109,14 @@ public final class CoveringQuery extends Query { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, this); + for (Query query : queries) { + query.visit(v); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { final List weights = new ArrayList<>(queries.size()); diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesNumbersQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesNumbersQuery.java index f018df4ca1..cdfe23de49 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesNumbersQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesNumbersQuery.java @@ -78,6 +78,13 @@ public class DocValuesNumbersQuery extends Query { return 31 * classHash() + Objects.hash(field, numbers); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + public String getField() { return field; } @@ -129,4 +136,5 @@ public class DocValuesNumbersQuery extends Query { }; } + } diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesTermsQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesTermsQuery.java index 8ca53b711e..ef67b028be 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesTermsQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/DocValuesTermsQuery.java @@ -26,9 +26,9 @@ import org.apache.lucene.index.DocValues; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PrefixCodedTerms; +import org.apache.lucene.index.PrefixCodedTerms.TermIterator; import org.apache.lucene.index.SortedSetDocValues; import org.apache.lucene.index.Term; -import org.apache.lucene.index.PrefixCodedTerms.TermIterator; import org.apache.lucene.util.ArrayUtil; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.FixedBitSet; @@ -164,6 +164,13 @@ public class DocValuesTermsQuery extends Query { return builder.toString(); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new ConstantScoreWeight(this, boost) { diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java index 81426a7ffe..2e99a0666d 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/TermAutomatonQuery.java @@ -29,8 +29,8 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermStates; import org.apache.lucene.index.TermState; +import org.apache.lucene.index.TermStates; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.similarities.Similarity; import org.apache.lucene.search.spans.SpanNearQuery; @@ -492,4 +492,15 @@ public class TermAutomatonQuery extends Query { // TODO: we could maybe also rewrite to union of PhraseQuery (pull all finite strings) if it's "worth it"? return this; } + + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field) == false) { + return; + } + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, this); + for (BytesRef term : termToID.keySet()) { + v.consumeTerms(this, new Term(field, term)); + } + } } diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ConjunctionIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ConjunctionIntervalsSource.java index ec4341de70..123cc38d43 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ConjunctionIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ConjunctionIntervalsSource.java @@ -21,15 +21,15 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.FilterMatchesIterator; import org.apache.lucene.search.MatchesIterator; import org.apache.lucene.search.MatchesUtils; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; class ConjunctionIntervalsSource extends IntervalsSource { @@ -56,9 +56,11 @@ class ConjunctionIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { + public void visit(String field, QueryVisitor visitor) { + Query parent = new IntervalQuery(field, this); + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.MUST, parent); for (IntervalsSource source : subSources) { - source.extractTerms(field, terms); + source.visit(field, v); } } diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DifferenceIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DifferenceIntervalsSource.java index 7289d04ba2..f1d096c0bc 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DifferenceIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DifferenceIntervalsSource.java @@ -19,11 +19,11 @@ package org.apache.lucene.search.intervals; import java.io.IOException; import java.util.Objects; -import java.util.Set; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.MatchesIterator; +import org.apache.lucene.search.QueryVisitor; class DifferenceIntervalsSource extends IntervalsSource { @@ -83,8 +83,10 @@ class DifferenceIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { - minuend.extractTerms(field, terms); + public void visit(String field, QueryVisitor visitor) { + IntervalQuery q = new IntervalQuery(field, this); + minuend.visit(field, visitor.getSubVisitor(BooleanClause.Occur.MUST, q)); + subtrahend.visit(field, visitor.getSubVisitor(BooleanClause.Occur.MUST_NOT, q)); } @Override diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DisjunctionIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DisjunctionIntervalsSource.java index b28088513a..f37dcb60d3 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DisjunctionIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/DisjunctionIntervalsSource.java @@ -21,14 +21,15 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.MatchesIterator; import org.apache.lucene.search.MatchesUtils; +import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.util.PriorityQueue; class DisjunctionIntervalsSource extends IntervalsSource { @@ -84,9 +85,11 @@ class DisjunctionIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { + public void visit(String field, QueryVisitor visitor) { + Query parent = new IntervalQuery(field, this); + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, parent); for (IntervalsSource source : subSources) { - source.extractTerms(field, terms); + source.visit(field, v); } } diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ExtendedIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ExtendedIntervalsSource.java index 864a4b573c..41a8829b94 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ExtendedIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/ExtendedIntervalsSource.java @@ -19,11 +19,10 @@ package org.apache.lucene.search.intervals; import java.io.IOException; import java.util.Objects; -import java.util.Set; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; import org.apache.lucene.search.MatchesIterator; +import org.apache.lucene.search.QueryVisitor; class ExtendedIntervalsSource extends IntervalsSource { @@ -57,8 +56,8 @@ class ExtendedIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { - source.extractTerms(field, terms); + public void visit(String field, QueryVisitor visitor) { + source.visit(field, visitor); } @Override diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FilteredIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FilteredIntervalsSource.java index c2b4d6012c..b3341297c9 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FilteredIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FilteredIntervalsSource.java @@ -19,11 +19,10 @@ package org.apache.lucene.search.intervals; import java.io.IOException; import java.util.Objects; -import java.util.Set; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; import org.apache.lucene.search.MatchesIterator; +import org.apache.lucene.search.QueryVisitor; /** * An IntervalsSource that filters the intervals from another IntervalsSource @@ -83,8 +82,8 @@ public abstract class FilteredIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { - in.extractTerms(field, terms); + public void visit(String field, QueryVisitor visitor) { + in.visit(field, visitor); } @Override diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FixedFieldIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FixedFieldIntervalsSource.java index 7776a2b543..ab24ee359e 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FixedFieldIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/FixedFieldIntervalsSource.java @@ -19,11 +19,10 @@ package org.apache.lucene.search.intervals; import java.io.IOException; import java.util.Objects; -import java.util.Set; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; import org.apache.lucene.search.MatchesIterator; +import org.apache.lucene.search.QueryVisitor; class FixedFieldIntervalsSource extends IntervalsSource { @@ -46,8 +45,8 @@ class FixedFieldIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { - source.extractTerms(this.field, terms); + public void visit(String field, QueryVisitor visitor) { + source.visit(this.field, visitor); } @Override diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalQuery.java index 62fe0679bf..ac922e87f9 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalQuery.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalQuery.java @@ -30,6 +30,7 @@ import org.apache.lucene.search.Matches; import org.apache.lucene.search.MatchesIterator; import org.apache.lucene.search.MatchesUtils; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -120,6 +121,13 @@ public final class IntervalQuery extends Query { return new IntervalWeight(this, boost, scoreMode); } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + intervalsSource.visit(field, visitor); + } + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -147,7 +155,7 @@ public final class IntervalQuery extends Query { @Override public void extractTerms(Set terms) { - intervalsSource.extractTerms(field, terms); + intervalsSource.visit(field, QueryVisitor.termCollector(terms)); } @Override diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalsSource.java index dc4161fa05..1c49d248fa 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/IntervalsSource.java @@ -18,11 +18,10 @@ package org.apache.lucene.search.intervals; import java.io.IOException; -import java.util.Set; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; import org.apache.lucene.search.MatchesIterator; +import org.apache.lucene.search.QueryVisitor; /** * A helper class for {@link IntervalQuery} that provides an {@link IntervalIterator} @@ -56,11 +55,9 @@ public abstract class IntervalsSource { public abstract MatchesIterator matches(String field, LeafReaderContext ctx, int doc) throws IOException; /** - * Expert: collect {@link Term} objects from this source - * @param field the field to be scored - * @param terms a {@link Set} which terms should be added to + * Expert: visit the tree of sources */ - public abstract void extractTerms(String field, Set terms); + public abstract void visit(String field, QueryVisitor visitor); /** * Return the minimum possible width of an interval returned by this source diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MinimumShouldMatchIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MinimumShouldMatchIntervalsSource.java index d51e559548..4f8b92f8c7 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MinimumShouldMatchIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MinimumShouldMatchIntervalsSource.java @@ -25,15 +25,15 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.stream.Collectors; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; +import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.MatchesIterator; import org.apache.lucene.search.MatchesUtils; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.util.PriorityQueue; class MinimumShouldMatchIntervalsSource extends IntervalsSource { @@ -85,9 +85,11 @@ class MinimumShouldMatchIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { + public void visit(String field, QueryVisitor visitor) { + Query parent = new IntervalQuery(field, this); + QueryVisitor v = visitor.getSubVisitor(BooleanClause.Occur.SHOULD, parent); for (IntervalsSource source : sources) { - source.extractTerms(field, terms); + source.visit(field, v); } } diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MultiTermIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MultiTermIntervalsSource.java index 7689d1dc48..1ee52d4c3a 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MultiTermIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/MultiTermIntervalsSource.java @@ -21,14 +21,13 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.Set; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.MatchesIterator; import org.apache.lucene.search.MatchesUtils; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.automaton.CompiledAutomaton; @@ -89,7 +88,7 @@ class MultiTermIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { + public void visit(String field, QueryVisitor visitor) { } diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/OffsetIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/OffsetIntervalsSource.java index b2ca30224e..078c64b027 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/OffsetIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/OffsetIntervalsSource.java @@ -19,11 +19,10 @@ package org.apache.lucene.search.intervals; import java.io.IOException; import java.util.Objects; -import java.util.Set; import org.apache.lucene.index.LeafReaderContext; -import org.apache.lucene.index.Term; import org.apache.lucene.search.MatchesIterator; +import org.apache.lucene.search.QueryVisitor; /** * Tracks a reference intervals source, and produces a pseudo-interval that appears @@ -144,8 +143,8 @@ class OffsetIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { - in.extractTerms(field, terms); + public void visit(String field, QueryVisitor visitor) { + in.visit(field, visitor); } @Override diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/TermIntervalsSource.java b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/TermIntervalsSource.java index 4539d2f2cb..3d6fe20b32 100644 --- a/lucene/sandbox/src/java/org/apache/lucene/search/intervals/TermIntervalsSource.java +++ b/lucene/sandbox/src/java/org/apache/lucene/search/intervals/TermIntervalsSource.java @@ -19,7 +19,6 @@ package org.apache.lucene.search.intervals; import java.io.IOException; import java.util.Objects; -import java.util.Set; import org.apache.lucene.codecs.lucene50.Lucene50PostingsFormat; import org.apache.lucene.codecs.lucene50.Lucene50PostingsReader; @@ -32,6 +31,7 @@ import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.MatchesIterator; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TwoPhaseIterator; import org.apache.lucene.util.BytesRef; @@ -227,8 +227,8 @@ class TermIntervalsSource extends IntervalsSource { } @Override - public void extractTerms(String field, Set terms) { - terms.add(new Term(field, term)); + public void visit(String field, QueryVisitor visitor) { + visitor.consumeTerms(new IntervalQuery(field, this), new Term(field, term)); } /** A guess of diff --git a/lucene/sandbox/src/test/org/apache/lucene/sandbox/queries/FuzzyLikeThisQueryTest.java b/lucene/sandbox/src/test/org/apache/lucene/sandbox/queries/FuzzyLikeThisQueryTest.java index ead2720a6f..95c6cca2dc 100644 --- a/lucene/sandbox/src/test/org/apache/lucene/sandbox/queries/FuzzyLikeThisQueryTest.java +++ b/lucene/sandbox/src/test/org/apache/lucene/sandbox/queries/FuzzyLikeThisQueryTest.java @@ -16,6 +16,9 @@ */ package org.apache.lucene.sandbox.queries; +import java.io.IOException; +import java.util.HashSet; + import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; @@ -25,16 +28,13 @@ import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreDoc; -import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TopDocs; import org.apache.lucene.store.Directory; import org.apache.lucene.util.IOUtils; import org.apache.lucene.util.LuceneTestCase; -import java.io.IOException; -import java.util.HashSet; - public class FuzzyLikeThisQueryTest extends LuceneTestCase { private Directory directory; private IndexSearcher searcher; @@ -81,7 +81,7 @@ public class FuzzyLikeThisQueryTest extends LuceneTestCase { flt.addTerms("smith", "name", 2, 1); Query q = flt.rewrite(searcher.getIndexReader()); HashSet queryTerms = new HashSet<>(); - searcher.createWeight(q, ScoreMode.COMPLETE, 1f).extractTerms(queryTerms); + q.visit(QueryVisitor.termCollector(queryTerms)); assertTrue("Should have variant smythe", queryTerms.contains(new Term("name", "smythe"))); assertTrue("Should have variant smith", queryTerms.contains(new Term("name", "smith"))); assertTrue("Should have variant smyth", queryTerms.contains(new Term("name", "smyth"))); @@ -98,7 +98,7 @@ public class FuzzyLikeThisQueryTest extends LuceneTestCase { flt.addTerms("jonathin smoth", "name", 2, 1); Query q = flt.rewrite(searcher.getIndexReader()); HashSet queryTerms = new HashSet<>(); - searcher.createWeight(q, ScoreMode.COMPLETE, 1f).extractTerms(queryTerms); + q.visit(QueryVisitor.termCollector(queryTerms)); assertTrue("Should have variant jonathan", queryTerms.contains(new Term("name", "jonathan"))); assertTrue("Should have variant smith", queryTerms.contains(new Term("name", "smith"))); TopDocs topDocs = searcher.search(flt, 1); @@ -116,7 +116,7 @@ public class FuzzyLikeThisQueryTest extends LuceneTestCase { // don't fail here just because the field doesn't exits Query q = flt.rewrite(searcher.getIndexReader()); HashSet queryTerms = new HashSet<>(); - searcher.createWeight(q, ScoreMode.COMPLETE, 1f).extractTerms(queryTerms); + q.visit(QueryVisitor.termCollector(queryTerms)); assertTrue("Should have variant jonathan", queryTerms.contains(new Term("name", "jonathan"))); assertTrue("Should have variant smith", queryTerms.contains(new Term("name", "smith"))); TopDocs topDocs = searcher.search(flt, 1); @@ -133,7 +133,7 @@ public class FuzzyLikeThisQueryTest extends LuceneTestCase { flt.addTerms("fernando smith", "name", 2, 1); Query q = flt.rewrite(searcher.getIndexReader()); HashSet queryTerms = new HashSet<>(); - searcher.createWeight(q, ScoreMode.COMPLETE, 1f).extractTerms(queryTerms); + q.visit(QueryVisitor.termCollector(queryTerms)); assertTrue("Should have variant smith", queryTerms.contains(new Term("name", "smith"))); TopDocs topDocs = searcher.search(flt, 1); ScoreDoc[] sd = topDocs.scoreDocs; diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java index 0d7ce58355..3aaa3ed837 100644 --- a/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java +++ b/lucene/sandbox/src/test/org/apache/lucene/search/TestTermAutomatonQuery.java @@ -605,6 +605,11 @@ public class TestTermAutomatonQuery extends LuceneTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "RandomFilter(seed=" + seed + ",density=" + density + ")"; diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java index cadacdc5e6..e8149cbb53 100644 --- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java +++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/CompositeVerifyQuery.java @@ -24,6 +24,7 @@ import org.apache.lucene.search.ConstantScoreScorer; import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -80,6 +81,11 @@ public class CompositeVerifyQuery extends Query { return getClass().getSimpleName() + "(" + indexQuery.toString(field) + ", " + predicateValueSource + ")"; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { final Weight indexQueryWeight = indexQuery.createWeight(searcher, ScoreMode.COMPLETE_NO_SCORES, boost);//scores aren't unsupported diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java index 1bcb7b53e6..7fa9864350 100644 --- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java +++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/composite/IntersectsRPTVerifyQuery.java @@ -25,6 +25,7 @@ import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -80,6 +81,11 @@ public class IntersectsRPTVerifyQuery extends Query { return result; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/AbstractPrefixTreeQuery.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/AbstractPrefixTreeQuery.java index a5171c3e29..943aa31a4a 100644 --- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/AbstractPrefixTreeQuery.java +++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/prefix/AbstractPrefixTreeQuery.java @@ -29,6 +29,7 @@ import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -76,6 +77,13 @@ public abstract class AbstractPrefixTreeQuery extends Query { return result; } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(fieldName)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new ConstantScoreWeight(this, boost) { diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java index ff019c0984..da70f0d39e 100644 --- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java +++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/serialized/SerializedDVStrategy.java @@ -34,6 +34,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -151,6 +152,11 @@ public class SerializedDVStrategy extends SpatialStrategy { }; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java index 9de43d13fb..6f2cb82050 100644 --- a/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java +++ b/lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java @@ -37,6 +37,7 @@ import org.apache.lucene.search.DoubleValues; import org.apache.lucene.search.DoubleValuesSource; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -265,6 +266,11 @@ public class PointVectorStrategy extends SpatialStrategy { return new DistanceRangeQuery(rewritten, distanceSource, limit); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { Weight w = inner.createWeight(searcher, scoreMode, 1f); diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java index 8fcf056d7e..6b0f78855c 100644 --- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java @@ -25,6 +25,7 @@ import org.apache.lucene.search.ConstantScoreScorer; import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -60,6 +61,13 @@ final class PointInGeo3DShapeQuery extends Query { } } + @Override + public void visit(QueryVisitor visitor) { + if (visitor.acceptField(field)) { + visitor.visitLeaf(this); + } + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { diff --git a/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/ContextQuery.java b/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/ContextQuery.java index 1a2680cb55..bb2131de44 100644 --- a/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/ContextQuery.java +++ b/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/ContextQuery.java @@ -24,6 +24,7 @@ import java.util.TreeSet; import org.apache.lucene.analysis.miscellaneous.ConcatenateGraphFilter; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; import org.apache.lucene.util.BytesRef; @@ -336,4 +337,9 @@ public class ContextQuery extends CompletionQuery { throw new UnsupportedOperationException(); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + } diff --git a/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/PrefixCompletionQuery.java b/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/PrefixCompletionQuery.java index a8da150f50..896d9c8f15 100644 --- a/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/PrefixCompletionQuery.java +++ b/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/PrefixCompletionQuery.java @@ -21,6 +21,7 @@ import java.io.IOException; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; import org.apache.lucene.search.suggest.BitsProducer; @@ -73,6 +74,10 @@ public class PrefixCompletionQuery extends CompletionQuery { } } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } /** * Gets the analyzer used to analyze the prefix. */ diff --git a/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/RegexCompletionQuery.java b/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/RegexCompletionQuery.java index 6fa547a7f6..4487f226a9 100644 --- a/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/RegexCompletionQuery.java +++ b/lucene/suggest/src/java/org/apache/lucene/search/suggest/document/RegexCompletionQuery.java @@ -20,6 +20,7 @@ import java.io.IOException; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; import org.apache.lucene.search.suggest.BitsProducer; @@ -123,4 +124,9 @@ public class RegexCompletionQuery extends CompletionQuery { public int hashCode() { throw new UnsupportedOperationException(); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingQuery.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingQuery.java index b3d2f8116c..8989106b95 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingQuery.java +++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingQuery.java @@ -78,4 +78,9 @@ public final class AssertingQuery extends Query { } } + @Override + public void visit(QueryVisitor visitor) { + in.visit(visitor); + } + } diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/BlockScoreQueryWrapper.java b/lucene/test-framework/src/java/org/apache/lucene/search/BlockScoreQueryWrapper.java index b15fa280fb..a574933e48 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/search/BlockScoreQueryWrapper.java +++ b/lucene/test-framework/src/java/org/apache/lucene/search/BlockScoreQueryWrapper.java @@ -72,6 +72,11 @@ public final class BlockScoreQueryWrapper extends Query { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + query.visit(visitor); + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { final Weight inWeight = query.createWeight(searcher, scoreMode, boost); diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java b/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java index 840e78e011..cdbac77784 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java +++ b/lucene/test-framework/src/java/org/apache/lucene/search/QueryUtils.java @@ -66,6 +66,9 @@ public class QueryUtils { return "My Whacky Query"; } + @Override + public void visit(QueryVisitor visitor) { } + @Override public boolean equals(Object o) { return o == this; diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/RandomApproximationQuery.java b/lucene/test-framework/src/java/org/apache/lucene/search/RandomApproximationQuery.java index b43ccc4514..445a39a1b2 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/search/RandomApproximationQuery.java +++ b/lucene/test-framework/src/java/org/apache/lucene/search/RandomApproximationQuery.java @@ -18,8 +18,8 @@ package org.apache.lucene.search; import java.io.IOException; import java.util.Random; -import com.carrotsearch.randomizedtesting.generators.RandomNumbers; +import com.carrotsearch.randomizedtesting.generators.RandomNumbers; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; @@ -45,6 +45,11 @@ public class RandomApproximationQuery extends Query { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + query.visit(visitor); + } + @Override public boolean equals(Object other) { return sameClassAs(other) && diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java b/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java index 1b81b77614..27d4e6c34f 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java +++ b/lucene/test-framework/src/java/org/apache/lucene/search/ShardSearchingTestBase.java @@ -231,9 +231,8 @@ public abstract class ShardSearchingTestBase extends LuceneTestCase { public Query rewrite(Query original) throws IOException { final IndexSearcher localSearcher = new IndexSearcher(getIndexReader()); original = localSearcher.rewrite(original); - final Weight weight = localSearcher.createWeight(original, ScoreMode.COMPLETE, 1); final Set terms = new HashSet<>(); - weight.extractTerms(terms); + original.visit(QueryVisitor.termCollector(terms)); // Make a single request to remote nodes for term // stats: @@ -259,7 +258,7 @@ public abstract class ShardSearchingTestBase extends LuceneTestCase { } } - return weight.getQuery(); + return original; } @Override diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/spans/AssertingSpanQuery.java b/lucene/test-framework/src/java/org/apache/lucene/search/spans/AssertingSpanQuery.java index f24a4ff8fe..1040a3a767 100644 --- a/lucene/test-framework/src/java/org/apache/lucene/search/spans/AssertingSpanQuery.java +++ b/lucene/test-framework/src/java/org/apache/lucene/search/spans/AssertingSpanQuery.java @@ -16,14 +16,15 @@ */ package org.apache.lucene.search.spans; +import java.io.IOException; +import java.util.Objects; + import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; -import java.io.IOException; -import java.util.Objects; - /** Wraps a span query with asserts */ public class AssertingSpanQuery extends SpanQuery { private final SpanQuery in; @@ -60,6 +61,11 @@ public class AssertingSpanQuery extends SpanQuery { } } + @Override + public void visit(QueryVisitor visitor) { + in.visit(visitor); + } + @Override public Query clone() { return new AssertingSpanQuery(in); diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java index 332e1b2890..33f1c28ae6 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRScoringQuery.java @@ -40,6 +40,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -153,6 +154,11 @@ public class LTRScoringQuery extends Query { return sameClassAs(o) && equalsTo(getClass().cast(o)); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + private boolean equalsTo(LTRScoringQuery other) { if (ltrScoringModel == null) { if (other.ltrScoringModel != null) { diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java index 066d281250..720d671b3d 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/feature/Feature.java @@ -27,6 +27,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; import org.apache.solr.core.SolrResourceLoader; @@ -134,6 +135,11 @@ public abstract class Feature extends Query { return sameClassAs(o) && equalsTo(getClass().cast(o)); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + private boolean equalsTo(Feature other) { if (index != other.index) { return false; diff --git a/solr/core/src/java/org/apache/solr/legacy/LegacyNumericRangeQuery.java b/solr/core/src/java/org/apache/solr/legacy/LegacyNumericRangeQuery.java index d07e497da2..e39cfb7d21 100644 --- a/solr/core/src/java/org/apache/solr/legacy/LegacyNumericRangeQuery.java +++ b/solr/core/src/java/org/apache/solr/legacy/LegacyNumericRangeQuery.java @@ -27,16 +27,17 @@ import org.apache.lucene.document.IntPoint; import org.apache.lucene.document.LongPoint; import org.apache.lucene.index.FilteredTermsEnum; import org.apache.lucene.index.PointValues; +import org.apache.lucene.index.Term; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.TermRangeQuery; import org.apache.lucene.util.AttributeSource; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.NumericUtils; -import org.apache.lucene.index.Term; // for javadocs /** *

A {@link Query} that matches numeric values within a @@ -533,5 +534,10 @@ public final class LegacyNumericRangeQuery extends MultiTermQu } } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java index 0068c9aaa0..a03675d334 100644 --- a/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java +++ b/solr/core/src/java/org/apache/solr/parser/SolrQueryParserBase.java @@ -41,6 +41,7 @@ import org.apache.lucene.search.MultiPhraseQuery; import org.apache.lucene.search.MultiTermQuery; import org.apache.lucene.search.PhraseQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.RegexpQuery; import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.util.QueryBuilder; @@ -62,7 +63,7 @@ import org.apache.solr.search.QueryUtils; import org.apache.solr.search.SolrConstantScoreQuery; import org.apache.solr.search.SyntaxError; -import static org.apache.solr.parser.SolrQueryParserBase.SynonymQueryStyle.*; +import static org.apache.solr.parser.SolrQueryParserBase.SynonymQueryStyle.AS_SAME_TERM; /** This class is overridden by QueryParser in QueryParser.jj * and acts to separate the majority of the Java code from the .jj grammar file. @@ -201,6 +202,11 @@ public abstract class SolrQueryParserBase extends QueryBuilder { return "RAW(" + field + "," + getJoinedExternalVal() + ")"; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + @Override public boolean equals(Object obj) { return false; diff --git a/solr/core/src/java/org/apache/solr/query/FilterQuery.java b/solr/core/src/java/org/apache/solr/query/FilterQuery.java index 02cb88b0c8..4df704edd3 100644 --- a/solr/core/src/java/org/apache/solr/query/FilterQuery.java +++ b/solr/core/src/java/org/apache/solr/query/FilterQuery.java @@ -24,6 +24,7 @@ import org.apache.lucene.search.ConstantScoreQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; import org.apache.solr.search.DocSet; @@ -67,6 +68,10 @@ public class FilterQuery extends ExtendedQueryBase { return sb.toString(); } + @Override + public void visit(QueryVisitor visitor) { + q.visit(visitor); + } @Override public Query rewrite(IndexReader reader) throws IOException { diff --git a/solr/core/src/java/org/apache/solr/query/SolrRangeQuery.java b/solr/core/src/java/org/apache/solr/query/SolrRangeQuery.java index be58bbc38f..40a68e54e2 100644 --- a/solr/core/src/java/org/apache/solr/query/SolrRangeQuery.java +++ b/solr/core/src/java/org/apache/solr/query/SolrRangeQuery.java @@ -26,8 +26,8 @@ import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.PostingsEnum; import org.apache.lucene.index.Term; -import org.apache.lucene.index.TermStates; import org.apache.lucene.index.TermState; +import org.apache.lucene.index.TermStates; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.BooleanClause; @@ -40,6 +40,7 @@ import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TermQuery; @@ -126,6 +127,11 @@ public final class SolrRangeQuery extends ExtendedQueryBase implements DocSetPro return buffer.toString(); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + private String endpoint(BytesRef ref) { if (ref == null) return "*"; String toStr = Term.toString(ref); diff --git a/solr/core/src/java/org/apache/solr/schema/LatLonType.java b/solr/core/src/java/org/apache/solr/schema/LatLonType.java index 9f9dcd18df..88ab20b9f1 100644 --- a/solr/core/src/java/org/apache/solr/schema/LatLonType.java +++ b/solr/core/src/java/org/apache/solr/schema/LatLonType.java @@ -15,6 +15,7 @@ * limitations under the License. */ package org.apache.solr.schema; + import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -34,6 +35,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.SortField; @@ -593,6 +595,11 @@ class SpatialDistanceQuery extends ExtendedQueryBase implements PostFilter { hash = hash * 31 + Double.doubleToLongBits(lonMin); return (int) (hash >> 32 + hash); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/solr/core/src/java/org/apache/solr/search/AbstractReRankQuery.java b/solr/core/src/java/org/apache/solr/search/AbstractReRankQuery.java index 0c2fb828aa..c87565813e 100644 --- a/solr/core/src/java/org/apache/solr/search/AbstractReRankQuery.java +++ b/solr/core/src/java/org/apache/solr/search/AbstractReRankQuery.java @@ -23,6 +23,7 @@ import java.util.Set; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Rescorer; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TopDocsCollector; @@ -83,4 +84,9 @@ public abstract class AbstractReRankQuery extends RankQuery { final Weight mainWeight = mainQuery.createWeight(searcher, scoreMode, boost); return new ReRankWeight(mainQuery, reRankQueryRescorer, searcher, mainWeight); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/solr/core/src/java/org/apache/solr/search/AnalyticsQuery.java b/solr/core/src/java/org/apache/solr/search/AnalyticsQuery.java index 1928c26ecc..b32f5182e7 100644 --- a/solr/core/src/java/org/apache/solr/search/AnalyticsQuery.java +++ b/solr/core/src/java/org/apache/solr/search/AnalyticsQuery.java @@ -17,8 +17,9 @@ package org.apache.solr.search; import org.apache.lucene.search.IndexSearcher; -import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.lucene.search.QueryVisitor; import org.apache.solr.handler.component.MergeStrategy; +import org.apache.solr.handler.component.ResponseBuilder; import org.apache.solr.request.SolrRequestInfo; /** @@ -77,4 +78,9 @@ public abstract class AnalyticsQuery extends ExtendedQueryBase implements PostFi } public abstract DelegatingCollector getAnalyticsCollector(ResponseBuilder rb, IndexSearcher searcher); + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } \ No newline at end of file diff --git a/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java index 53e5acd790..44787ec71e 100644 --- a/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/CollapsingQParserPlugin.java @@ -54,6 +54,7 @@ import org.apache.lucene.search.FieldComparator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafFieldComparator; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorable; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Sort; @@ -263,6 +264,11 @@ public class CollapsingQParserPlugin extends QParserPlugin { nullPolicy == other.nullPolicy; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + public int getCost() { return Math.max(super.getCost(), 100); } diff --git a/solr/core/src/java/org/apache/solr/search/ExportQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/ExportQParserPlugin.java index e3fbcac111..bdf943e055 100644 --- a/solr/core/src/java/org/apache/solr/search/ExportQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/ExportQParserPlugin.java @@ -25,6 +25,7 @@ import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafCollector; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorable; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.ScoreMode; @@ -122,6 +123,11 @@ public class ExportQParserPlugin extends QParserPlugin { return s; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + public ExportQuery() { } diff --git a/solr/core/src/java/org/apache/solr/search/Filter.java b/solr/core/src/java/org/apache/solr/search/Filter.java index 4a48bb6ad4..c7df4e1056 100644 --- a/solr/core/src/java/org/apache/solr/search/Filter.java +++ b/solr/core/src/java/org/apache/solr/search/Filter.java @@ -27,6 +27,7 @@ import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.TwoPhaseIterator; @@ -142,4 +143,9 @@ public abstract class Filter extends Query { } }; } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/solr/core/src/java/org/apache/solr/search/GraphTermsQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/GraphTermsQParserPlugin.java index 0ce7f72fba..554222543e 100644 --- a/solr/core/src/java/org/apache/solr/search/GraphTermsQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/GraphTermsQParserPlugin.java @@ -51,6 +51,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.MatchNoDocsQuery; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -239,6 +240,11 @@ public class GraphTermsQParserPlugin extends QParserPlugin { return builder.toString(); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + private class WeightOrDocIdSet { final Weight weight; final DocIdSet set; @@ -781,5 +787,10 @@ abstract class PointSetQuery extends Query implements DocSetProducer { return sb.toString(); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + protected abstract String toString(byte[] value); } diff --git a/solr/core/src/java/org/apache/solr/search/HashQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/HashQParserPlugin.java index 2af5bd84fd..2cdf9f3149 100644 --- a/solr/core/src/java/org/apache/solr/search/HashQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/HashQParserPlugin.java @@ -31,6 +31,7 @@ import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafCollector; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorable; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; @@ -113,6 +114,11 @@ public class HashQParserPlugin extends QParserPlugin { worker == other.worker; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + public HashQuery(String[] keys, int workers, int worker) { this.keys = keys; this.workers = workers; diff --git a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java index 4e832a0067..c6fb0dec41 100644 --- a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java @@ -36,6 +36,7 @@ import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -164,6 +165,11 @@ class JoinQuery extends Query { return super.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException { return new JoinQueryWeight((SolrIndexSearcher) searcher, scoreMode, boost); diff --git a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java index b032dc02e9..0d37abb95b 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java +++ b/solr/core/src/java/org/apache/solr/search/SolrConstantScoreQuery.java @@ -28,6 +28,7 @@ import org.apache.lucene.search.DocIdSet; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -138,4 +139,9 @@ public class SolrConstantScoreQuery extends Query implements ExtendedQuery { return 31 * classHash() + filter.hashCode(); } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + } diff --git a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java index ab0996d31d..210e0ad66a 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java +++ b/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java @@ -2310,6 +2310,11 @@ public class SolrIndexSearcher extends IndexSearcher implements Closeable, SolrI return "SolrFilter"; } + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + private class FilterSet extends DocIdSet { private final DocIdSet docIdSet; private final LeafReaderContext context; diff --git a/solr/core/src/java/org/apache/solr/search/WrappedQuery.java b/solr/core/src/java/org/apache/solr/search/WrappedQuery.java index bea01a65ac..e123c5003e 100644 --- a/solr/core/src/java/org/apache/solr/search/WrappedQuery.java +++ b/solr/core/src/java/org/apache/solr/search/WrappedQuery.java @@ -16,14 +16,15 @@ */ package org.apache.solr.search; +import java.io.IOException; + import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; -import java.io.IOException; - /** A simple query that wraps another query and implements ExtendedQuery. */ public final class WrappedQuery extends ExtendedQueryBase { private Query q; @@ -51,6 +52,11 @@ public final class WrappedQuery extends ExtendedQueryBase { return q.rewrite(reader); } + @Override + public void visit(QueryVisitor visitor) { + q.visit(visitor); + } + @Override public int hashCode() { return q.hashCode(); diff --git a/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetFilter.java b/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetFilter.java index 83e9a8fac8..5d65422c6c 100644 --- a/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetFilter.java +++ b/solr/core/src/java/org/apache/solr/search/join/BlockJoinFacetFilter.java @@ -20,6 +20,7 @@ import java.util.Objects; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.solr.search.DelegatingCollector; import org.apache.solr.search.PostFilter; @@ -87,4 +88,9 @@ class BlockJoinFacetFilter extends Query implements PostFilter { public int hashCode() { return classHash() * 31 + blockJoinFacetCollector.hashCode(); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/solr/core/src/java/org/apache/solr/search/join/GraphQuery.java b/solr/core/src/java/org/apache/solr/search/join/GraphQuery.java index 4402a26674..aadfc88ba2 100644 --- a/solr/core/src/java/org/apache/solr/search/join/GraphQuery.java +++ b/solr/core/src/java/org/apache/solr/search/join/GraphQuery.java @@ -34,6 +34,7 @@ import org.apache.lucene.search.DocValuesFieldExistsQuery; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -286,7 +287,7 @@ public class GraphQuery extends Query { public void extractTerms(Set terms) { // NoOp for now , not used.. / supported } - + } private static class GraphScorer extends Scorer { @@ -441,5 +442,10 @@ public class GraphQuery extends Query { Objects.equals(toField, other.toField) && Objects.equals(traversalFilter, other.traversalFilter); } - + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } + } diff --git a/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java index 55d58fc5ed..c2f866238f 100644 --- a/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java +++ b/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java @@ -23,6 +23,7 @@ import java.util.Objects; import org.apache.lucene.index.DocValuesType; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Weight; import org.apache.lucene.search.join.JoinUtil; import org.apache.lucene.search.join.ScoreMode; @@ -195,6 +196,11 @@ public class ScoreJoinQParserPlugin extends QParserPlugin { Objects.equals(scoreMode, other.scoreMode) && Objects.equals(toField, other.toField); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/solr/core/src/java/org/apache/solr/search/stats/ExactStatsCache.java b/solr/core/src/java/org/apache/solr/search/stats/ExactStatsCache.java index 290b7c1b8f..fe315d2114 100644 --- a/solr/core/src/java/org/apache/solr/search/stats/ExactStatsCache.java +++ b/solr/core/src/java/org/apache/solr/search/stats/ExactStatsCache.java @@ -27,10 +27,10 @@ import java.util.Map.Entry; import java.util.Set; import com.google.common.collect.Lists; -import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.Term; import org.apache.lucene.index.TermStates; import org.apache.lucene.search.CollectionStatistics; +import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TermStatistics; @@ -157,26 +157,32 @@ public class ExactStatsCache extends StatsCache { Query q = rb.getQuery(); try { HashSet terms = new HashSet<>(); - searcher.createWeight(searcher.rewrite(q), ScoreMode.COMPLETE, 1).extractTerms(terms); - IndexReaderContext context = searcher.getTopReaderContext(); HashMap statsMap = new HashMap<>(); HashMap colMap = new HashMap<>(); - for (Term t : terms) { - TermStates termStates = TermStates.build(context, t, true); - - if (!colMap.containsKey(t.field())) { // collection stats for this field - CollectionStatistics collectionStatistics = searcher.localCollectionStatistics(t.field()); - if (collectionStatistics != null) { - colMap.put(t.field(), new CollectionStats(collectionStatistics)); + IndexSearcher statsCollectingSearcher = new IndexSearcher(searcher.getIndexReader()){ + @Override + public CollectionStatistics collectionStatistics(String field) throws IOException { + CollectionStatistics cs = super.collectionStatistics(field); + if (cs != null) { + colMap.put(field, new CollectionStats(cs)); } + return cs; } - TermStatistics tst = searcher.localTermStatistics(t, termStates); - if (tst == null) { // skip terms that are not present here - continue; + @Override + public TermStatistics termStatistics(Term term, TermStates context) throws IOException { + TermStatistics ts = super.termStatistics(term, context); + if (ts == null) { + return null; + } + terms.add(term); + statsMap.put(term.toString(), new TermStats(term.field(), ts)); + return ts; } + }; + statsCollectingSearcher.createWeight(searcher.rewrite(q), ScoreMode.COMPLETE, 1); - statsMap.put(t.toString(), new TermStats(t.field(), tst)); + for (Term t : terms) { rb.rsp.add(TERMS_KEY, t.toString()); } if (statsMap.size() != 0) { //Don't add empty keys diff --git a/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java b/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java index 9a16f608d0..05cb7e6f26 100644 --- a/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java +++ b/solr/core/src/java/org/apache/solr/update/DeleteByQueryWrapper.java @@ -27,6 +27,7 @@ import org.apache.lucene.index.Term; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -116,4 +117,9 @@ final class DeleteByQueryWrapper extends Query { return Objects.equals(in, other.in) && Objects.equals(schema, other.schema); } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } diff --git a/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java b/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java index 0360afc57a..b56bcb6ef5 100644 --- a/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java +++ b/solr/core/src/java/org/apache/solr/update/SolrIndexSplitter.java @@ -45,6 +45,7 @@ import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Weight; @@ -529,6 +530,11 @@ public class SolrIndexSplitter { public int hashCode() { return partition; } + + @Override + public void visit(QueryVisitor visitor) { + visitor.visitLeaf(this); + } } static FixedBitSet[] split(LeafReaderContext readerContext, int numPieces, SchemaField field, DocRouter.Range[] rangesArr, diff --git a/solr/core/src/test/org/apache/solr/search/RankQueryTestPlugin.java b/solr/core/src/test/org/apache/solr/search/RankQueryTestPlugin.java index bc3839771b..102488313c 100644 --- a/solr/core/src/test/org/apache/solr/search/RankQueryTestPlugin.java +++ b/solr/core/src/test/org/apache/solr/search/RankQueryTestPlugin.java @@ -36,6 +36,7 @@ import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.LeafCollector; import org.apache.lucene.search.LeafFieldComparator; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.Scorable; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.ScoreMode; @@ -111,6 +112,11 @@ public class RankQueryTestPlugin extends QParserPlugin { return q.createWeight(indexSearcher, scoreMode, boost); } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return q.toString(field); diff --git a/solr/core/src/test/org/apache/solr/uninverting/TestFieldCacheSortRandom.java b/solr/core/src/test/org/apache/solr/uninverting/TestFieldCacheSortRandom.java index bca479483e..915263288f 100644 --- a/solr/core/src/test/org/apache/solr/uninverting/TestFieldCacheSortRandom.java +++ b/solr/core/src/test/org/apache/solr/uninverting/TestFieldCacheSortRandom.java @@ -44,6 +44,7 @@ import org.apache.lucene.search.ConstantScoreWeight; import org.apache.lucene.search.FieldDoc; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; +import org.apache.lucene.search.QueryVisitor; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Sort; @@ -54,8 +55,8 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.util.BitSetIterator; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.FixedBitSet; -import org.apache.solr.SolrTestCase; import org.apache.lucene.util.TestUtil; +import org.apache.solr.SolrTestCase; import org.apache.solr.uninverting.UninvertingReader.Type; /** random sorting tests with uninversion */ @@ -297,6 +298,11 @@ public class TestFieldCacheSortRandom extends SolrTestCase { }; } + @Override + public void visit(QueryVisitor visitor) { + + } + @Override public String toString(String field) { return "RandomFilter(density=" + density + ")";