You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ro...@apache.org on 2019/08/07 10:58:49 UTC
[lucene-solr] branch branch_8x updated (f853198 -> 8dd116a)
This is an automated email from the ASF dual-hosted git repository.
romseygeek pushed a change to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git.
from f853198 SOLR-13672: Cloud -> Zk Status page now parses response from Zookeeper 3.5.5 correctly
new b5b78e0 LUCENE-8941: Build wildcard matches lazily
new 8dd116a LUCENE-8747: Allow access to submatches from Matches
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
lucene/CHANGES.txt | 8 ++
.../lucene/search/DisjunctionMatchesIterator.java | 84 ++++++++++-
.../src/java/org/apache/lucene/search/Matches.java | 7 +
.../org/apache/lucene/search/MatchesUtils.java | 24 +++-
.../org/apache/lucene/search/NamedMatches.java | 158 +++++++++++++++++++++
.../apache/lucene/search/TestMatchesIterator.java | 106 +++++++++++++-
.../org/apache/lucene/search/AssertingMatches.java | 14 ++
7 files changed, 386 insertions(+), 15 deletions(-)
create mode 100644 lucene/core/src/java/org/apache/lucene/search/NamedMatches.java
[lucene-solr] 01/02: LUCENE-8941: Build wildcard matches lazily
Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
romseygeek pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
commit b5b78e0adeb9db9345b69abedabd8c5cd684df7b
Author: Alan Woodward <ro...@apache.org>
AuthorDate: Mon Aug 5 09:44:44 2019 +0100
LUCENE-8941: Build wildcard matches lazily
---
lucene/CHANGES.txt | 3 +
.../lucene/search/DisjunctionMatchesIterator.java | 84 ++++++++++++++++++++--
.../apache/lucene/search/TestMatchesIterator.java | 66 ++++++++++++++++-
3 files changed, 148 insertions(+), 5 deletions(-)
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index bd1ee2b..88f22f1 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -42,6 +42,9 @@ Optimizations
* LUCENE-8935: BooleanQuery with no scoring clause can now early terminate the query when
the total hits is not requested.
+* LUCENE-8941: Matches on wildcard queries will defer building their full
+ disjunction until a MatchesIterator is pulled (Alan Woodward)
+
Other
* LUCENE-8778 LUCENE-8911: Define analyzer SPI names as static final fields and document the names in Javadocs.
diff --git a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMatchesIterator.java b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMatchesIterator.java
index f28c051..986b8d9 100644
--- a/lucene/core/src/java/org/apache/lucene/search/DisjunctionMatchesIterator.java
+++ b/lucene/core/src/java/org/apache/lucene/search/DisjunctionMatchesIterator.java
@@ -74,7 +74,6 @@ final class DisjunctionMatchesIterator implements MatchesIterator {
*/
static MatchesIterator fromTermsEnum(LeafReaderContext context, int doc, Query query, String field, BytesRefIterator terms) throws IOException {
Objects.requireNonNull(field);
- List<MatchesIterator> mis = new ArrayList<>();
Terms t = context.reader().terms(field);
if (t == null)
return null;
@@ -84,15 +83,92 @@ final class DisjunctionMatchesIterator implements MatchesIterator {
if (te.seekExact(term)) {
PostingsEnum pe = te.postings(reuse, PostingsEnum.OFFSETS);
if (pe.advance(doc) == doc) {
- mis.add(new TermMatchesIterator(query, pe));
- reuse = null;
+ return new TermsEnumDisjunctionMatchesIterator(new TermMatchesIterator(query, pe), terms, te, doc, query);
}
else {
reuse = pe;
}
}
}
- return fromSubIterators(mis);
+ return null;
+ }
+
+ // MatchesIterator over a set of terms that only loads the first matching term at construction,
+ // waiting until the iterator is actually used before it loads all other matching terms.
+ private static class TermsEnumDisjunctionMatchesIterator implements MatchesIterator {
+
+ private final MatchesIterator first;
+ private final BytesRefIterator terms;
+ private final TermsEnum te;
+ private final int doc;
+ private final Query query;
+
+ private MatchesIterator it = null;
+
+ TermsEnumDisjunctionMatchesIterator(MatchesIterator first, BytesRefIterator terms, TermsEnum te, int doc, Query query) {
+ this.first = first;
+ this.terms = terms;
+ this.te = te;
+ this.doc = doc;
+ this.query = query;
+ }
+
+ private void init() throws IOException {
+ List<MatchesIterator> mis = new ArrayList<>();
+ mis.add(first);
+ PostingsEnum reuse = null;
+ for (BytesRef term = terms.next(); term != null; term = terms.next()) {
+ if (te.seekExact(term)) {
+ PostingsEnum pe = te.postings(reuse, PostingsEnum.OFFSETS);
+ if (pe.advance(doc) == doc) {
+ mis.add(new TermMatchesIterator(query, pe));
+ reuse = null;
+ } else {
+ reuse = pe;
+ }
+ }
+ }
+ it = fromSubIterators(mis);
+ }
+
+ @Override
+ public boolean next() throws IOException {
+ if (it == null) {
+ init();
+ }
+ assert it != null;
+ return it.next();
+ }
+
+ @Override
+ public int startPosition() {
+ return it.startPosition();
+ }
+
+ @Override
+ public int endPosition() {
+ return it.endPosition();
+ }
+
+ @Override
+ public int startOffset() throws IOException {
+ return it.startOffset();
+ }
+
+ @Override
+ public int endOffset() throws IOException {
+ return it.endOffset();
+ }
+
+ @Override
+ public MatchesIterator getSubMatches() throws IOException {
+ return it.getSubMatches();
+ }
+
+ @Override
+ public Query getQuery() {
+ return it.getQuery();
+ }
}
static MatchesIterator fromSubIterators(List<MatchesIterator> mis) throws IOException {
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java b/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java
index 45fda8c..235bead 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java
@@ -31,25 +31,30 @@ import org.apache.lucene.document.FieldType;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.FilterLeafReader;
import org.apache.lucene.index.IndexOptions;
import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.ReaderUtil;
import org.apache.lucene.index.Term;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
import org.apache.lucene.search.spans.SpanQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.LuceneTestCase;
public class TestMatchesIterator extends LuceneTestCase {
protected IndexSearcher searcher;
protected Directory directory;
- protected IndexReader reader;
+ protected IndexReader reader = null;
private static final String FIELD_WITH_OFFSETS = "field_offsets";
private static final String FIELD_NO_OFFSETS = "field_no_offsets";
@@ -701,4 +706,63 @@ public class TestMatchesIterator extends LuceneTestCase {
});
}
+ public void testMinimalSeekingWithWildcards() throws IOException {
+ SeekCountingLeafReader reader = new SeekCountingLeafReader(getOnlyLeafReader(this.reader));
+ this.searcher = new IndexSearcher(reader);
+ Query query = new PrefixQuery(new Term(FIELD_WITH_OFFSETS, "w"));
+ Weight w = searcher.createWeight(query.rewrite(reader), ScoreMode.COMPLETE, 1);
+
+ // docs 0-3 match several different terms here, but we only seek to the first term and
+ // then short-cut return; other terms are ignored until we try and iterate over matches
+ int[] expectedSeeks = new int[]{ 1, 1, 1, 1, 6, 6 };
+ int i = 0;
+ for (LeafReaderContext ctx : reader.leaves()) {
+ for (int doc = 0; doc < ctx.reader().maxDoc(); doc++) {
+ reader.seeks = 0;
+ w.matches(ctx, doc);
+ assertEquals("Unexpected seek count on doc " + doc, expectedSeeks[i], reader.seeks);
+ i++;
+ }
+ }
+ }
+
+ private static class SeekCountingLeafReader extends FilterLeafReader {
+
+ int seeks = 0;
+
+ public SeekCountingLeafReader(LeafReader in) {
+ super(in);
+ }
+
+ @Override
+ public Terms terms(String field) throws IOException {
+ Terms terms = super.terms(field);
+ if (terms == null) {
+ return null;
+ }
+ return new FilterTerms(terms) {
+ @Override
+ public TermsEnum iterator() throws IOException {
+ return new FilterTermsEnum(super.iterator()) {
+ @Override
+ public boolean seekExact(BytesRef text) throws IOException {
+ seeks++;
+ return super.seekExact(text);
+ }
+ };
+ }
+ };
+ }
+
+ @Override
+ public CacheHelper getCoreCacheHelper() {
+ return null;
+ }
+
+ @Override
+ public CacheHelper getReaderCacheHelper() {
+ return null;
+ }
+ }
+
}
[lucene-solr] 02/02: LUCENE-8747: Allow access to submatches from
Matches
Posted by ro...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
romseygeek pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
commit 8dd116a615821c7d9b539316b051f466009b5130
Author: Alan Woodward <ro...@apache.org>
AuthorDate: Mon Aug 5 14:27:16 2019 +0100
LUCENE-8747: Allow access to submatches from Matches
---
lucene/CHANGES.txt | 5 +
.../src/java/org/apache/lucene/search/Matches.java | 7 +
.../org/apache/lucene/search/MatchesUtils.java | 24 +++-
.../org/apache/lucene/search/NamedMatches.java | 158 +++++++++++++++++++++
.../apache/lucene/search/TestMatchesIterator.java | 40 +++++-
.../org/apache/lucene/search/AssertingMatches.java | 14 ++
6 files changed, 238 insertions(+), 10 deletions(-)
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 88f22f1..46e79da 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -16,6 +16,11 @@ New Features
* LUCENE-8764: Add "export all terms" feature to Luke. (Leonardo Menezes via Tomoko Uchida)
+* LUCENE-8747: Composite Matches from multiple subqueries now allow access to
+ their submatches, and a new NamedMatches API allows marking of subqueries
+ and a simple way to find which subqueries have matched on a given document
+ (Alan Woodward, Jim Ferenczi)
+
Improvements
* LUCENE-8874: Show SPI names instead of class names in Luke Analysis tab. (Tomoko Uchida)
diff --git a/lucene/core/src/java/org/apache/lucene/search/Matches.java b/lucene/core/src/java/org/apache/lucene/search/Matches.java
index 43cffa6..80a860e 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Matches.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Matches.java
@@ -18,6 +18,7 @@
package org.apache.lucene.search;
import java.io.IOException;
+import java.util.Collection;
/**
* Reports the positions and optionally offsets of all matching terms in a query
@@ -37,4 +38,10 @@ public interface Matches extends Iterable<String> {
*/
MatchesIterator getMatches(String field) throws IOException;
+ /**
+ * Returns a collection of Matches that make up this instance; if it is not
+ * a composite, then this returns an empty list
+ */
+ Collection<Matches> getSubMatches();
+
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/MatchesUtils.java b/lucene/core/src/java/org/apache/lucene/search/MatchesUtils.java
index bf460b7..5dc966c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/MatchesUtils.java
+++ b/lucene/core/src/java/org/apache/lucene/search/MatchesUtils.java
@@ -19,6 +19,7 @@ package org.apache.lucene.search;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -44,13 +45,18 @@ public final class MatchesUtils {
*/
public static final Matches MATCH_WITH_NO_TERMS = new Matches() {
@Override
- public Iterator<String> iterator() {
- return Collections.emptyIterator();
+ public MatchesIterator getMatches(String field) {
+ return null;
}
@Override
- public MatchesIterator getMatches(String field) {
- return null;
+ public Collection<Matches> getSubMatches() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return Collections.emptyIterator();
}
};
@@ -87,6 +93,11 @@ public final class MatchesUtils {
// for each sub-match, iterate its fields (it's an Iterable of the fields), and return the distinct set
return sm.stream().flatMap(m -> StreamSupport.stream(m.spliterator(), false)).distinct().iterator();
}
+
+ @Override
+ public Collection<Matches> getSubMatches() {
+ return subMatches;
+ }
};
}
@@ -122,6 +133,11 @@ public final class MatchesUtils {
public Iterator<String> iterator() {
return Collections.singleton(field).iterator();
}
+
+ @Override
+ public Collection<Matches> getSubMatches() {
+ return Collections.emptyList();
+ }
};
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/NamedMatches.java b/lucene/core/src/java/org/apache/lucene/search/NamedMatches.java
new file mode 100644
index 0000000..c9cdf5e
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/NamedMatches.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.search;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.LeafReaderContext;
+
+/**
+ * Utility class to help extract the set of sub queries that have matched from
+ * a larger query.
+ *
+ * Individual subqueries may be wrapped using {@link #wrapQuery(String, Query)}, and
+ * the matching queries for a particular document can then be pulled from the parent
+ * Query's {@link Matches} object by calling {@link #findNamedMatches(Matches)}
+ */
+public class NamedMatches implements Matches {
+
+ private final Matches in;
+ private final String name;
+
+ /**
+ * Wraps a {@link Matches} object and associates a name with it
+ */
+ public NamedMatches(String name, Matches in) {
+ this.in = Objects.requireNonNull(in);
+ this.name = name;
+ }
+
+ /**
+ * Returns the name of this {@link Matches}
+ */
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public MatchesIterator getMatches(String field) throws IOException {
+ return in.getMatches(field);
+ }
+
+ @Override
+ public Collection<Matches> getSubMatches() {
+ return Collections.singleton(in);
+ }
+
+ @Override
+ public Iterator<String> iterator() {
+ return in.iterator();
+ }
+
+ /**
+ * Wrap a Query so that it associates a name with its {@link Matches}
+ */
+ public static Query wrapQuery(String name, Query in) {
+ return new NamedQuery(name, in);
+ }
+
+ /**
+ * Finds all {@link NamedMatches} in a {@link Matches} tree
+ */
+ public static List<NamedMatches> findNamedMatches(Matches matches) {
+ List<NamedMatches> nm = new ArrayList<>();
+ List<Matches> toProcess = new LinkedList<>();
+ toProcess.add(matches);
+ while (toProcess.size() > 0) {
+ matches = toProcess.remove(0);
+ if (matches instanceof NamedMatches) {
+ nm.add((NamedMatches) matches);
+ }
+ toProcess.addAll(matches.getSubMatches());
+ }
+ return nm;
+ }
+
+ private static class NamedQuery extends Query {
+
+ private final String name;
+ private final Query in;
+
+ private NamedQuery(String name, Query in) {
+ this.name = name;
+ this.in = in;
+ }
+
+ @Override
+ public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
+ Weight w = in.createWeight(searcher, scoreMode, boost);
+ return new FilterWeight(w) {
+ @Override
+ public Matches matches(LeafReaderContext context, int doc) throws IOException {
+ Matches m = in.matches(context, doc);
+ if (m == null) {
+ return null;
+ }
+ return new NamedMatches(name, m);
+ }
+ };
+ }
+
+ @Override
+ public Query rewrite(IndexReader reader) throws IOException {
+ Query rewritten = in.rewrite(reader);
+ if (rewritten != in) {
+ return new NamedQuery(name, rewritten);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString(String field) {
+ return "NamedQuery(" + name + "," + in.toString(field) + ")";
+ }
+
+ @Override
+ public void visit(QueryVisitor visitor) {
+ QueryVisitor sub = visitor.getSubVisitor(BooleanClause.Occur.MUST, this);
+ in.visit(sub);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ NamedQuery that = (NamedQuery) o;
+ return Objects.equals(name, that.name) &&
+ Objects.equals(in, that.in);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, in);
+ }
+ }
+}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java b/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java
index 235bead..3948406 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestMatchesIterator.java
@@ -192,6 +192,29 @@ public class TestMatchesIterator extends LuceneTestCase {
}
}
+ private void checkSubMatches(Query q, String[][] expectedNames) throws IOException {
+ Weight w = searcher.createWeight(searcher.rewrite(q), ScoreMode.COMPLETE_NO_SCORES, 1);
+ for (int i = 0; i < expectedNames.length; i++) {
+ LeafReaderContext ctx = searcher.leafContexts.get(ReaderUtil.subIndex(i, searcher.leafContexts));
+ int doc = i - ctx.docBase;
+ Matches matches = w.matches(ctx, doc);
+ if (matches == null) {
+ assertEquals("Expected to get no matches on document " + i, 0, expectedNames[i].length);
+ continue;
+ }
+ Set<String> expectedQueries = new HashSet<>(Arrays.asList(expectedNames[i]));
+ Set<String> actualQueries = NamedMatches.findNamedMatches(matches)
+ .stream().map(NamedMatches::getName).collect(Collectors.toSet());
+
+ Set<String> unexpected = new HashSet<>(actualQueries);
+ unexpected.removeAll(expectedQueries);
+ assertEquals("Unexpected matching leaf queries: " + unexpected, 0, unexpected.size());
+ Set<String> missing = new HashSet<>(expectedQueries);
+ missing.removeAll(actualQueries);
+ assertEquals("Missing matching leaf queries: " + missing, 0, missing.size());
+ }
+ }
+
private void assertIsLeafMatch(Query q, String field) throws IOException {
Weight w = searcher.createWeight(searcher.rewrite(q), ScoreMode.COMPLETE, 1);
for (int i = 0; i < searcher.reader.maxDoc(); i++) {
@@ -294,7 +317,7 @@ public class TestMatchesIterator extends LuceneTestCase {
public void testTermQuery() throws IOException {
Term t = new Term(FIELD_WITH_OFFSETS, "w1");
- Query q = new TermQuery(t);
+ Query q = NamedMatches.wrapQuery("q", new TermQuery(t));
checkMatches(q, FIELD_WITH_OFFSETS, new int[][]{
{ 0, 0, 0, 0, 2 },
{ 1, 0, 0, 0, 2 },
@@ -304,6 +327,7 @@ public class TestMatchesIterator extends LuceneTestCase {
});
checkLabelCount(q, FIELD_WITH_OFFSETS, new int[]{ 1, 1, 1, 1, 0, 0 });
assertIsLeafMatch(q, FIELD_WITH_OFFSETS);
+ checkSubMatches(q, new String[][]{ {"q"}, {"q"}, {"q"}, {"q"}, {}, {}});
}
public void testTermQueryNoStoredOffsets() throws IOException {
@@ -325,8 +349,8 @@ public class TestMatchesIterator extends LuceneTestCase {
}
public void testDisjunction() throws IOException {
- Query w1 = new TermQuery(new Term(FIELD_WITH_OFFSETS, "w1"));
- Query w3 = new TermQuery(new Term(FIELD_WITH_OFFSETS, "w3"));
+ Query w1 = NamedMatches.wrapQuery("w1", new TermQuery(new Term(FIELD_WITH_OFFSETS, "w1")));
+ Query w3 = NamedMatches.wrapQuery("w3", new TermQuery(new Term(FIELD_WITH_OFFSETS, "w3")));
Query q = new BooleanQuery.Builder()
.add(w1, BooleanClause.Occur.SHOULD)
.add(w3, BooleanClause.Occur.SHOULD)
@@ -340,6 +364,7 @@ public class TestMatchesIterator extends LuceneTestCase {
});
checkLabelCount(q, FIELD_WITH_OFFSETS, new int[]{ 2, 2, 1, 2, 0, 0 });
assertIsLeafMatch(q, FIELD_WITH_OFFSETS);
+ checkSubMatches(q, new String[][]{ {"w1", "w3"}, {"w1", "w3"}, {"w1"}, {"w1", "w3"}, {}, {}});
}
public void testDisjunctionNoPositions() throws IOException {
@@ -378,10 +403,10 @@ public class TestMatchesIterator extends LuceneTestCase {
}
public void testMinShouldMatch() throws IOException {
- Query w1 = new TermQuery(new Term(FIELD_WITH_OFFSETS, "w1"));
- Query w3 = new TermQuery(new Term(FIELD_WITH_OFFSETS, "w3"));
+ Query w1 = NamedMatches.wrapQuery("w1", new TermQuery(new Term(FIELD_WITH_OFFSETS, "w1")));
+ Query w3 = NamedMatches.wrapQuery("w3", new TermQuery(new Term(FIELD_WITH_OFFSETS, "w3")));
Query w4 = new TermQuery(new Term(FIELD_WITH_OFFSETS, "w4"));
- Query xx = new TermQuery(new Term(FIELD_WITH_OFFSETS, "xx"));
+ Query xx = NamedMatches.wrapQuery("xx", new TermQuery(new Term(FIELD_WITH_OFFSETS, "xx")));
Query q = new BooleanQuery.Builder()
.add(w3, BooleanClause.Occur.SHOULD)
.add(new BooleanQuery.Builder()
@@ -400,6 +425,7 @@ public class TestMatchesIterator extends LuceneTestCase {
});
checkLabelCount(q, FIELD_WITH_OFFSETS, new int[]{ 3, 1, 3, 3, 0, 0 });
assertIsLeafMatch(q, FIELD_WITH_OFFSETS);
+ checkSubMatches(q, new String[][]{ {"w1", "w3"}, {"w3"}, {"w1", "xx"}, {"w1", "w3"}, {}, {}});
}
public void testMinShouldMatchNoPositions() throws IOException {
@@ -544,6 +570,8 @@ public class TestMatchesIterator extends LuceneTestCase {
assertEquals(2, fields.size());
assertTrue(fields.contains(FIELD_WITH_OFFSETS));
assertTrue(fields.contains("id"));
+
+ assertEquals(2, AssertingMatches.unWrap(m).getSubMatches().size());
}
// 0 1 2 3 4 5 6 7
diff --git a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingMatches.java b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingMatches.java
index c5c6e98..f57a83b 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/search/AssertingMatches.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/search/AssertingMatches.java
@@ -18,6 +18,8 @@
package org.apache.lucene.search;
import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
class AssertingMatches implements Matches {
@@ -37,7 +39,19 @@ class AssertingMatches implements Matches {
}
@Override
+ public Collection<Matches> getSubMatches() {
+ return Collections.singleton(in);
+ }
+
+ @Override
public Iterator<String> iterator() {
return in.iterator();
}
+
+ public static Matches unWrap(Matches m) {
+ while (m instanceof AssertingMatches) {
+ m = (((AssertingMatches)m).in);
+ }
+ return m;
+ }
}