LUCENE-7755: Join queries should not reference IndexReaders.

This commit is contained in:
Adrien Grand 2017-03-30 15:11:52 +02:00
parent edcdc3052b
commit edafcbad14
5 changed files with 39 additions and 18 deletions

View File

@ -93,6 +93,13 @@ Other
* LUCENE-7743: Never call new String(String). * LUCENE-7743: Never call new String(String).
(Daniel Jelinski via Adrien Grand) (Daniel Jelinski via Adrien Grand)
======================= Lucene 6.5.1 =======================
Bug Fixes
* LUCENE-7755: Fixed join queries to not reference IndexReaders, as it could
cause leaks if they are cached. (Adrien Grand)
======================= Lucene 6.5.0 ======================= ======================= Lucene 6.5.0 =======================
API Changes API Changes

View File

@ -47,6 +47,14 @@ public abstract class IndexReaderContext {
this.isTopLevel = parent==null; this.isTopLevel = parent==null;
} }
/** Expert: Return an {@link Object} that uniquely identifies this context.
* The returned object does neither reference this {@link IndexReaderContext}
* nor the wrapped {@link IndexReader}.
* @lucene.experimental */
public Object id() {
return identity;
}
/** Returns the {@link IndexReader}, this context represents. */ /** Returns the {@link IndexReader}, this context represents. */
public abstract IndexReader reader(); public abstract IndexReader reader();

View File

@ -20,7 +20,7 @@ import java.io.IOException;
import java.util.Set; import java.util.Set;
import org.apache.lucene.index.DocValues; import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiDocValues; import org.apache.lucene.index.MultiDocValues;
import org.apache.lucene.index.SortedDocValues; import org.apache.lucene.index.SortedDocValues;
@ -48,19 +48,23 @@ final class GlobalOrdinalsQuery extends Query {
// just for hashcode and equals: // just for hashcode and equals:
private final Query fromQuery; private final Query fromQuery;
private final IndexReader indexReader; // id of the context rather than the context itself in order not to hold references to index readers
private final Object indexReaderContextId;
GlobalOrdinalsQuery(LongBitSet foundOrds, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, IndexReader indexReader) { GlobalOrdinalsQuery(LongBitSet foundOrds, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, IndexReaderContext context) {
this.foundOrds = foundOrds; this.foundOrds = foundOrds;
this.joinField = joinField; this.joinField = joinField;
this.globalOrds = globalOrds; this.globalOrds = globalOrds;
this.toQuery = toQuery; this.toQuery = toQuery;
this.fromQuery = fromQuery; this.fromQuery = fromQuery;
this.indexReader = indexReader; this.indexReaderContextId = context.id();
} }
@Override @Override
public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException { public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
if (searcher.getTopReaderContext().id() != indexReaderContextId) {
throw new IllegalStateException("Creating the weight against a different index reader than this query has been built for.");
}
return new W(this, toQuery.createWeight(searcher, false, 1f), boost); return new W(this, toQuery.createWeight(searcher, false, 1f), boost);
} }
@ -74,7 +78,7 @@ final class GlobalOrdinalsQuery extends Query {
return fromQuery.equals(other.fromQuery) && return fromQuery.equals(other.fromQuery) &&
joinField.equals(other.joinField) && joinField.equals(other.joinField) &&
toQuery.equals(other.toQuery) && toQuery.equals(other.toQuery) &&
indexReader.equals(other.indexReader); indexReaderContextId.equals(other.indexReaderContextId);
} }
@Override @Override
@ -83,7 +87,7 @@ final class GlobalOrdinalsQuery extends Query {
result = 31 * result + joinField.hashCode(); result = 31 * result + joinField.hashCode();
result = 31 * result + toQuery.hashCode(); result = 31 * result + toQuery.hashCode();
result = 31 * result + fromQuery.hashCode(); result = 31 * result + fromQuery.hashCode();
result = 31 * result + indexReader.hashCode(); result = 31 * result + indexReaderContextId.hashCode();
return result; return result;
} }

View File

@ -20,7 +20,7 @@ import java.io.IOException;
import java.util.Set; import java.util.Set;
import org.apache.lucene.index.DocValues; import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiDocValues; import org.apache.lucene.index.MultiDocValues;
import org.apache.lucene.index.SortedDocValues; import org.apache.lucene.index.SortedDocValues;
@ -48,9 +48,10 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
private final Query fromQuery; private final Query fromQuery;
private final int min; private final int min;
private final int max; private final int max;
private final IndexReader indexReader; // id of the context rather than the context itself in order not to hold references to index readers
private final Object indexReaderContextId;
GlobalOrdinalsWithScoreQuery(GlobalOrdinalsWithScoreCollector collector, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, int min, int max, IndexReader indexReader) { GlobalOrdinalsWithScoreQuery(GlobalOrdinalsWithScoreCollector collector, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, int min, int max, IndexReaderContext context) {
this.collector = collector; this.collector = collector;
this.joinField = joinField; this.joinField = joinField;
this.globalOrds = globalOrds; this.globalOrds = globalOrds;
@ -58,11 +59,14 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
this.fromQuery = fromQuery; this.fromQuery = fromQuery;
this.min = min; this.min = min;
this.max = max; this.max = max;
this.indexReader = indexReader; this.indexReaderContextId = context.id();
} }
@Override @Override
public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException { public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
if (searcher.getTopReaderContext().id() != indexReaderContextId) {
throw new IllegalStateException("Creating the weight against a different index reader than this query has been built for.");
}
return new W(this, toQuery.createWeight(searcher, false, 1f)); return new W(this, toQuery.createWeight(searcher, false, 1f));
} }
@ -78,7 +82,7 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
joinField.equals(other.joinField) && joinField.equals(other.joinField) &&
fromQuery.equals(other.fromQuery) && fromQuery.equals(other.fromQuery) &&
toQuery.equals(other.toQuery) && toQuery.equals(other.toQuery) &&
indexReader.equals(other.indexReader); indexReaderContextId.equals(other.indexReaderContextId);
} }
@Override @Override
@ -89,7 +93,7 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
result = 31 * result + fromQuery.hashCode(); result = 31 * result + fromQuery.hashCode();
result = 31 * result + min; result = 31 * result + min;
result = 31 * result + max; result = 31 * result + max;
result = 31 * result + indexReader.hashCode(); result = 31 * result + indexReaderContextId.hashCode();
return result; return result;
} }

View File

@ -32,7 +32,6 @@ import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.BinaryDocValues; import org.apache.lucene.index.BinaryDocValues;
import org.apache.lucene.index.DocValues; import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.DocValuesType;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReader; import org.apache.lucene.index.LeafReader;
import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiDocValues; import org.apache.lucene.index.MultiDocValues;
@ -467,8 +466,7 @@ public final class JoinUtil {
MultiDocValues.OrdinalMap ordinalMap, MultiDocValues.OrdinalMap ordinalMap,
int min, int min,
int max) throws IOException { int max) throws IOException {
IndexReader indexReader = searcher.getIndexReader(); int numSegments = searcher.getIndexReader().leaves().size();
int numSegments = indexReader.leaves().size();
final long valueCount; final long valueCount;
if (numSegments == 0) { if (numSegments == 0) {
return new MatchNoDocsQuery("JoinUtil.createJoinQuery with no segments"); return new MatchNoDocsQuery("JoinUtil.createJoinQuery with no segments");
@ -509,7 +507,7 @@ public final class JoinUtil {
if (min <= 0 && max == Integer.MAX_VALUE) { if (min <= 0 && max == Integer.MAX_VALUE) {
GlobalOrdinalsCollector globalOrdinalsCollector = new GlobalOrdinalsCollector(joinField, ordinalMap, valueCount); GlobalOrdinalsCollector globalOrdinalsCollector = new GlobalOrdinalsCollector(joinField, ordinalMap, valueCount);
searcher.search(rewrittenFromQuery, globalOrdinalsCollector); searcher.search(rewrittenFromQuery, globalOrdinalsCollector);
return new GlobalOrdinalsQuery(globalOrdinalsCollector.getCollectorOrdinals(), joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, indexReader); return new GlobalOrdinalsQuery(globalOrdinalsCollector.getCollectorOrdinals(), joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, searcher.getTopReaderContext());
} else { } else {
globalOrdinalsWithScoreCollector = new GlobalOrdinalsWithScoreCollector.NoScore(joinField, ordinalMap, valueCount, min, max); globalOrdinalsWithScoreCollector = new GlobalOrdinalsWithScoreCollector.NoScore(joinField, ordinalMap, valueCount, min, max);
break; break;
@ -518,7 +516,7 @@ public final class JoinUtil {
throw new IllegalArgumentException(String.format(Locale.ROOT, "Score mode %s isn't supported.", scoreMode)); throw new IllegalArgumentException(String.format(Locale.ROOT, "Score mode %s isn't supported.", scoreMode));
} }
searcher.search(rewrittenFromQuery, globalOrdinalsWithScoreCollector); searcher.search(rewrittenFromQuery, globalOrdinalsWithScoreCollector);
return new GlobalOrdinalsWithScoreQuery(globalOrdinalsWithScoreCollector, joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, min, max, indexReader); return new GlobalOrdinalsWithScoreQuery(globalOrdinalsWithScoreCollector, joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, min, max, searcher.getTopReaderContext());
} }
} }