mirror of https://github.com/apache/lucene.git
SOLR-6371: Simplify SpanCollector interface
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1684700 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
d5fac68323
commit
9c12b35df9
|
@ -26,7 +26,6 @@ import org.apache.lucene.search.Scorer;
|
||||||
import org.apache.lucene.search.similarities.DefaultSimilarity;
|
import org.apache.lucene.search.similarities.DefaultSimilarity;
|
||||||
import org.apache.lucene.search.similarities.Similarity;
|
import org.apache.lucene.search.similarities.Similarity;
|
||||||
import org.apache.lucene.search.similarities.Similarity.SimScorer;
|
import org.apache.lucene.search.similarities.Similarity.SimScorer;
|
||||||
import org.apache.lucene.search.spans.SpanCollectorFactory;
|
|
||||||
import org.apache.lucene.search.spans.SpanNearQuery;
|
import org.apache.lucene.search.spans.SpanNearQuery;
|
||||||
import org.apache.lucene.search.spans.SpanQuery;
|
import org.apache.lucene.search.spans.SpanQuery;
|
||||||
import org.apache.lucene.search.spans.SpanScorer;
|
import org.apache.lucene.search.spans.SpanScorer;
|
||||||
|
@ -75,10 +74,10 @@ public class PayloadNearQuery extends SpanNearQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
List<SpanWeight> subWeights = new ArrayList<>();
|
List<SpanWeight> subWeights = new ArrayList<>();
|
||||||
for (SpanQuery q : clauses) {
|
for (SpanQuery q : clauses) {
|
||||||
subWeights.add(q.createWeight(searcher, false, PayloadSpanCollector.FACTORY));
|
subWeights.add(q.createWeight(searcher, false));
|
||||||
}
|
}
|
||||||
return new PayloadNearSpanWeight(subWeights, searcher, needsScores ? getTermContexts(subWeights) : null);
|
return new PayloadNearSpanWeight(subWeights, searcher, needsScores ? getTermContexts(subWeights) : null);
|
||||||
}
|
}
|
||||||
|
@ -141,17 +140,14 @@ public class PayloadNearQuery extends SpanNearQuery {
|
||||||
|
|
||||||
public PayloadNearSpanWeight(List<SpanWeight> subWeights, IndexSearcher searcher, Map<Term, TermContext> terms)
|
public PayloadNearSpanWeight(List<SpanWeight> subWeights, IndexSearcher searcher, Map<Term, TermContext> terms)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
super(subWeights, searcher, terms, PayloadSpanCollector.FACTORY);
|
super(subWeights, searcher, terms);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
|
public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
|
||||||
PayloadSpanCollector collector = (PayloadSpanCollector) collectorFactory.newCollector();
|
Spans spans = super.getSpans(context, acceptDocs, Postings.PAYLOADS);
|
||||||
Spans spans = super.getSpans(context, acceptDocs, collector);
|
|
||||||
Similarity.SimScorer simScorer = simWeight == null ? null : similarity.simScorer(simWeight, context);
|
Similarity.SimScorer simScorer = simWeight == null ? null : similarity.simScorer(simWeight, context);
|
||||||
return (spans == null)
|
return (spans == null) ? null : new PayloadNearSpanScorer(spans, this, simScorer);
|
||||||
? null
|
|
||||||
: new PayloadNearSpanScorer(spans, this, collector, simScorer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -187,13 +183,11 @@ public class PayloadNearQuery extends SpanNearQuery {
|
||||||
Spans spans;
|
Spans spans;
|
||||||
protected float payloadScore;
|
protected float payloadScore;
|
||||||
private int payloadsSeen;
|
private int payloadsSeen;
|
||||||
private final PayloadSpanCollector collector;
|
private final PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
|
|
||||||
protected PayloadNearSpanScorer(Spans spans, SpanWeight weight, PayloadSpanCollector collector,
|
protected PayloadNearSpanScorer(Spans spans, SpanWeight weight, Similarity.SimScorer docScorer) throws IOException {
|
||||||
Similarity.SimScorer docScorer) throws IOException {
|
|
||||||
super(spans, weight, docScorer);
|
super(spans, weight, docScorer);
|
||||||
this.spans = spans;
|
this.spans = spans;
|
||||||
this.collector = collector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO change the whole spans api to use bytesRef, or nuke spans
|
// TODO change the whole spans api to use bytesRef, or nuke spans
|
||||||
|
|
|
@ -19,10 +19,7 @@ package org.apache.lucene.search.payloads;
|
||||||
|
|
||||||
import org.apache.lucene.index.PostingsEnum;
|
import org.apache.lucene.index.PostingsEnum;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.search.spans.BufferedSpanCollector;
|
|
||||||
import org.apache.lucene.search.spans.SpanCollector;
|
import org.apache.lucene.search.spans.SpanCollector;
|
||||||
import org.apache.lucene.search.spans.SpanCollectorFactory;
|
|
||||||
import org.apache.lucene.search.spans.Spans;
|
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -30,36 +27,14 @@ import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SpanCollector implementation that collects payloads from a {@link Spans}
|
* SpanCollector for collecting payloads
|
||||||
*/
|
*/
|
||||||
public class PayloadSpanCollector implements SpanCollector {
|
public class PayloadSpanCollector implements SpanCollector {
|
||||||
|
|
||||||
public static final SpanCollectorFactory FACTORY = new SpanCollectorFactory() {
|
|
||||||
@Override
|
|
||||||
public PayloadSpanCollector newCollector() {
|
|
||||||
return new PayloadSpanCollector();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Collection<byte[]> payloads = new ArrayList<>();
|
private final Collection<byte[]> payloads = new ArrayList<>();
|
||||||
BufferedPayloadCollector bufferedCollector;
|
|
||||||
|
|
||||||
public Collection<byte[]> getPayloads() {
|
|
||||||
return payloads;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void collectLeaf(PostingsEnum postings, int position, Term term) throws IOException {
|
||||||
payloads.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int requiredPostings() {
|
|
||||||
return PostingsEnum.PAYLOADS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void collectLeaf(PostingsEnum postings, Term term) throws IOException {
|
|
||||||
BytesRef payload = postings.getPayload();
|
BytesRef payload = postings.getPayload();
|
||||||
if (payload == null)
|
if (payload == null)
|
||||||
return;
|
return;
|
||||||
|
@ -69,43 +44,14 @@ public class PayloadSpanCollector implements SpanCollector {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BufferedSpanCollector buffer() {
|
public void reset() {
|
||||||
if (bufferedCollector == null)
|
payloads.clear();
|
||||||
bufferedCollector = new BufferedPayloadCollector();
|
|
||||||
bufferedCollector.reset();
|
|
||||||
return bufferedCollector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public SpanCollector bufferedCollector() {
|
* @return the collected payloads
|
||||||
if (bufferedCollector == null)
|
*/
|
||||||
bufferedCollector = new BufferedPayloadCollector();
|
public Collection<byte[]> getPayloads() {
|
||||||
return bufferedCollector.candidateCollector;
|
return payloads;
|
||||||
}
|
|
||||||
|
|
||||||
class BufferedPayloadCollector implements BufferedSpanCollector {
|
|
||||||
|
|
||||||
final Collection<byte[]> buffer = new ArrayList<>();
|
|
||||||
PayloadSpanCollector candidateCollector = new PayloadSpanCollector();
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
buffer.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void collectCandidate(Spans spans) throws IOException {
|
|
||||||
candidateCollector.reset();
|
|
||||||
spans.collect(candidateCollector);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept() {
|
|
||||||
buffer.addAll(candidateCollector.payloads);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replay() {
|
|
||||||
payloads.addAll(buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ public class PayloadSpanUtil {
|
||||||
|
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
for (LeafReaderContext leafReaderContext : context.leaves()) {
|
for (LeafReaderContext leafReaderContext : context.leaves()) {
|
||||||
final Spans spans = w.getSpans(leafReaderContext, leafReaderContext.reader().getLiveDocs(), collector);
|
final Spans spans = w.getSpans(leafReaderContext, leafReaderContext.reader().getLiveDocs(), SpanWeight.Postings.PAYLOADS);
|
||||||
if (spans != null) {
|
if (spans != null) {
|
||||||
while (spans.nextDoc() != Spans.NO_MORE_DOCS) {
|
while (spans.nextDoc() != Spans.NO_MORE_DOCS) {
|
||||||
while (spans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
|
while (spans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
|
||||||
|
|
|
@ -26,7 +26,6 @@ import org.apache.lucene.search.IndexSearcher;
|
||||||
import org.apache.lucene.search.similarities.DefaultSimilarity;
|
import org.apache.lucene.search.similarities.DefaultSimilarity;
|
||||||
import org.apache.lucene.search.similarities.Similarity;
|
import org.apache.lucene.search.similarities.Similarity;
|
||||||
import org.apache.lucene.search.similarities.Similarity.SimScorer;
|
import org.apache.lucene.search.similarities.Similarity.SimScorer;
|
||||||
import org.apache.lucene.search.spans.BufferedSpanCollector;
|
|
||||||
import org.apache.lucene.search.spans.SpanCollector;
|
import org.apache.lucene.search.spans.SpanCollector;
|
||||||
import org.apache.lucene.search.spans.SpanQuery;
|
import org.apache.lucene.search.spans.SpanQuery;
|
||||||
import org.apache.lucene.search.spans.SpanScorer;
|
import org.apache.lucene.search.spans.SpanScorer;
|
||||||
|
@ -80,58 +79,39 @@ public class PayloadTermQuery extends SpanTermQuery {
|
||||||
BytesRef payload;
|
BytesRef payload;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() {
|
public void collectLeaf(PostingsEnum postings, int position, Term term) throws IOException {
|
||||||
payload = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int requiredPostings() {
|
|
||||||
return PostingsEnum.PAYLOADS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void collectLeaf(PostingsEnum postings, Term term) throws IOException {
|
|
||||||
payload = postings.getPayload();
|
payload = postings.getPayload();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BufferedSpanCollector buffer() {
|
public void reset() {
|
||||||
throw new UnsupportedOperationException();
|
payload = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SpanCollector bufferedCollector() {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class PayloadTermWeight extends SpanTermWeight {
|
private class PayloadTermWeight extends SpanTermWeight {
|
||||||
|
|
||||||
public PayloadTermWeight(TermContext context, IndexSearcher searcher, Map<Term, TermContext> terms)
|
public PayloadTermWeight(TermContext context, IndexSearcher searcher, Map<Term, TermContext> terms)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
super(context, searcher, terms, PayloadSpanCollector.FACTORY);
|
super(context, searcher, terms);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PayloadTermSpanScorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
|
public PayloadTermSpanScorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
|
||||||
PayloadTermCollector collector = new PayloadTermCollector();
|
Spans spans = super.getSpans(context, acceptDocs, Postings.PAYLOADS);
|
||||||
Spans spans = super.getSpans(context, acceptDocs, collector);
|
|
||||||
Similarity.SimScorer simScorer = simWeight == null ? null : similarity.simScorer(simWeight, context);
|
Similarity.SimScorer simScorer = simWeight == null ? null : similarity.simScorer(simWeight, context);
|
||||||
return (spans == null)
|
return (spans == null) ? null : new PayloadTermSpanScorer(spans, this, simScorer);
|
||||||
? null
|
|
||||||
: new PayloadTermSpanScorer(spans, this, collector, simScorer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class PayloadTermSpanScorer extends SpanScorer {
|
protected class PayloadTermSpanScorer extends SpanScorer {
|
||||||
protected BytesRef payload;
|
protected BytesRef payload;
|
||||||
protected float payloadScore;
|
protected float payloadScore;
|
||||||
protected int payloadsSeen;
|
protected int payloadsSeen;
|
||||||
private final PayloadTermCollector payloadCollector;
|
private final PayloadTermCollector payloadCollector = new PayloadTermCollector();
|
||||||
|
|
||||||
public PayloadTermSpanScorer(Spans spans, SpanWeight weight, PayloadTermCollector collector,
|
public PayloadTermSpanScorer(Spans spans, SpanWeight weight, Similarity.SimScorer docScorer) throws IOException {
|
||||||
Similarity.SimScorer docScorer) throws IOException {
|
|
||||||
super(spans, weight, docScorer);
|
super(spans, weight, docScorer);
|
||||||
this.payloadCollector = collector;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -144,7 +124,10 @@ public class PayloadTermQuery extends SpanTermQuery {
|
||||||
assert startPos != Spans.NO_MORE_POSITIONS : "initial startPos NO_MORE_POSITIONS, spans="+spans;
|
assert startPos != Spans.NO_MORE_POSITIONS : "initial startPos NO_MORE_POSITIONS, spans="+spans;
|
||||||
do {
|
do {
|
||||||
int matchLength = spans.endPosition() - startPos;
|
int matchLength = spans.endPosition() - startPos;
|
||||||
|
if (docScorer == null) {
|
||||||
|
freq = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
freq += docScorer.computeSlopFactor(matchLength);
|
freq += docScorer.computeSlopFactor(matchLength);
|
||||||
numMatches++;
|
numMatches++;
|
||||||
payloadCollector.reset();
|
payloadCollector.reset();
|
||||||
|
@ -156,13 +139,11 @@ public class PayloadTermQuery extends SpanTermQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void processPayload() throws IOException {
|
protected void processPayload() throws IOException {
|
||||||
|
|
||||||
float payloadFactor = payloadCollector.payload == null ? 1F :
|
float payloadFactor = payloadCollector.payload == null ? 1F :
|
||||||
docScorer.computePayloadFactor(docID(), spans.startPosition(), spans.endPosition(), payloadCollector.payload);
|
docScorer.computePayloadFactor(docID(), spans.startPosition(), spans.endPosition(), payloadCollector.payload);
|
||||||
payloadScore = function.currentScore(docID(), term.field(), spans.startPosition(), spans.endPosition(),
|
payloadScore = function.currentScore(docID(), term.field(), spans.startPosition(), spans.endPosition(),
|
||||||
payloadsSeen, payloadScore, payloadFactor);
|
payloadsSeen, payloadScore, payloadFactor);
|
||||||
payloadsSeen++;
|
payloadsSeen++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,75 +16,27 @@ package org.apache.lucene.search.payloads;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
|
||||||
import org.apache.lucene.search.spans.FilterSpans.AcceptStatus;
|
|
||||||
import org.apache.lucene.search.spans.SpanCollector;
|
|
||||||
import org.apache.lucene.search.spans.SpanNearQuery;
|
import org.apache.lucene.search.spans.SpanNearQuery;
|
||||||
import org.apache.lucene.search.spans.SpanPositionCheckQuery;
|
|
||||||
import org.apache.lucene.search.spans.SpanWeight;
|
|
||||||
import org.apache.lucene.search.spans.Spans;
|
|
||||||
import org.apache.lucene.util.ToStringUtils;
|
import org.apache.lucene.util.ToStringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only return those matches that have a specific payload at
|
* Only return those matches that have a specific payload at
|
||||||
* the given position.
|
* the given position.
|
||||||
|
*
|
||||||
|
* @deprecated Use {@link SpanPayloadCheckQuery}
|
||||||
*/
|
*/
|
||||||
public class SpanNearPayloadCheckQuery extends SpanPositionCheckQuery {
|
@Deprecated
|
||||||
|
public class SpanNearPayloadCheckQuery extends SpanPayloadCheckQuery {
|
||||||
protected final Collection<byte[]> payloadToMatch;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param match The underlying {@link org.apache.lucene.search.spans.SpanQuery} to check
|
* @param match The underlying {@link org.apache.lucene.search.spans.SpanQuery} to check
|
||||||
* @param payloadToMatch The {@link java.util.Collection} of payloads to match
|
* @param payloadToMatch The {@link java.util.Collection} of payloads to match
|
||||||
*/
|
*/
|
||||||
public SpanNearPayloadCheckQuery(SpanNearQuery match, Collection<byte[]> payloadToMatch) {
|
public SpanNearPayloadCheckQuery(SpanNearQuery match, Collection<byte[]> payloadToMatch) {
|
||||||
super(match);
|
super(match, payloadToMatch);
|
||||||
this.payloadToMatch = Objects.requireNonNull(payloadToMatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
|
||||||
return createWeight(searcher, needsScores, PayloadSpanCollector.FACTORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected AcceptStatus acceptPosition(Spans spans, SpanCollector collector) throws IOException {
|
|
||||||
|
|
||||||
PayloadSpanCollector payloadCollector = (PayloadSpanCollector) collector;
|
|
||||||
|
|
||||||
payloadCollector.reset();
|
|
||||||
spans.collect(payloadCollector);
|
|
||||||
|
|
||||||
Collection<byte[]> candidate = payloadCollector.getPayloads();
|
|
||||||
if (candidate.size() == payloadToMatch.size()) {
|
|
||||||
//TODO: check the byte arrays are the same
|
|
||||||
//hmm, can't rely on order here
|
|
||||||
int matches = 0;
|
|
||||||
for (byte[] candBytes : candidate) {
|
|
||||||
//Unfortunately, we can't rely on order, so we need to compare all
|
|
||||||
for (byte[] payBytes : payloadToMatch) {
|
|
||||||
if (Arrays.equals(candBytes, payBytes) == true) {
|
|
||||||
matches++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matches == payloadToMatch.size()){
|
|
||||||
//we've verified all the bytes
|
|
||||||
return AcceptStatus.YES;
|
|
||||||
} else {
|
|
||||||
return AcceptStatus.NO;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return AcceptStatus.NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,60 +16,130 @@ package org.apache.lucene.search.payloads;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.index.Term;
|
||||||
|
import org.apache.lucene.index.TermContext;
|
||||||
|
import org.apache.lucene.index.Terms;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.search.Scorer;
|
||||||
|
import org.apache.lucene.search.similarities.Similarity;
|
||||||
|
import org.apache.lucene.search.spans.FilterSpans;
|
||||||
import org.apache.lucene.search.spans.FilterSpans.AcceptStatus;
|
import org.apache.lucene.search.spans.FilterSpans.AcceptStatus;
|
||||||
import org.apache.lucene.search.spans.SpanCollector;
|
|
||||||
import org.apache.lucene.search.spans.SpanNearQuery;
|
import org.apache.lucene.search.spans.SpanNearQuery;
|
||||||
import org.apache.lucene.search.spans.SpanPositionCheckQuery;
|
|
||||||
import org.apache.lucene.search.spans.SpanQuery;
|
import org.apache.lucene.search.spans.SpanQuery;
|
||||||
|
import org.apache.lucene.search.spans.SpanScorer;
|
||||||
import org.apache.lucene.search.spans.SpanWeight;
|
import org.apache.lucene.search.spans.SpanWeight;
|
||||||
import org.apache.lucene.search.spans.Spans;
|
import org.apache.lucene.search.spans.Spans;
|
||||||
|
import org.apache.lucene.util.Bits;
|
||||||
import org.apache.lucene.util.ToStringUtils;
|
import org.apache.lucene.util.ToStringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only return those matches that have a specific payload at
|
* Only return those matches that have a specific payload at the given position.
|
||||||
* the given position.
|
|
||||||
* <p>
|
|
||||||
* Do not use this with a SpanQuery that contains a {@link org.apache.lucene.search.spans.SpanNearQuery}.
|
|
||||||
* Instead, use {@link org.apache.lucene.search.payloads.SpanNearPayloadCheckQuery} since it properly handles the fact that payloads
|
|
||||||
* aren't ordered by {@link org.apache.lucene.search.spans.SpanNearQuery}.
|
|
||||||
*/
|
*/
|
||||||
public class SpanPayloadCheckQuery extends SpanPositionCheckQuery {
|
public class SpanPayloadCheckQuery extends SpanQuery {
|
||||||
|
|
||||||
protected final Collection<byte[]> payloadToMatch;
|
protected final Collection<byte[]> payloadToMatch;
|
||||||
|
protected final SpanQuery match;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param match The underlying {@link org.apache.lucene.search.spans.SpanQuery} to check
|
* @param match The underlying {@link org.apache.lucene.search.spans.SpanQuery} to check
|
||||||
* @param payloadToMatch The {@link java.util.Collection} of payloads to match
|
* @param payloadToMatch The {@link java.util.Collection} of payloads to match
|
||||||
*/
|
*/
|
||||||
public SpanPayloadCheckQuery(SpanQuery match, Collection<byte[]> payloadToMatch) {
|
public SpanPayloadCheckQuery(SpanQuery match, Collection<byte[]> payloadToMatch) {
|
||||||
super(match);
|
this.match = match;
|
||||||
if (match instanceof SpanNearQuery){
|
|
||||||
throw new IllegalArgumentException("SpanNearQuery not allowed");
|
|
||||||
}
|
|
||||||
this.payloadToMatch = payloadToMatch;
|
this.payloadToMatch = payloadToMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
public String getField() {
|
||||||
return super.createWeight(searcher, needsScores, PayloadSpanCollector.FACTORY);
|
return match.getField();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AcceptStatus acceptPosition(Spans spans, SpanCollector collector) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
|
SpanWeight matchWeight = match.createWeight(searcher, false);
|
||||||
|
return new SpanPayloadCheckWeight(searcher, needsScores ? getTermContexts(matchWeight) : null, matchWeight);
|
||||||
|
}
|
||||||
|
|
||||||
PayloadSpanCollector payloadCollector = (PayloadSpanCollector) collector;
|
/**
|
||||||
|
* Weight that pulls its Spans using a PayloadSpanCollector
|
||||||
|
*/
|
||||||
|
public class SpanPayloadCheckWeight extends SpanWeight {
|
||||||
|
|
||||||
payloadCollector.reset();
|
final SpanWeight matchWeight;
|
||||||
spans.collect(payloadCollector);
|
|
||||||
|
|
||||||
Collection<byte[]> candidate = payloadCollector.getPayloads();
|
public SpanPayloadCheckWeight(IndexSearcher searcher, Map<Term, TermContext> termContexts, SpanWeight matchWeight) throws IOException {
|
||||||
|
super(SpanPayloadCheckQuery.this, searcher, termContexts);
|
||||||
|
this.matchWeight = matchWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void extractTerms(Set<Term> terms) {
|
||||||
|
matchWeight.extractTerms(terms);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void extractTermContexts(Map<Term, TermContext> contexts) {
|
||||||
|
matchWeight.extractTermContexts(contexts);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, Postings requiredPostings) throws IOException {
|
||||||
|
final PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
|
Spans matchSpans = matchWeight.getSpans(context, acceptDocs, requiredPostings.atLeast(Postings.PAYLOADS));
|
||||||
|
return (matchSpans == null) ? null : new FilterSpans(matchSpans) {
|
||||||
|
@Override
|
||||||
|
protected AcceptStatus accept(Spans candidate) throws IOException {
|
||||||
|
|
||||||
|
collector.reset();
|
||||||
|
candidate.collect(collector);
|
||||||
|
Collection<byte[]> collected = collector.getPayloads();
|
||||||
|
|
||||||
|
if (match instanceof SpanNearQuery) {
|
||||||
|
return checkCompositePayloads(collected);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return checkOrderedPayloads(collected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Scorer scorer(LeafReaderContext context, Bits acceptDocs) throws IOException {
|
||||||
|
if (field == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Terms terms = context.reader().terms(field);
|
||||||
|
if (terms != null && terms.hasPositions() == false) {
|
||||||
|
throw new IllegalStateException("field \"" + field + "\" was indexed without position data; cannot run SpanQuery (query=" + parentQuery + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
Spans spans = getSpans(context, acceptDocs, Postings.PAYLOADS);
|
||||||
|
Similarity.SimScorer simScorer = simWeight == null ? null : similarity.simScorer(simWeight, context);
|
||||||
|
return (spans == null) ? null : new SpanScorer(spans, this, simScorer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if the collected payloads match the required set.
|
||||||
|
*
|
||||||
|
* This is called for Near span queries which collect their sub spans
|
||||||
|
* out-of-order, meaning that we can't rely on the order of payloads
|
||||||
|
* in the collection
|
||||||
|
*
|
||||||
|
* @param candidate a collection of payloads from the current Spans
|
||||||
|
* @return whether or not the payloads match
|
||||||
|
*/
|
||||||
|
protected AcceptStatus checkOrderedPayloads(Collection<byte[]> candidate) {
|
||||||
if (candidate.size() == payloadToMatch.size()){
|
if (candidate.size() == payloadToMatch.size()){
|
||||||
//TODO: check the byte arrays are the same
|
//TODO: check the byte arrays are the same
|
||||||
Iterator<byte[]> toMatchIter = payloadToMatch.iterator();
|
Iterator<byte[]> toMatchIter = payloadToMatch.iterator();
|
||||||
|
@ -86,7 +156,36 @@ public class SpanPayloadCheckQuery extends SpanPositionCheckQuery {
|
||||||
} else {
|
} else {
|
||||||
return AcceptStatus.NO;
|
return AcceptStatus.NO;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if the collected payloads match the required set.
|
||||||
|
* @param candidate a collection of payloads from the current Spans
|
||||||
|
* @return whether or not the payloads match
|
||||||
|
*/
|
||||||
|
protected AcceptStatus checkCompositePayloads(Collection<byte[]> candidate) {
|
||||||
|
if (candidate.size() == payloadToMatch.size()) {
|
||||||
|
//TODO: check the byte arrays are the same
|
||||||
|
//hmm, can't rely on order here
|
||||||
|
int matches = 0;
|
||||||
|
for (byte[] candBytes : candidate) {
|
||||||
|
//Unfortunately, we can't rely on order, so we need to compare all
|
||||||
|
for (byte[] payBytes : payloadToMatch) {
|
||||||
|
if (Arrays.equals(candBytes, payBytes) == true) {
|
||||||
|
matches++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matches == payloadToMatch.size()){
|
||||||
|
//we've verified all the bytes
|
||||||
|
return AcceptStatus.YES;
|
||||||
|
} else {
|
||||||
|
return AcceptStatus.NO;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return AcceptStatus.NO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
package org.apache.lucene.search.spans;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines span collection for eager Span implementations, such as
|
|
||||||
* {@link org.apache.lucene.search.spans.NearSpansOrdered}
|
|
||||||
*
|
|
||||||
* @lucene.experimental
|
|
||||||
*/
|
|
||||||
public interface BufferedSpanCollector {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect information from a possible candidate
|
|
||||||
* @param spans the candidate Spans
|
|
||||||
* @throws IOException on error
|
|
||||||
*/
|
|
||||||
public void collectCandidate(Spans spans) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Confirm that the last candidate Spans has been accepted by the parent algorithm
|
|
||||||
*/
|
|
||||||
public void accept();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replay buffered information back to the parent SpanCollector
|
|
||||||
*/
|
|
||||||
public void replay();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A default No-op BufferedSpanCollector
|
|
||||||
*/
|
|
||||||
public static final BufferedSpanCollector NO_OP = new BufferedSpanCollector() {
|
|
||||||
@Override
|
|
||||||
public void collectCandidate(Spans spans) throws IOException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replay() {
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -90,8 +90,8 @@ public class FieldMaskingSpanQuery extends SpanQuery {
|
||||||
// ...this is done to be more consistent with things like SpanFirstQuery
|
// ...this is done to be more consistent with things like SpanFirstQuery
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
return maskedQuery.createWeight(searcher, needsScores, factory);
|
return maskedQuery.createWeight(searcher, needsScores);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,85 +26,22 @@ import java.io.IOException;
|
||||||
* An interface defining the collection of postings information from the leaves
|
* An interface defining the collection of postings information from the leaves
|
||||||
* of a {@link org.apache.lucene.search.spans.Spans}
|
* of a {@link org.apache.lucene.search.spans.Spans}
|
||||||
*
|
*
|
||||||
* Typical use would be as follows:
|
|
||||||
* <pre>
|
|
||||||
* while (spans.nextStartPosition() != NO_MORE_POSITIONS) {
|
|
||||||
* spanCollector.reset();
|
|
||||||
* spans.collect(spanCollector);
|
|
||||||
* doSomethingWith(spanCollector);
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
public interface SpanCollector {
|
public interface SpanCollector {
|
||||||
|
|
||||||
/**
|
|
||||||
* Called to indicate that the driving {@link org.apache.lucene.search.spans.Spans} has
|
|
||||||
* been moved to a new position
|
|
||||||
*/
|
|
||||||
public void reset();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an integer indicating what postings information should be retrieved
|
|
||||||
*
|
|
||||||
* See {@link org.apache.lucene.index.TermsEnum#postings(org.apache.lucene.util.Bits, org.apache.lucene.index.PostingsEnum, int)}
|
|
||||||
*
|
|
||||||
* @return the postings flag
|
|
||||||
*/
|
|
||||||
public int requiredPostings();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect information from postings
|
* Collect information from postings
|
||||||
* @param postings a {@link PostingsEnum}
|
* @param postings a {@link PostingsEnum}
|
||||||
|
* @param position the position of the PostingsEnum
|
||||||
* @param term the {@link Term} for this postings list
|
* @param term the {@link Term} for this postings list
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public void collectLeaf(PostingsEnum postings, Term term) throws IOException;
|
public void collectLeaf(PostingsEnum postings, int position, Term term) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a {@link BufferedSpanCollector} for use by eager spans implementations, such
|
* Call to indicate that the driving Spans has moved to a new position
|
||||||
* as {@link NearSpansOrdered}.
|
|
||||||
*
|
|
||||||
* @return a BufferedSpanCollector
|
|
||||||
*/
|
*/
|
||||||
public BufferedSpanCollector buffer();
|
public void reset();
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the SpanCollector used by the {@link org.apache.lucene.search.spans.BufferedSpanCollector}
|
|
||||||
* returned from {@link #buffer()}.
|
|
||||||
*/
|
|
||||||
public SpanCollector bufferedCollector();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A default No-op implementation of SpanCollector
|
|
||||||
*/
|
|
||||||
public static final SpanCollector NO_OP = new SpanCollector() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int requiredPostings() {
|
|
||||||
return PostingsEnum.POSITIONS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void collectLeaf(PostingsEnum postings, Term term) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BufferedSpanCollector buffer() {
|
|
||||||
return BufferedSpanCollector.NO_OP;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SpanCollector bufferedCollector() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +0,0 @@
|
||||||
package org.apache.lucene.search.spans;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface defining a factory for creating new {@link SpanCollector}s
|
|
||||||
*/
|
|
||||||
public interface SpanCollectorFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return a new SpanCollector
|
|
||||||
*/
|
|
||||||
SpanCollector newCollector();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory for creating NO_OP collectors
|
|
||||||
*/
|
|
||||||
public static final SpanCollectorFactory NO_OP_FACTORY = new SpanCollectorFactory() {
|
|
||||||
@Override
|
|
||||||
public SpanCollector newCollector() {
|
|
||||||
return SpanCollector.NO_OP;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
|
@ -55,9 +55,9 @@ abstract class SpanContainQuery extends SpanQuery implements Cloneable {
|
||||||
final SpanWeight bigWeight;
|
final SpanWeight bigWeight;
|
||||||
final SpanWeight littleWeight;
|
final SpanWeight littleWeight;
|
||||||
|
|
||||||
public SpanContainWeight(IndexSearcher searcher, Map<Term, TermContext> terms, SpanCollectorFactory factory,
|
public SpanContainWeight(IndexSearcher searcher, Map<Term, TermContext> terms,
|
||||||
SpanWeight bigWeight, SpanWeight littleWeight) throws IOException {
|
SpanWeight bigWeight, SpanWeight littleWeight) throws IOException {
|
||||||
super(SpanContainQuery.this, searcher, terms, factory);
|
super(SpanContainQuery.this, searcher, terms);
|
||||||
this.bigWeight = bigWeight;
|
this.bigWeight = bigWeight;
|
||||||
this.littleWeight = littleWeight;
|
this.littleWeight = littleWeight;
|
||||||
}
|
}
|
||||||
|
@ -71,12 +71,12 @@ abstract class SpanContainQuery extends SpanQuery implements Cloneable {
|
||||||
littleWeight.extractTerms(terms);
|
littleWeight.extractTerms(terms);
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Spans> prepareConjunction(final LeafReaderContext context, final Bits acceptDocs, SpanCollector collector) throws IOException {
|
ArrayList<Spans> prepareConjunction(final LeafReaderContext context, final Bits acceptDocs, Postings postings) throws IOException {
|
||||||
Spans bigSpans = bigWeight.getSpans(context, acceptDocs, collector);
|
Spans bigSpans = bigWeight.getSpans(context, acceptDocs, postings);
|
||||||
if (bigSpans == null) {
|
if (bigSpans == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Spans littleSpans = littleWeight.getSpans(context, acceptDocs, collector);
|
Spans littleSpans = littleWeight.getSpans(context, acceptDocs, postings);
|
||||||
if (littleSpans == null) {
|
if (littleSpans == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,18 +51,18 @@ public class SpanContainingQuery extends SpanContainQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
SpanWeight bigWeight = big.createWeight(searcher, false, factory);
|
SpanWeight bigWeight = big.createWeight(searcher, false);
|
||||||
SpanWeight littleWeight = little.createWeight(searcher, false, factory);
|
SpanWeight littleWeight = little.createWeight(searcher, false);
|
||||||
return new SpanContainingWeight(searcher, needsScores ? getTermContexts(bigWeight, littleWeight) : null,
|
return new SpanContainingWeight(searcher, needsScores ? getTermContexts(bigWeight, littleWeight) : null,
|
||||||
factory, bigWeight, littleWeight);
|
bigWeight, littleWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpanContainingWeight extends SpanContainWeight {
|
public class SpanContainingWeight extends SpanContainWeight {
|
||||||
|
|
||||||
public SpanContainingWeight(IndexSearcher searcher, Map<Term, TermContext> terms, SpanCollectorFactory factory,
|
public SpanContainingWeight(IndexSearcher searcher, Map<Term, TermContext> terms,
|
||||||
SpanWeight bigWeight, SpanWeight littleWeight) throws IOException {
|
SpanWeight bigWeight, SpanWeight littleWeight) throws IOException {
|
||||||
super(searcher, terms, factory, bigWeight, littleWeight);
|
super(searcher, terms, bigWeight, littleWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,8 +70,8 @@ public class SpanContainingQuery extends SpanContainQuery {
|
||||||
* The payload is from the spans of <code>big</code>.
|
* The payload is from the spans of <code>big</code>.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, SpanCollector collector) throws IOException {
|
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, Postings requiredPostings) throws IOException {
|
||||||
ArrayList<Spans> containerContained = prepareConjunction(context, acceptDocs, collector);
|
ArrayList<Spans> containerContained = prepareConjunction(context, acceptDocs, requiredPostings);
|
||||||
if (containerContained == null) {
|
if (containerContained == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,7 @@ public class SpanFirstQuery extends SpanPositionRangeQuery {
|
||||||
super(match, 0, end);
|
super(match, 0, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
protected AcceptStatus acceptPosition(Spans spans) throws IOException {
|
||||||
protected AcceptStatus acceptPosition(Spans spans, SpanCollector collector) throws IOException {
|
|
||||||
assert spans.startPosition() != spans.endPosition() : "start equals end: " + spans.startPosition();
|
assert spans.startPosition() != spans.endPosition() : "start equals end: " + spans.startPosition();
|
||||||
if (spans.startPosition() >= end)
|
if (spans.startPosition() >= end)
|
||||||
return AcceptStatus.NO_MORE_IN_CURRENT_DOC;
|
return AcceptStatus.NO_MORE_IN_CURRENT_DOC;
|
||||||
|
@ -47,7 +46,6 @@ public class SpanFirstQuery extends SpanPositionRangeQuery {
|
||||||
return AcceptStatus.NO;
|
return AcceptStatus.NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString(String field) {
|
public String toString(String field) {
|
||||||
StringBuilder buffer = new StringBuilder();
|
StringBuilder buffer = new StringBuilder();
|
||||||
|
|
|
@ -96,7 +96,7 @@ public class SpanMultiTermQueryWrapper<Q extends MultiTermQuery> extends SpanQue
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
throw new IllegalArgumentException("Rewrite first!");
|
throw new IllegalArgumentException("Rewrite first!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,20 +112,20 @@ public class SpanNearQuery extends SpanQuery implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
List<SpanWeight> subWeights = new ArrayList<>();
|
List<SpanWeight> subWeights = new ArrayList<>();
|
||||||
for (SpanQuery q : clauses) {
|
for (SpanQuery q : clauses) {
|
||||||
subWeights.add(q.createWeight(searcher, false, factory));
|
subWeights.add(q.createWeight(searcher, false));
|
||||||
}
|
}
|
||||||
return new SpanNearWeight(subWeights, searcher, needsScores ? getTermContexts(subWeights) : null, factory);
|
return new SpanNearWeight(subWeights, searcher, needsScores ? getTermContexts(subWeights) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpanNearWeight extends SpanWeight {
|
public class SpanNearWeight extends SpanWeight {
|
||||||
|
|
||||||
final List<SpanWeight> subWeights;
|
final List<SpanWeight> subWeights;
|
||||||
|
|
||||||
public SpanNearWeight(List<SpanWeight> subWeights, IndexSearcher searcher, Map<Term, TermContext> terms, SpanCollectorFactory factory) throws IOException {
|
public SpanNearWeight(List<SpanWeight> subWeights, IndexSearcher searcher, Map<Term, TermContext> terms) throws IOException {
|
||||||
super(SpanNearQuery.this, searcher, terms, factory);
|
super(SpanNearQuery.this, searcher, terms);
|
||||||
this.subWeights = subWeights;
|
this.subWeights = subWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ public class SpanNearQuery extends SpanQuery implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, SpanCollector collector) throws IOException {
|
public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, Postings requiredPostings) throws IOException {
|
||||||
|
|
||||||
Terms terms = context.reader().terms(field);
|
Terms terms = context.reader().terms(field);
|
||||||
if (terms == null) {
|
if (terms == null) {
|
||||||
|
@ -145,9 +145,8 @@ public class SpanNearQuery extends SpanQuery implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Spans> subSpans = new ArrayList<>(clauses.size());
|
ArrayList<Spans> subSpans = new ArrayList<>(clauses.size());
|
||||||
SpanCollector subSpanCollector = inOrder ? collector.bufferedCollector() : collector;
|
|
||||||
for (SpanWeight w : subWeights) {
|
for (SpanWeight w : subWeights) {
|
||||||
Spans subSpan = w.getSpans(context, acceptDocs, subSpanCollector);
|
Spans subSpan = w.getSpans(context, acceptDocs, requiredPostings);
|
||||||
if (subSpan != null) {
|
if (subSpan != null) {
|
||||||
subSpans.add(subSpan);
|
subSpans.add(subSpan);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,8 +17,8 @@ package org.apache.lucene.search.spans;
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.index.TermContext;
|
import org.apache.lucene.index.TermContext;
|
||||||
import org.apache.lucene.search.DocIdSetIterator;
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
|
@ -30,8 +30,8 @@ import org.apache.lucene.util.ToStringUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/** Removes matches which overlap with another SpanQuery or which are
|
/** Removes matches which overlap with another SpanQuery or which are
|
||||||
* within x tokens before or y tokens after another SpanQuery.
|
* within x tokens before or y tokens after another SpanQuery.
|
||||||
|
@ -103,11 +103,11 @@ public class SpanNotQuery extends SpanQuery implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
SpanWeight includeWeight = include.createWeight(searcher, false, factory);
|
SpanWeight includeWeight = include.createWeight(searcher, false);
|
||||||
SpanWeight excludeWeight = exclude.createWeight(searcher, false, factory);
|
SpanWeight excludeWeight = exclude.createWeight(searcher, false);
|
||||||
return new SpanNotWeight(searcher, needsScores ? getTermContexts(includeWeight, excludeWeight) : null,
|
return new SpanNotWeight(searcher, needsScores ? getTermContexts(includeWeight, excludeWeight) : null,
|
||||||
factory, includeWeight, excludeWeight);
|
includeWeight, excludeWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpanNotWeight extends SpanWeight {
|
public class SpanNotWeight extends SpanWeight {
|
||||||
|
@ -115,9 +115,9 @@ public class SpanNotQuery extends SpanQuery implements Cloneable {
|
||||||
final SpanWeight includeWeight;
|
final SpanWeight includeWeight;
|
||||||
final SpanWeight excludeWeight;
|
final SpanWeight excludeWeight;
|
||||||
|
|
||||||
public SpanNotWeight(IndexSearcher searcher, Map<Term, TermContext> terms, SpanCollectorFactory factory,
|
public SpanNotWeight(IndexSearcher searcher, Map<Term, TermContext> terms,
|
||||||
SpanWeight includeWeight, SpanWeight excludeWeight) throws IOException {
|
SpanWeight includeWeight, SpanWeight excludeWeight) throws IOException {
|
||||||
super(SpanNotQuery.this, searcher, terms, factory);
|
super(SpanNotQuery.this, searcher, terms);
|
||||||
this.includeWeight = includeWeight;
|
this.includeWeight = includeWeight;
|
||||||
this.excludeWeight = excludeWeight;
|
this.excludeWeight = excludeWeight;
|
||||||
}
|
}
|
||||||
|
@ -128,13 +128,13 @@ public class SpanNotQuery extends SpanQuery implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, SpanCollector collector) throws IOException {
|
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, Postings requiredPostings) throws IOException {
|
||||||
Spans includeSpans = includeWeight.getSpans(context, acceptDocs, collector);
|
Spans includeSpans = includeWeight.getSpans(context, acceptDocs, requiredPostings);
|
||||||
if (includeSpans == null) {
|
if (includeSpans == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Spans excludeSpans = excludeWeight.getSpans(context, acceptDocs, collector);
|
Spans excludeSpans = excludeWeight.getSpans(context, acceptDocs, requiredPostings);
|
||||||
if (excludeSpans == null) {
|
if (excludeSpans == null) {
|
||||||
return includeSpans;
|
return includeSpans;
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,20 +138,20 @@ public class SpanOrQuery extends SpanQuery implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
List<SpanWeight> subWeights = new ArrayList<>(clauses.size());
|
List<SpanWeight> subWeights = new ArrayList<>(clauses.size());
|
||||||
for (SpanQuery q : clauses) {
|
for (SpanQuery q : clauses) {
|
||||||
subWeights.add(q.createWeight(searcher, false, factory));
|
subWeights.add(q.createWeight(searcher, false));
|
||||||
}
|
}
|
||||||
return new SpanOrWeight(searcher, needsScores ? getTermContexts(subWeights) : null, factory, subWeights);
|
return new SpanOrWeight(searcher, needsScores ? getTermContexts(subWeights) : null, subWeights);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpanOrWeight extends SpanWeight {
|
public class SpanOrWeight extends SpanWeight {
|
||||||
|
|
||||||
final List<SpanWeight> subWeights;
|
final List<SpanWeight> subWeights;
|
||||||
|
|
||||||
public SpanOrWeight(IndexSearcher searcher, Map<Term, TermContext> terms, SpanCollectorFactory factory, List<SpanWeight> subWeights) throws IOException {
|
public SpanOrWeight(IndexSearcher searcher, Map<Term, TermContext> terms, List<SpanWeight> subWeights) throws IOException {
|
||||||
super(SpanOrQuery.this, searcher, terms, factory);
|
super(SpanOrQuery.this, searcher, terms);
|
||||||
this.subWeights = subWeights;
|
this.subWeights = subWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,13 +170,13 @@ public class SpanOrQuery extends SpanQuery implements Cloneable {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, SpanCollector collector)
|
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, Postings requiredPostings)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
ArrayList<Spans> subSpans = new ArrayList<>(clauses.size());
|
ArrayList<Spans> subSpans = new ArrayList<>(clauses.size());
|
||||||
|
|
||||||
for (SpanWeight w : subWeights) {
|
for (SpanWeight w : subWeights) {
|
||||||
Spans spans = w.getSpans(context, acceptDocs, collector);
|
Spans spans = w.getSpans(context, acceptDocs, requiredPostings);
|
||||||
if (spans != null) {
|
if (spans != null) {
|
||||||
subSpans.add(spans);
|
subSpans.add(spans);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ package org.apache.lucene.search.spans;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.index.TermContext;
|
import org.apache.lucene.index.TermContext;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
@ -28,8 +28,8 @@ import org.apache.lucene.util.Bits;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,28 +59,26 @@ public abstract class SpanPositionCheckQuery extends SpanQuery implements Clonea
|
||||||
* match indicated a valid start position.
|
* match indicated a valid start position.
|
||||||
*
|
*
|
||||||
* @param spans The {@link Spans} instance, positioned at the spot to check
|
* @param spans The {@link Spans} instance, positioned at the spot to check
|
||||||
* @param collector the {@link SpanCollector} associated with the Spans
|
|
||||||
*
|
*
|
||||||
* @return whether the match is accepted, rejected, or rejected and should move to the next doc.
|
* @return whether the match is accepted, rejected, or rejected and should move to the next doc.
|
||||||
*
|
*
|
||||||
* @see Spans#nextDoc()
|
* @see Spans#nextDoc()
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
protected abstract AcceptStatus acceptPosition(Spans spans, SpanCollector collector) throws IOException;
|
protected abstract AcceptStatus acceptPosition(Spans spans) throws IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
SpanWeight matchWeight = match.createWeight(searcher, false, factory);
|
SpanWeight matchWeight = match.createWeight(searcher, false);
|
||||||
return new SpanPositionCheckWeight(matchWeight, searcher, needsScores ? getTermContexts(matchWeight) : null, factory);
|
return new SpanPositionCheckWeight(matchWeight, searcher, needsScores ? getTermContexts(matchWeight) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpanPositionCheckWeight extends SpanWeight {
|
public class SpanPositionCheckWeight extends SpanWeight {
|
||||||
|
|
||||||
final SpanWeight matchWeight;
|
final SpanWeight matchWeight;
|
||||||
|
|
||||||
public SpanPositionCheckWeight(SpanWeight matchWeight, IndexSearcher searcher, Map<Term, TermContext> terms,
|
public SpanPositionCheckWeight(SpanWeight matchWeight, IndexSearcher searcher, Map<Term, TermContext> terms) throws IOException {
|
||||||
SpanCollectorFactory collectorFactory) throws IOException {
|
super(SpanPositionCheckQuery.this, searcher, terms);
|
||||||
super(SpanPositionCheckQuery.this, searcher, terms, collectorFactory);
|
|
||||||
this.matchWeight = matchWeight;
|
this.matchWeight = matchWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,12 +93,12 @@ public abstract class SpanPositionCheckQuery extends SpanQuery implements Clonea
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, SpanCollector collector) throws IOException {
|
public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, Postings requiredPostings) throws IOException {
|
||||||
Spans matchSpans = matchWeight.getSpans(context, acceptDocs, collector);
|
Spans matchSpans = matchWeight.getSpans(context, acceptDocs, requiredPostings);
|
||||||
return (matchSpans == null) ? null : new FilterSpans(matchSpans) {
|
return (matchSpans == null) ? null : new FilterSpans(matchSpans) {
|
||||||
@Override
|
@Override
|
||||||
protected AcceptStatus accept(Spans candidate) throws IOException {
|
protected AcceptStatus accept(Spans candidate) throws IOException {
|
||||||
return acceptPosition(candidate, collector);
|
return acceptPosition(candidate);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,18 +38,16 @@ public class SpanPositionRangeQuery extends SpanPositionCheckQuery {
|
||||||
this.end = end;
|
this.end = end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected AcceptStatus acceptPosition(Spans spans, SpanCollector collector) throws IOException {
|
protected AcceptStatus acceptPosition(Spans spans) throws IOException {
|
||||||
assert spans.startPosition() != spans.endPosition();
|
assert spans.startPosition() != spans.endPosition();
|
||||||
AcceptStatus res = (spans.startPosition() >= end)
|
AcceptStatus res = (spans.startPosition() >= end)
|
||||||
? AcceptStatus.NO_MORE_IN_CURRENT_DOC
|
? AcceptStatus.NO_MORE_IN_CURRENT_DOC
|
||||||
: (spans.startPosition() >= start && spans.endPosition() <= end)
|
: (spans.startPosition() >= start && spans.endPosition() <= end)
|
||||||
? AcceptStatus.YES : AcceptStatus.NO;
|
? AcceptStatus.YES : AcceptStatus.NO;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The minimum position permitted in a match
|
* @return The minimum position permitted in a match
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.index.TermContext;
|
import org.apache.lucene.index.TermContext;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
import org.apache.lucene.search.Weight;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
@ -36,21 +35,8 @@ public abstract class SpanQuery extends Query {
|
||||||
*/
|
*/
|
||||||
public abstract String getField();
|
public abstract String getField();
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a SpanWeight for this query
|
|
||||||
* @param searcher the IndexSearcher to be searched across
|
|
||||||
* @param needsScores if the query needs scores
|
|
||||||
* @param collectorFactory a SpanCollectorFactory to use in collecting postings data
|
|
||||||
* @return a SpanWeight
|
|
||||||
* @throws IOException on error
|
|
||||||
*/
|
|
||||||
public abstract SpanWeight createWeight(IndexSearcher searcher, boolean needsScores,
|
|
||||||
SpanCollectorFactory collectorFactory) throws IOException;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
public abstract SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException;
|
||||||
return createWeight(searcher, needsScores, SpanCollectorFactory.NO_OP_FACTORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a map of terms to termcontexts, for use in constructing SpanWeights
|
* Build a map of terms to termcontexts, for use in constructing SpanWeights
|
||||||
|
|
|
@ -40,7 +40,11 @@ public class SpanScorer extends Scorer {
|
||||||
|
|
||||||
private int lastScoredDoc = -1; // last doc we called setFreqCurrentDoc() for
|
private int lastScoredDoc = -1; // last doc we called setFreqCurrentDoc() for
|
||||||
|
|
||||||
protected SpanScorer(Spans spans, SpanWeight weight, Similarity.SimScorer docScorer) throws IOException {
|
/**
|
||||||
|
* Creates a new SpanScorer
|
||||||
|
* @lucene.internal
|
||||||
|
*/
|
||||||
|
public SpanScorer(Spans spans, SpanWeight weight, Similarity.SimScorer docScorer) throws IOException {
|
||||||
super(weight);
|
super(weight);
|
||||||
this.docScorer = docScorer;
|
this.docScorer = docScorer;
|
||||||
this.spans = Objects.requireNonNull(spans);
|
this.spans = Objects.requireNonNull(spans);
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class SpanTermQuery extends SpanQuery {
|
||||||
public String getField() { return term.field(); }
|
public String getField() { return term.field(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
final TermContext context;
|
final TermContext context;
|
||||||
final IndexReaderContext topContext = searcher.getTopReaderContext();
|
final IndexReaderContext topContext = searcher.getTopReaderContext();
|
||||||
if (termContext == null || termContext.topReaderContext != topContext) {
|
if (termContext == null || termContext.topReaderContext != topContext) {
|
||||||
|
@ -75,15 +75,15 @@ public class SpanTermQuery extends SpanQuery {
|
||||||
else {
|
else {
|
||||||
context = termContext;
|
context = termContext;
|
||||||
}
|
}
|
||||||
return new SpanTermWeight(context, searcher, needsScores ? Collections.singletonMap(term, context) : null, factory);
|
return new SpanTermWeight(context, searcher, needsScores ? Collections.singletonMap(term, context) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpanTermWeight extends SpanWeight {
|
public class SpanTermWeight extends SpanWeight {
|
||||||
|
|
||||||
final TermContext termContext;
|
final TermContext termContext;
|
||||||
|
|
||||||
public SpanTermWeight(TermContext termContext, IndexSearcher searcher, Map<Term, TermContext> terms, SpanCollectorFactory factory) throws IOException {
|
public SpanTermWeight(TermContext termContext, IndexSearcher searcher, Map<Term, TermContext> terms) throws IOException {
|
||||||
super(SpanTermQuery.this, searcher, terms, factory);
|
super(SpanTermQuery.this, searcher, terms);
|
||||||
this.termContext = termContext;
|
this.termContext = termContext;
|
||||||
assert termContext != null : "TermContext must not be null";
|
assert termContext != null : "TermContext must not be null";
|
||||||
}
|
}
|
||||||
|
@ -99,7 +99,7 @@ public class SpanTermQuery extends SpanQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, SpanCollector collector) throws IOException {
|
public Spans getSpans(final LeafReaderContext context, Bits acceptDocs, Postings requiredPostings) throws IOException {
|
||||||
|
|
||||||
assert termContext.topReaderContext == ReaderUtil.getTopLevelContext(context) : "The top-reader used to create Weight (" + termContext.topReaderContext + ") is not the same as the current reader's top-reader (" + ReaderUtil.getTopLevelContext(context);
|
assert termContext.topReaderContext == ReaderUtil.getTopLevelContext(context) : "The top-reader used to create Weight (" + termContext.topReaderContext + ") is not the same as the current reader's top-reader (" + ReaderUtil.getTopLevelContext(context);
|
||||||
|
|
||||||
|
@ -118,7 +118,7 @@ public class SpanTermQuery extends SpanQuery {
|
||||||
final TermsEnum termsEnum = terms.iterator();
|
final TermsEnum termsEnum = terms.iterator();
|
||||||
termsEnum.seekExact(term.bytes(), state);
|
termsEnum.seekExact(term.bytes(), state);
|
||||||
|
|
||||||
final PostingsEnum postings = termsEnum.postings(acceptDocs, null, collector.requiredPostings());
|
final PostingsEnum postings = termsEnum.postings(acceptDocs, null, requiredPostings.getRequiredPostings());
|
||||||
return new TermSpans(postings, term);
|
return new TermSpans(postings, term);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.apache.lucene.search.spans;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
import org.apache.lucene.index.PostingsEnum;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.index.TermContext;
|
import org.apache.lucene.index.TermContext;
|
||||||
import org.apache.lucene.index.Terms;
|
import org.apache.lucene.index.Terms;
|
||||||
|
@ -39,9 +40,41 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public abstract class SpanWeight extends Weight {
|
public abstract class SpanWeight extends Weight {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumeration defining what postings information should be retrieved from the
|
||||||
|
* index for a given Spans
|
||||||
|
*/
|
||||||
|
public enum Postings {
|
||||||
|
POSITIONS {
|
||||||
|
@Override
|
||||||
|
public int getRequiredPostings() {
|
||||||
|
return PostingsEnum.POSITIONS;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
PAYLOADS {
|
||||||
|
@Override
|
||||||
|
public int getRequiredPostings() {
|
||||||
|
return PostingsEnum.PAYLOADS;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OFFSETS {
|
||||||
|
@Override
|
||||||
|
public int getRequiredPostings() {
|
||||||
|
return PostingsEnum.PAYLOADS | PostingsEnum.OFFSETS;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public abstract int getRequiredPostings();
|
||||||
|
|
||||||
|
public Postings atLeast(Postings postings) {
|
||||||
|
if (postings.compareTo(this) > 0)
|
||||||
|
return postings;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected final Similarity similarity;
|
protected final Similarity similarity;
|
||||||
protected final Similarity.SimWeight simWeight;
|
protected final Similarity.SimWeight simWeight;
|
||||||
protected final SpanCollectorFactory collectorFactory;
|
|
||||||
protected final String field;
|
protected final String field;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,14 +83,12 @@ public abstract class SpanWeight extends Weight {
|
||||||
* @param searcher the IndexSearcher to query against
|
* @param searcher the IndexSearcher to query against
|
||||||
* @param termContexts a map of terms to termcontexts for use in building the similarity. May
|
* @param termContexts a map of terms to termcontexts for use in building the similarity. May
|
||||||
* be null if scores are not required
|
* be null if scores are not required
|
||||||
* @param collectorFactory a SpanCollectorFactory to be used for Span collection
|
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public SpanWeight(SpanQuery query, IndexSearcher searcher, Map<Term, TermContext> termContexts, SpanCollectorFactory collectorFactory) throws IOException {
|
public SpanWeight(SpanQuery query, IndexSearcher searcher, Map<Term, TermContext> termContexts) throws IOException {
|
||||||
super(query);
|
super(query);
|
||||||
this.field = query.getField();
|
this.field = query.getField();
|
||||||
this.similarity = searcher.getSimilarity(termContexts != null);
|
this.similarity = searcher.getSimilarity(termContexts != null);
|
||||||
this.collectorFactory = collectorFactory;
|
|
||||||
this.simWeight = buildSimWeight(query, searcher, termContexts);
|
this.simWeight = buildSimWeight(query, searcher, termContexts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,23 +115,10 @@ public abstract class SpanWeight extends Weight {
|
||||||
* Expert: Return a Spans object iterating over matches from this Weight
|
* Expert: Return a Spans object iterating over matches from this Weight
|
||||||
* @param ctx a LeafReaderContext for this Spans
|
* @param ctx a LeafReaderContext for this Spans
|
||||||
* @param acceptDocs a bitset of documents to check
|
* @param acceptDocs a bitset of documents to check
|
||||||
* @param collector a SpanCollector to use for postings data collection
|
|
||||||
* @return a Spans
|
* @return a Spans
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public abstract Spans getSpans(LeafReaderContext ctx, Bits acceptDocs, SpanCollector collector) throws IOException;
|
public abstract Spans getSpans(LeafReaderContext ctx, Bits acceptDocs, Postings requiredPostings) throws IOException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Expert: Return a Spans object iterating over matches from this Weight, without
|
|
||||||
* collecting any postings data.
|
|
||||||
* @param ctx a LeafReaderContext for this Spans
|
|
||||||
* @param acceptDocs a bitset of documents to check
|
|
||||||
* @return a Spans
|
|
||||||
* @throws IOException on error
|
|
||||||
*/
|
|
||||||
public final Spans getSpans(LeafReaderContext ctx, Bits acceptDocs) throws IOException {
|
|
||||||
return getSpans(ctx, acceptDocs, collectorFactory.newCollector());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getValueForNormalization() throws IOException {
|
public float getValueForNormalization() throws IOException {
|
||||||
|
@ -123,7 +141,7 @@ public abstract class SpanWeight extends Weight {
|
||||||
if (terms != null && terms.hasPositions() == false) {
|
if (terms != null && terms.hasPositions() == false) {
|
||||||
throw new IllegalStateException("field \"" + field + "\" was indexed without position data; cannot run SpanQuery (query=" + parentQuery + ")");
|
throw new IllegalStateException("field \"" + field + "\" was indexed without position data; cannot run SpanQuery (query=" + parentQuery + ")");
|
||||||
}
|
}
|
||||||
Spans spans = getSpans(context, acceptDocs, collectorFactory.newCollector());
|
Spans spans = getSpans(context, acceptDocs, Postings.POSITIONS);
|
||||||
Similarity.SimScorer simScorer = simWeight == null ? null : similarity.simScorer(simWeight, context);
|
Similarity.SimScorer simScorer = simWeight == null ? null : similarity.simScorer(simWeight, context);
|
||||||
return (spans == null) ? null : new SpanScorer(spans, this, simScorer);
|
return (spans == null) ? null : new SpanScorer(spans, this, simScorer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,18 +52,18 @@ public class SpanWithinQuery extends SpanContainQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
SpanWeight bigWeight = big.createWeight(searcher, false, factory);
|
SpanWeight bigWeight = big.createWeight(searcher, false);
|
||||||
SpanWeight littleWeight = little.createWeight(searcher, false, factory);
|
SpanWeight littleWeight = little.createWeight(searcher, false);
|
||||||
return new SpanWithinWeight(searcher, needsScores ? getTermContexts(bigWeight, littleWeight) : null,
|
return new SpanWithinWeight(searcher, needsScores ? getTermContexts(bigWeight, littleWeight) : null,
|
||||||
factory, bigWeight, littleWeight);
|
bigWeight, littleWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpanWithinWeight extends SpanContainWeight {
|
public class SpanWithinWeight extends SpanContainWeight {
|
||||||
|
|
||||||
public SpanWithinWeight(IndexSearcher searcher, Map<Term, TermContext> terms, SpanCollectorFactory factory,
|
public SpanWithinWeight(IndexSearcher searcher, Map<Term, TermContext> terms,
|
||||||
SpanWeight bigWeight, SpanWeight littleWeight) throws IOException {
|
SpanWeight bigWeight, SpanWeight littleWeight) throws IOException {
|
||||||
super(searcher, terms, factory, bigWeight, littleWeight);
|
super(searcher, terms, bigWeight, littleWeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,8 +71,8 @@ public class SpanWithinQuery extends SpanContainQuery {
|
||||||
* The payload is from the spans of <code>little</code>.
|
* The payload is from the spans of <code>little</code>.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, SpanCollector collector) throws IOException {
|
public Spans getSpans(final LeafReaderContext context, final Bits acceptDocs, Postings requiredPostings) throws IOException {
|
||||||
ArrayList<Spans> containerContained = prepareConjunction(context, acceptDocs, collector);
|
ArrayList<Spans> containerContained = prepareConjunction(context, acceptDocs, requiredPostings);
|
||||||
if (containerContained == null) {
|
if (containerContained == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,11 @@ public abstract class Spans extends DocIdSetIterator {
|
||||||
public abstract int endPosition();
|
public abstract int endPosition();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Collect data from the current Spans
|
* Collect postings data from the leaves of the current Spans.
|
||||||
|
*
|
||||||
|
* This method should only be called after {@link #nextStartPosition()}, and before
|
||||||
|
* {@link #NO_MORE_POSITIONS} has been reached.
|
||||||
|
*
|
||||||
* @param collector a SpanCollector
|
* @param collector a SpanCollector
|
||||||
*
|
*
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
|
|
|
@ -106,30 +106,9 @@ public class TermSpans extends Spans {
|
||||||
return postings.cost();
|
return postings.cost();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
@Override
|
|
||||||
public Collection<byte[]> getPayload() throws IOException {
|
|
||||||
final BytesRef payload = postings.getPayload();
|
|
||||||
readPayload = true;
|
|
||||||
final byte[] bytes;
|
|
||||||
if (payload != null) {
|
|
||||||
bytes = new byte[payload.length];
|
|
||||||
System.arraycopy(payload.bytes, payload.offset, bytes, 0, payload.length);
|
|
||||||
} else {
|
|
||||||
bytes = null;
|
|
||||||
}
|
|
||||||
return Collections.singletonList(bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPayloadAvailable() throws IOException {
|
|
||||||
return readPayload == false && postings.getPayload() != null;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void collect(SpanCollector collector) throws IOException {
|
public void collect(SpanCollector collector) throws IOException {
|
||||||
collector.collectLeaf(postings, term);
|
collector.collectLeaf(postings, position, term);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.apache.lucene.search.spans.MultiSpansWrapper;
|
||||||
import org.apache.lucene.search.spans.SpanNearQuery;
|
import org.apache.lucene.search.spans.SpanNearQuery;
|
||||||
import org.apache.lucene.search.spans.SpanQuery;
|
import org.apache.lucene.search.spans.SpanQuery;
|
||||||
import org.apache.lucene.search.spans.SpanTermQuery;
|
import org.apache.lucene.search.spans.SpanTermQuery;
|
||||||
|
import org.apache.lucene.search.spans.SpanWeight;
|
||||||
import org.apache.lucene.search.spans.Spans;
|
import org.apache.lucene.search.spans.Spans;
|
||||||
import org.apache.lucene.store.Directory;
|
import org.apache.lucene.store.Directory;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
|
@ -242,7 +243,7 @@ public class TestPositionIncrement extends LuceneTestCase {
|
||||||
System.out.println("\ngetPayloadSpans test");
|
System.out.println("\ngetPayloadSpans test");
|
||||||
}
|
}
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
Spans pspans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, collector);
|
Spans pspans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, SpanWeight.Postings.PAYLOADS);
|
||||||
while (pspans.nextDoc() != Spans.NO_MORE_DOCS) {
|
while (pspans.nextDoc() != Spans.NO_MORE_DOCS) {
|
||||||
while (pspans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
|
while (pspans.nextStartPosition() != Spans.NO_MORE_POSITIONS) {
|
||||||
if (VERBOSE) {
|
if (VERBOSE) {
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.apache.lucene.document.Document;
|
||||||
import org.apache.lucene.document.Field;
|
import org.apache.lucene.document.Field;
|
||||||
import org.apache.lucene.document.TextField;
|
import org.apache.lucene.document.TextField;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.PostingsEnum;
|
||||||
import org.apache.lucene.index.RandomIndexWriter;
|
import org.apache.lucene.index.RandomIndexWriter;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
@ -41,6 +42,7 @@ import org.apache.lucene.search.spans.SpanNearQuery;
|
||||||
import org.apache.lucene.search.spans.SpanNotQuery;
|
import org.apache.lucene.search.spans.SpanNotQuery;
|
||||||
import org.apache.lucene.search.spans.SpanQuery;
|
import org.apache.lucene.search.spans.SpanQuery;
|
||||||
import org.apache.lucene.search.spans.SpanTermQuery;
|
import org.apache.lucene.search.spans.SpanTermQuery;
|
||||||
|
import org.apache.lucene.search.spans.SpanWeight;
|
||||||
import org.apache.lucene.search.spans.Spans;
|
import org.apache.lucene.search.spans.Spans;
|
||||||
import org.apache.lucene.store.Directory;
|
import org.apache.lucene.store.Directory;
|
||||||
import org.apache.lucene.util.BytesRef;
|
import org.apache.lucene.util.BytesRef;
|
||||||
|
@ -73,12 +75,12 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
Spans spans;
|
Spans spans;
|
||||||
stq = new SpanTermQuery(new Term(PayloadHelper.FIELD, "seventy"));
|
stq = new SpanTermQuery(new Term(PayloadHelper.FIELD, "seventy"));
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
spans = MultiSpansWrapper.wrap(indexReader, stq, collector);
|
spans = MultiSpansWrapper.wrap(indexReader, stq, SpanWeight.Postings.PAYLOADS);
|
||||||
assertTrue("spans is null and it shouldn't be", spans != null);
|
assertTrue("spans is null and it shouldn't be", spans != null);
|
||||||
checkSpans(spans, collector, 100, 1, 1, 1);
|
checkSpans(spans, collector, 100, 1, 1, 1);
|
||||||
|
|
||||||
stq = new SpanTermQuery(new Term(PayloadHelper.NO_PAYLOAD_FIELD, "seventy"));
|
stq = new SpanTermQuery(new Term(PayloadHelper.NO_PAYLOAD_FIELD, "seventy"));
|
||||||
spans = MultiSpansWrapper.wrap(indexReader, stq, collector);
|
spans = MultiSpansWrapper.wrap(indexReader, stq, SpanWeight.Postings.PAYLOADS);
|
||||||
assertTrue("spans is null and it shouldn't be", spans != null);
|
assertTrue("spans is null and it shouldn't be", spans != null);
|
||||||
checkSpans(spans, collector, 100, 0, 0, 0);
|
checkSpans(spans, collector, 100, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -90,7 +92,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
match = new SpanTermQuery(new Term(PayloadHelper.FIELD, "one"));
|
match = new SpanTermQuery(new Term(PayloadHelper.FIELD, "one"));
|
||||||
sfq = new SpanFirstQuery(match, 2);
|
sfq = new SpanFirstQuery(match, 2);
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
Spans spans = MultiSpansWrapper.wrap(indexReader, sfq, collector);
|
Spans spans = MultiSpansWrapper.wrap(indexReader, sfq, SpanWeight.Postings.PAYLOADS);
|
||||||
checkSpans(spans, collector, 109, 1, 1, 1);
|
checkSpans(spans, collector, 109, 1, 1, 1);
|
||||||
//Test more complicated subclause
|
//Test more complicated subclause
|
||||||
SpanQuery[] clauses = new SpanQuery[2];
|
SpanQuery[] clauses = new SpanQuery[2];
|
||||||
|
@ -98,11 +100,11 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "hundred"));
|
clauses[1] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "hundred"));
|
||||||
match = new SpanNearQuery(clauses, 0, true);
|
match = new SpanNearQuery(clauses, 0, true);
|
||||||
sfq = new SpanFirstQuery(match, 2);
|
sfq = new SpanFirstQuery(match, 2);
|
||||||
checkSpans(MultiSpansWrapper.wrap(indexReader, sfq, collector), collector, 100, 2, 1, 1);
|
checkSpans(MultiSpansWrapper.wrap(indexReader, sfq, SpanWeight.Postings.PAYLOADS), collector, 100, 2, 1, 1);
|
||||||
|
|
||||||
match = new SpanNearQuery(clauses, 0, false);
|
match = new SpanNearQuery(clauses, 0, false);
|
||||||
sfq = new SpanFirstQuery(match, 2);
|
sfq = new SpanFirstQuery(match, 2);
|
||||||
checkSpans(MultiSpansWrapper.wrap(indexReader, sfq, collector), collector, 100, 2, 1, 1);
|
checkSpans(MultiSpansWrapper.wrap(indexReader, sfq, SpanWeight.Postings.PAYLOADS), collector, 100, 2, 1, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +128,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
writer.close();
|
writer.close();
|
||||||
|
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
checkSpans(MultiSpansWrapper.wrap(reader, snq, collector), collector, 1, new int[]{2});
|
checkSpans(MultiSpansWrapper.wrap(reader, snq, SpanWeight.Postings.PAYLOADS), collector, 1, new int[]{2});
|
||||||
reader.close();
|
reader.close();
|
||||||
directory.close();
|
directory.close();
|
||||||
}
|
}
|
||||||
|
@ -138,7 +140,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
|
|
||||||
stq = new SpanTermQuery(new Term(PayloadHelper.FIELD, "mark"));
|
stq = new SpanTermQuery(new Term(PayloadHelper.FIELD, "mark"));
|
||||||
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), stq, collector);
|
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), stq, SpanWeight.Postings.PAYLOADS);
|
||||||
assertNull(spans);
|
assertNull(spans);
|
||||||
|
|
||||||
SpanQuery[] clauses = new SpanQuery[3];
|
SpanQuery[] clauses = new SpanQuery[3];
|
||||||
|
@ -147,7 +149,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
clauses[2] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "xx"));
|
clauses[2] = new SpanTermQuery(new Term(PayloadHelper.FIELD, "xx"));
|
||||||
SpanNearQuery spanNearQuery = new SpanNearQuery(clauses, 12, false);
|
SpanNearQuery spanNearQuery = new SpanNearQuery(clauses, 12, false);
|
||||||
|
|
||||||
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), spanNearQuery, collector);
|
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), spanNearQuery, SpanWeight.Postings.PAYLOADS);
|
||||||
assertTrue("spans is null and it shouldn't be", spans != null);
|
assertTrue("spans is null and it shouldn't be", spans != null);
|
||||||
checkSpans(spans, collector, 2, new int[]{3,3});
|
checkSpans(spans, collector, 2, new int[]{3,3});
|
||||||
|
|
||||||
|
@ -158,7 +160,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
|
|
||||||
spanNearQuery = new SpanNearQuery(clauses, 6, true);
|
spanNearQuery = new SpanNearQuery(clauses, 6, true);
|
||||||
|
|
||||||
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), spanNearQuery, collector);
|
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), spanNearQuery, SpanWeight.Postings.PAYLOADS);
|
||||||
|
|
||||||
assertTrue("spans is null and it shouldn't be", spans != null);
|
assertTrue("spans is null and it shouldn't be", spans != null);
|
||||||
checkSpans(spans, collector, 1, new int[]{3});
|
checkSpans(spans, collector, 1, new int[]{3});
|
||||||
|
@ -180,7 +182,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses2, 6, false);
|
SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses2, 6, false);
|
||||||
|
|
||||||
// yy within 6 of xx within 6 of rr
|
// yy within 6 of xx within 6 of rr
|
||||||
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), nestedSpanNearQuery, collector);
|
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), nestedSpanNearQuery, SpanWeight.Postings.PAYLOADS);
|
||||||
assertTrue("spans is null and it shouldn't be", spans != null);
|
assertTrue("spans is null and it shouldn't be", spans != null);
|
||||||
checkSpans(spans, collector, 2, new int[]{3,3});
|
checkSpans(spans, collector, 2, new int[]{3,3});
|
||||||
closeIndexReader.close();
|
closeIndexReader.close();
|
||||||
|
@ -212,7 +214,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
|
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses3, 6, false);
|
SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses3, 6, false);
|
||||||
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), nestedSpanNearQuery, collector);
|
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), nestedSpanNearQuery, SpanWeight.Postings.PAYLOADS);
|
||||||
|
|
||||||
assertTrue("spans is null and it shouldn't be", spans != null);
|
assertTrue("spans is null and it shouldn't be", spans != null);
|
||||||
checkSpans(spans, collector, 1, new int[]{3});
|
checkSpans(spans, collector, 1, new int[]{3});
|
||||||
|
@ -251,7 +253,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses3, 6, false);
|
SpanNearQuery nestedSpanNearQuery = new SpanNearQuery(clauses3, 6, false);
|
||||||
|
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), nestedSpanNearQuery, collector);
|
spans = MultiSpansWrapper.wrap(searcher.getIndexReader(), nestedSpanNearQuery, SpanWeight.Postings.PAYLOADS);
|
||||||
assertTrue("spans is null and it shouldn't be", spans != null);
|
assertTrue("spans is null and it shouldn't be", spans != null);
|
||||||
checkSpans(spans, collector, 2, new int[]{8, 8});
|
checkSpans(spans, collector, 2, new int[]{8, 8});
|
||||||
closeIndexReader.close();
|
closeIndexReader.close();
|
||||||
|
@ -276,7 +278,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
SpanQuery[] sqs = { stq1, stq2 };
|
SpanQuery[] sqs = { stq1, stq2 };
|
||||||
SpanNearQuery snq = new SpanNearQuery(sqs, 1, true);
|
SpanNearQuery snq = new SpanNearQuery(sqs, 1, true);
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
Spans spans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, collector);
|
Spans spans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, SpanWeight.Postings.PAYLOADS);
|
||||||
|
|
||||||
TopDocs topDocs = is.search(snq, 1);
|
TopDocs topDocs = is.search(snq, 1);
|
||||||
Set<String> payloadSet = new HashSet<>();
|
Set<String> payloadSet = new HashSet<>();
|
||||||
|
@ -316,7 +318,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
SpanQuery[] sqs = { stq1, stq2 };
|
SpanQuery[] sqs = { stq1, stq2 };
|
||||||
SpanNearQuery snq = new SpanNearQuery(sqs, 0, true);
|
SpanNearQuery snq = new SpanNearQuery(sqs, 0, true);
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
Spans spans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, collector);
|
Spans spans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, SpanWeight.Postings.PAYLOADS);
|
||||||
|
|
||||||
TopDocs topDocs = is.search(snq, 1);
|
TopDocs topDocs = is.search(snq, 1);
|
||||||
Set<String> payloadSet = new HashSet<>();
|
Set<String> payloadSet = new HashSet<>();
|
||||||
|
@ -357,7 +359,7 @@ public class TestPayloadSpans extends LuceneTestCase {
|
||||||
SpanQuery[] sqs = { stq1, stq2 };
|
SpanQuery[] sqs = { stq1, stq2 };
|
||||||
SpanNearQuery snq = new SpanNearQuery(sqs, 0, true);
|
SpanNearQuery snq = new SpanNearQuery(sqs, 0, true);
|
||||||
PayloadSpanCollector collector = new PayloadSpanCollector();
|
PayloadSpanCollector collector = new PayloadSpanCollector();
|
||||||
Spans spans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, collector);
|
Spans spans = MultiSpansWrapper.wrap(is.getIndexReader(), snq, SpanWeight.Postings.PAYLOADS);
|
||||||
|
|
||||||
TopDocs topDocs = is.search(snq, 1);
|
TopDocs topDocs = is.search(snq, 1);
|
||||||
Set<String> payloadSet = new HashSet<>();
|
Set<String> payloadSet = new HashSet<>();
|
||||||
|
|
|
@ -84,7 +84,7 @@ final class JustCompileSearchSpans {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
|
throw new UnsupportedOperationException(UNSUPPORTED_MSG);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,18 +36,18 @@ import java.io.IOException;
|
||||||
public class MultiSpansWrapper {
|
public class MultiSpansWrapper {
|
||||||
|
|
||||||
public static Spans wrap(IndexReader reader, SpanQuery spanQuery) throws IOException {
|
public static Spans wrap(IndexReader reader, SpanQuery spanQuery) throws IOException {
|
||||||
return wrap(reader, spanQuery, SpanCollector.NO_OP);
|
return wrap(reader, spanQuery, SpanWeight.Postings.POSITIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Spans wrap(IndexReader reader, SpanQuery spanQuery, SpanCollector collector) throws IOException {
|
public static Spans wrap(IndexReader reader, SpanQuery spanQuery, SpanWeight.Postings requiredPostings) throws IOException {
|
||||||
|
|
||||||
LeafReader lr = SlowCompositeReaderWrapper.wrap(reader); // slow, but ok for testing
|
LeafReader lr = SlowCompositeReaderWrapper.wrap(reader); // slow, but ok for testing
|
||||||
LeafReaderContext lrContext = lr.getContext();
|
LeafReaderContext lrContext = lr.getContext();
|
||||||
IndexSearcher searcher = new IndexSearcher(lr);
|
IndexSearcher searcher = new IndexSearcher(lr);
|
||||||
searcher.setQueryCache(null);
|
searcher.setQueryCache(null);
|
||||||
|
|
||||||
SpanWeight w = (SpanWeight) searcher.createNormalizedWeight(spanQuery, false);
|
SpanWeight w = spanQuery.createWeight(searcher, false);
|
||||||
|
|
||||||
return w.getSpans(lrContext, new Bits.MatchAllBits(lr.numDocs()), collector);
|
return w.getSpans(lrContext, new Bits.MatchAllBits(lr.numDocs()), requiredPostings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
package org.apache.lucene.search.spans;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||||
|
* contributor license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright ownership.
|
||||||
|
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||||
|
* (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import org.apache.lucene.analysis.MockAnalyzer;
|
||||||
|
import org.apache.lucene.document.Document;
|
||||||
|
import org.apache.lucene.document.FieldType;
|
||||||
|
import org.apache.lucene.document.TextField;
|
||||||
|
import org.apache.lucene.index.IndexOptions;
|
||||||
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.NoMergePolicy;
|
||||||
|
import org.apache.lucene.index.PostingsEnum;
|
||||||
|
import org.apache.lucene.index.RandomIndexWriter;
|
||||||
|
import org.apache.lucene.index.Term;
|
||||||
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
|
import org.apache.lucene.store.Directory;
|
||||||
|
import org.apache.lucene.util.LuceneTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class TestSpanCollection extends LuceneTestCase {
|
||||||
|
|
||||||
|
protected IndexSearcher searcher;
|
||||||
|
protected Directory directory;
|
||||||
|
protected IndexReader reader;
|
||||||
|
|
||||||
|
public static final String FIELD = "field";
|
||||||
|
|
||||||
|
public static FieldType OFFSETS = new FieldType(TextField.TYPE_STORED);
|
||||||
|
static {
|
||||||
|
OFFSETS.setIndexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
reader.close();
|
||||||
|
directory.close();
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
directory = newDirectory();
|
||||||
|
RandomIndexWriter writer = new RandomIndexWriter(random(), directory,
|
||||||
|
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(NoMergePolicy.INSTANCE));
|
||||||
|
for (int i = 0; i < docFields.length; i++) {
|
||||||
|
Document doc = new Document();
|
||||||
|
doc.add(newField(FIELD, docFields[i], OFFSETS));
|
||||||
|
writer.addDocument(doc);
|
||||||
|
}
|
||||||
|
reader = writer.getReader();
|
||||||
|
writer.close();
|
||||||
|
searcher = newSearcher(reader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TermCollector implements SpanCollector {
|
||||||
|
|
||||||
|
final Set<Term> terms = new HashSet<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void collectLeaf(PostingsEnum postings, int position, Term term) throws IOException {
|
||||||
|
terms.add(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset() {
|
||||||
|
terms.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String[] docFields = {
|
||||||
|
"w1 w2 w3 w4 w5",
|
||||||
|
"w1 w3 w2 w3 zz",
|
||||||
|
"w1 xx w2 yy w4",
|
||||||
|
"w1 w2 w1 w4 w2 w3"
|
||||||
|
};
|
||||||
|
|
||||||
|
private void checkCollectedTerms(Spans spans, TermCollector collector, Term... expectedTerms) throws IOException {
|
||||||
|
collector.reset();
|
||||||
|
spans.collect(collector);
|
||||||
|
for (Term t : expectedTerms) {
|
||||||
|
assertTrue("Missing term " + t, collector.terms.contains(t));
|
||||||
|
}
|
||||||
|
assertEquals("Unexpected terms found", expectedTerms.length, collector.terms.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNestedNearQuery() throws IOException {
|
||||||
|
|
||||||
|
// near(w1, near(w2, or(w3, w4)))
|
||||||
|
|
||||||
|
SpanTermQuery q1 = new SpanTermQuery(new Term(FIELD, "w1"));
|
||||||
|
SpanTermQuery q2 = new SpanTermQuery(new Term(FIELD, "w2"));
|
||||||
|
SpanTermQuery q3 = new SpanTermQuery(new Term(FIELD, "w3"));
|
||||||
|
SpanTermQuery q4 = new SpanTermQuery(new Term(FIELD, "w4"));
|
||||||
|
|
||||||
|
SpanOrQuery q5 = new SpanOrQuery(q4, q3);
|
||||||
|
SpanNearQuery q6 = new SpanNearQuery(new SpanQuery[]{q2, q5}, 1, true);
|
||||||
|
SpanNearQuery q7 = new SpanNearQuery(new SpanQuery[]{q1, q6}, 1, true);
|
||||||
|
|
||||||
|
TermCollector collector = new TermCollector();
|
||||||
|
Spans spans = MultiSpansWrapper.wrap(reader, q7, SpanWeight.Postings.POSITIONS);
|
||||||
|
assertEquals(0, spans.advance(0));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w1"), new Term(FIELD, "w2"), new Term(FIELD, "w3"));
|
||||||
|
|
||||||
|
assertEquals(3, spans.advance(3));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w1"), new Term(FIELD, "w2"), new Term(FIELD, "w4"));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w1"), new Term(FIELD, "w2"), new Term(FIELD, "w3"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOrQuery() throws IOException {
|
||||||
|
SpanTermQuery q2 = new SpanTermQuery(new Term(FIELD, "w2"));
|
||||||
|
SpanTermQuery q3 = new SpanTermQuery(new Term(FIELD, "w3"));
|
||||||
|
SpanOrQuery orQuery = new SpanOrQuery(q2, q3);
|
||||||
|
|
||||||
|
TermCollector collector = new TermCollector();
|
||||||
|
Spans spans = MultiSpansWrapper.wrap(reader, orQuery, SpanWeight.Postings.POSITIONS);
|
||||||
|
|
||||||
|
assertEquals(1, spans.advance(1));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w3"));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w2"));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w3"));
|
||||||
|
|
||||||
|
assertEquals(3, spans.advance(3));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w2"));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w2"));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w3"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSpanNotQuery() throws IOException {
|
||||||
|
|
||||||
|
SpanTermQuery q1 = new SpanTermQuery(new Term(FIELD, "w1"));
|
||||||
|
SpanTermQuery q2 = new SpanTermQuery(new Term(FIELD, "w2"));
|
||||||
|
SpanTermQuery q3 = new SpanTermQuery(new Term(FIELD, "w3"));
|
||||||
|
|
||||||
|
SpanNearQuery nq = new SpanNearQuery(new SpanQuery[]{q1, q2}, 2, true);
|
||||||
|
SpanNotQuery notq = new SpanNotQuery(nq, q3);
|
||||||
|
|
||||||
|
TermCollector collector = new TermCollector();
|
||||||
|
Spans spans = MultiSpansWrapper.wrap(reader, notq, SpanWeight.Postings.POSITIONS);
|
||||||
|
|
||||||
|
assertEquals(2, spans.advance(2));
|
||||||
|
spans.nextStartPosition();
|
||||||
|
checkCollectedTerms(spans, collector, new Term(FIELD, "w1"), new Term(FIELD, "w2"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ public class WeightedSpanTermExtractor {
|
||||||
LeafReaderContext context = getLeafContext();
|
LeafReaderContext context = getLeafContext();
|
||||||
SpanWeight w = (SpanWeight) searcher.createNormalizedWeight(q, false);
|
SpanWeight w = (SpanWeight) searcher.createNormalizedWeight(q, false);
|
||||||
Bits acceptDocs = context.reader().getLiveDocs();
|
Bits acceptDocs = context.reader().getLiveDocs();
|
||||||
final Spans spans = w.getSpans(context, acceptDocs);
|
final Spans spans = w.getSpans(context, acceptDocs, SpanWeight.Postings.POSITIONS);
|
||||||
if (spans == null) {
|
if (spans == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,8 @@ public class AssertingSpanQuery extends SpanQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores, SpanCollectorFactory factory) throws IOException {
|
public SpanWeight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||||
SpanWeight weight = in.createWeight(searcher, needsScores, factory);
|
SpanWeight weight = in.createWeight(searcher, needsScores);
|
||||||
return new AssertingSpanWeight(searcher, weight);
|
return new AssertingSpanWeight(searcher, weight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class AssertingSpanWeight extends SpanWeight {
|
||||||
* @throws IOException on error
|
* @throws IOException on error
|
||||||
*/
|
*/
|
||||||
public AssertingSpanWeight(IndexSearcher searcher, SpanWeight in) throws IOException {
|
public AssertingSpanWeight(IndexSearcher searcher, SpanWeight in) throws IOException {
|
||||||
super((SpanQuery) in.getQuery(), searcher, null, in.collectorFactory);
|
super((SpanQuery) in.getQuery(), searcher, null);
|
||||||
this.in = in;
|
this.in = in;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ public class AssertingSpanWeight extends SpanWeight {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Spans getSpans(LeafReaderContext context, Bits liveDocs, SpanCollector collector) throws IOException {
|
public Spans getSpans(LeafReaderContext context, Bits liveDocs, Postings requiredPostings) throws IOException {
|
||||||
Spans spans = in.getSpans(context, liveDocs, collector);
|
Spans spans = in.getSpans(context, liveDocs, requiredPostings);
|
||||||
if (spans == null)
|
if (spans == null)
|
||||||
return null;
|
return null;
|
||||||
return new AssertingSpans(spans);
|
return new AssertingSpans(spans);
|
||||||
|
|
Loading…
Reference in New Issue