LUCENE-7810: Fix equals() and hashCode() methods of several join queries.

This commit is contained in:
Martijn van Groningen 2017-05-22 17:56:08 +02:00 committed by Martijn van Groningen
parent 08d24570a7
commit 85c1319c76
No known key found for this signature in database
GPG Key ID: AB236F4FCF2AF12A
9 changed files with 356 additions and 89 deletions

View File

@ -103,6 +103,11 @@ Other
from methods that don't declare them ("sneaky throw" hack). (Robert Muir,
Uwe Schindler, Dawid Weiss)
Bug Fixes
* LUCENE-7810: Fix equals() and hashCode() methods of several join queries.
(Hossman, Adrien Grand, Martijn van Groningen)
======================= Lucene 6.6.0 =======================
New Features

View File

@ -20,7 +20,6 @@ import java.io.IOException;
import java.util.Set;
import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiDocValues;
import org.apache.lucene.index.SortedDocValues;
@ -51,13 +50,14 @@ final class GlobalOrdinalsQuery extends Query {
// 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, IndexReaderContext context) {
GlobalOrdinalsQuery(LongBitSet foundOrds, String joinField, MultiDocValues.OrdinalMap globalOrds, Query toQuery,
Query fromQuery, Object indexReaderContextId) {
this.foundOrds = foundOrds;
this.joinField = joinField;
this.globalOrds = globalOrds;
this.toQuery = toQuery;
this.fromQuery = fromQuery;
this.indexReaderContextId = context.id();
this.indexReaderContextId = indexReaderContextId;
}
@Override

View File

@ -20,7 +20,6 @@ import java.io.IOException;
import java.util.Set;
import org.apache.lucene.index.DocValues;
import org.apache.lucene.index.IndexReaderContext;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiDocValues;
import org.apache.lucene.index.SortedDocValues;
@ -45,21 +44,25 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
private final Query toQuery;
// just for hashcode and equals:
private final ScoreMode scoreMode;
private final Query fromQuery;
private final int min;
private final int max;
// 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, IndexReaderContext context) {
GlobalOrdinalsWithScoreQuery(GlobalOrdinalsWithScoreCollector collector, ScoreMode scoreMode, String joinField,
MultiDocValues.OrdinalMap globalOrds, Query toQuery, Query fromQuery, int min, int max,
Object indexReaderContextId) {
this.collector = collector;
this.joinField = joinField;
this.globalOrds = globalOrds;
this.toQuery = toQuery;
this.scoreMode = scoreMode;
this.fromQuery = fromQuery;
this.min = min;
this.max = max;
this.indexReaderContextId = context.id();
this.indexReaderContextId = indexReaderContextId;
}
@Override
@ -67,6 +70,13 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
if (searcher.getTopReaderContext().id() != indexReaderContextId) {
throw new IllegalStateException("Creating the weight against a different index reader than this query has been built for.");
}
boolean doNoMinMax = min <= 0 && max == Integer.MAX_VALUE;
if (needsScores == false && doNoMinMax) {
// We don't need scores then quickly change the query to not uses the scores:
GlobalOrdinalsQuery globalOrdinalsQuery = new GlobalOrdinalsQuery(collector.collectedOrds, joinField, globalOrds,
toQuery, fromQuery, indexReaderContextId);
return globalOrdinalsQuery.createWeight(searcher, false, boost);
}
return new W(this, toQuery.createWeight(searcher, false, 1f));
}
@ -79,6 +89,7 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
private boolean equalsTo(GlobalOrdinalsWithScoreQuery other) {
return min == other.min &&
max == other.max &&
scoreMode.equals(other.scoreMode) &&
joinField.equals(other.joinField) &&
fromQuery.equals(other.fromQuery) &&
toQuery.equals(other.toQuery) &&
@ -88,6 +99,7 @@ final class GlobalOrdinalsWithScoreQuery extends Query {
@Override
public int hashCode() {
int result = classHash();
result = 31 * result + scoreMode.hashCode();
result = 31 * result + joinField.hashCode();
result = 31 * result + toQuery.hashCode();
result = 31 * result + fromQuery.hashCode();

View File

@ -104,7 +104,7 @@ public final class JoinUtil {
termsWithScoreCollector = GenericTermsCollector.createCollectorSV(svFunction, scoreMode);
}
return createJoinQuery(multipleValuesPerDocument, toField, fromQuery, fromSearcher, scoreMode, termsWithScoreCollector);
return createJoinQuery(multipleValuesPerDocument, toField, fromQuery, fromField, fromSearcher, scoreMode, termsWithScoreCollector);
}
/**
@ -362,7 +362,7 @@ public final class JoinUtil {
encoded.length = bytesPerDim;
if (needsScore) {
return new PointInSetIncludingScoreQuery(fromQuery, multipleValuesPerDocument, toField, bytesPerDim, stream) {
return new PointInSetIncludingScoreQuery(scoreMode, fromQuery, multipleValuesPerDocument, toField, bytesPerDim, stream) {
@Override
protected String toString(byte[] value) {
@ -379,25 +379,26 @@ public final class JoinUtil {
}
}
private static Query createJoinQuery(boolean multipleValuesPerDocument, String toField, Query fromQuery,
IndexSearcher fromSearcher, ScoreMode scoreMode, final GenericTermsCollector collector)
throws IOException {
private static Query createJoinQuery(boolean multipleValuesPerDocument, String toField, Query fromQuery, String fromField,
IndexSearcher fromSearcher, ScoreMode scoreMode, final GenericTermsCollector collector) throws IOException {
fromSearcher.search(fromQuery, collector);
switch (scoreMode) {
case None:
return new TermsQuery(toField, fromQuery, collector.getCollectedTerms());
return new TermsQuery(toField, collector.getCollectedTerms(), fromField, fromQuery, fromSearcher.getTopReaderContext().id());
case Total:
case Max:
case Min:
case Avg:
return new TermsIncludingScoreQuery(
scoreMode,
toField,
multipleValuesPerDocument,
collector.getCollectedTerms(),
collector.getScoresPerTerm(),
fromQuery
fromField,
fromQuery,
fromSearcher.getTopReaderContext().id()
);
default:
throw new IllegalArgumentException(String.format(Locale.ROOT, "Score mode %s isn't supported.", scoreMode));
@ -507,7 +508,8 @@ public final class JoinUtil {
if (min <= 0 && max == Integer.MAX_VALUE) {
GlobalOrdinalsCollector globalOrdinalsCollector = new GlobalOrdinalsCollector(joinField, ordinalMap, valueCount);
searcher.search(rewrittenFromQuery, globalOrdinalsCollector);
return new GlobalOrdinalsQuery(globalOrdinalsCollector.getCollectorOrdinals(), joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, searcher.getTopReaderContext());
return new GlobalOrdinalsQuery(globalOrdinalsCollector.getCollectorOrdinals(), joinField, ordinalMap, rewrittenToQuery,
rewrittenFromQuery, searcher.getTopReaderContext().id());
} else {
globalOrdinalsWithScoreCollector = new GlobalOrdinalsWithScoreCollector.NoScore(joinField, ordinalMap, valueCount, min, max);
break;
@ -516,7 +518,8 @@ public final class JoinUtil {
throw new IllegalArgumentException(String.format(Locale.ROOT, "Score mode %s isn't supported.", scoreMode));
}
searcher.search(rewrittenFromQuery, globalOrdinalsWithScoreCollector);
return new GlobalOrdinalsWithScoreQuery(globalOrdinalsWithScoreCollector, joinField, ordinalMap, rewrittenToQuery, rewrittenFromQuery, min, max, searcher.getTopReaderContext());
return new GlobalOrdinalsWithScoreQuery(globalOrdinalsWithScoreCollector, scoreMode, joinField, ordinalMap, rewrittenToQuery,
rewrittenFromQuery, min, max, searcher.getTopReaderContext().id());
}
}

View File

@ -66,6 +66,7 @@ abstract class PointInSetIncludingScoreQuery extends Query {
}
};
final ScoreMode scoreMode;
final Query originalQuery;
final boolean multipleValuesPerDocument;
final PrefixCodedTerms sortedPackedPoints;
@ -81,8 +82,9 @@ abstract class PointInSetIncludingScoreQuery extends Query {
}
PointInSetIncludingScoreQuery(Query originalQuery, boolean multipleValuesPerDocument, String field, int bytesPerDim,
Stream packedPoints) {
PointInSetIncludingScoreQuery(ScoreMode scoreMode, Query originalQuery, boolean multipleValuesPerDocument,
String field, int bytesPerDim, Stream packedPoints) {
this.scoreMode = scoreMode;
this.originalQuery = originalQuery;
this.multipleValuesPerDocument = multipleValuesPerDocument;
this.field = field;
@ -276,6 +278,7 @@ abstract class PointInSetIncludingScoreQuery extends Query {
@Override
public final int hashCode() {
int hash = classHash();
hash = 31 * hash + scoreMode.hashCode();
hash = 31 * hash + field.hashCode();
hash = 31 * hash + originalQuery.hashCode();
hash = 31 * hash + sortedPackedPointsHashCode;
@ -290,7 +293,8 @@ abstract class PointInSetIncludingScoreQuery extends Query {
}
private boolean equalsTo(PointInSetIncludingScoreQuery other) {
return other.field.equals(field) &&
return other.scoreMode.equals(scoreMode) &&
other.field.equals(field) &&
other.originalQuery.equals(originalQuery) &&
other.bytesPerDim == bytesPerDim &&
other.sortedPackedPointsHashCode == sortedPackedPointsHashCode &&

View File

@ -17,11 +17,10 @@
package org.apache.lucene.search.join;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.PostingsEnum;
import org.apache.lucene.index.Term;
@ -40,48 +39,36 @@ import org.apache.lucene.util.FixedBitSet;
class TermsIncludingScoreQuery extends Query {
final String field;
final boolean multipleValuesPerDocument;
final BytesRefHash terms;
final float[] scores;
final int[] ords;
final Query originalQuery;
final Query unwrittenOriginalQuery;
private final ScoreMode scoreMode;
private final String toField;
private final boolean multipleValuesPerDocument;
private final BytesRefHash terms;
private final float[] scores;
private final int[] ords;
TermsIncludingScoreQuery(String field, boolean multipleValuesPerDocument, BytesRefHash terms, float[] scores, Query originalQuery) {
this.field = field;
// These fields are used for equals() and hashcode() only
private final Query fromQuery;
private final String fromField;
// id of the context rather than the context itself in order not to hold references to index readers
private final Object topReaderContextId;
TermsIncludingScoreQuery(ScoreMode scoreMode, String toField, boolean multipleValuesPerDocument, BytesRefHash terms, float[] scores,
String fromField, Query fromQuery, Object indexReaderContextId) {
this.scoreMode = scoreMode;
this.toField = toField;
this.multipleValuesPerDocument = multipleValuesPerDocument;
this.terms = terms;
this.scores = scores;
this.originalQuery = originalQuery;
this.ords = terms.sort();
this.unwrittenOriginalQuery = originalQuery;
}
private TermsIncludingScoreQuery(String field, boolean multipleValuesPerDocument, BytesRefHash terms, float[] scores, int[] ords, Query originalQuery, Query unwrittenOriginalQuery) {
this.field = field;
this.multipleValuesPerDocument = multipleValuesPerDocument;
this.terms = terms;
this.scores = scores;
this.originalQuery = originalQuery;
this.ords = ords;
this.unwrittenOriginalQuery = unwrittenOriginalQuery;
this.fromField = fromField;
this.fromQuery = fromQuery;
this.topReaderContextId = indexReaderContextId;
}
@Override
public String toString(String string) {
return String.format(Locale.ROOT, "TermsIncludingScoreQuery{field=%s;originalQuery=%s}", field, unwrittenOriginalQuery);
}
@Override
public Query rewrite(IndexReader reader) throws IOException {
final Query originalQueryRewrite = originalQuery.rewrite(reader);
if (originalQueryRewrite != originalQuery) {
return new TermsIncludingScoreQuery(field, multipleValuesPerDocument, terms, scores,
ords, originalQueryRewrite, originalQuery);
} else {
return super.rewrite(reader);
}
return String.format(Locale.ROOT, "TermsIncludingScoreQuery{field=%s;fromQuery=%s}", toField, fromQuery);
}
@Override
@ -91,21 +78,25 @@ class TermsIncludingScoreQuery extends Query {
}
private boolean equalsTo(TermsIncludingScoreQuery other) {
return field.equals(other.field) &&
unwrittenOriginalQuery.equals(other.unwrittenOriginalQuery);
return Objects.equals(scoreMode, other.scoreMode) &&
Objects.equals(toField, other.toField) &&
Objects.equals(fromField, other.fromField) &&
Objects.equals(fromQuery, other.fromQuery) &&
Objects.equals(topReaderContextId, other.topReaderContextId);
}
@Override
public int hashCode() {
final int prime = 31;
int result = classHash();
result += prime * field.hashCode();
result += prime * unwrittenOriginalQuery.hashCode();
return result;
return classHash() + Objects.hash(scoreMode, toField, fromField, fromQuery, topReaderContextId);
}
@Override
public Weight createWeight(IndexSearcher searcher, boolean needsScores, float boost) throws IOException {
if (needsScores == false) {
// We don't need scores then quickly change the query:
TermsQuery termsQuery = new TermsQuery(toField, terms, fromField, fromQuery, topReaderContextId);
return searcher.rewrite(termsQuery).createWeight(searcher, false, boost);
}
return new Weight(TermsIncludingScoreQuery.this) {
@Override
@ -113,7 +104,7 @@ class TermsIncludingScoreQuery extends Query {
@Override
public Explanation explain(LeafReaderContext context, int doc) throws IOException {
Terms terms = context.reader().terms(field);
Terms terms = context.reader().terms(toField);
if (terms != null) {
TermsEnum segmentTermsEnum = terms.iterator();
BytesRef spare = new BytesRef();
@ -133,7 +124,7 @@ class TermsIncludingScoreQuery extends Query {
@Override
public Scorer scorer(LeafReaderContext context) throws IOException {
Terms terms = context.reader().terms(field);
Terms terms = context.reader().terms(toField);
if (terms == null) {
return null;
}
@ -151,7 +142,7 @@ class TermsIncludingScoreQuery extends Query {
};
}
class SVInOrderScorer extends Scorer {
final DocIdSetIterator matchingDocsIterator;
@ -238,14 +229,4 @@ class TermsIncludingScoreQuery extends Query {
}
}
void dump(PrintStream out){
out.println(field+":");
final BytesRef ref = new BytesRef();
for (int i = 0; i < terms.size(); i++) {
terms.get(ords[i], ref);
out.print(ref+" "+ref.utf8ToString()+" ");
out.println(" score="+scores[ords[i]]);
out.println("");
}
}
}

View File

@ -16,6 +16,9 @@
*/
package org.apache.lucene.search.join;
import java.io.IOException;
import java.util.Objects;
import org.apache.lucene.index.FilteredTermsEnum;
import org.apache.lucene.index.Terms;
import org.apache.lucene.index.TermsEnum;
@ -25,8 +28,6 @@ import org.apache.lucene.util.AttributeSource;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.BytesRefHash;
import java.io.IOException;
/**
* A query that has an array of terms from a specific field. This query will match documents have one or more terms in
* the specified field that match with the terms specified in the array.
@ -37,17 +38,25 @@ class TermsQuery extends MultiTermQuery {
private final BytesRefHash terms;
private final int[] ords;
private final Query fromQuery; // Used for equals() only
// These fields are used for equals() and hashcode() only
private final String fromField;
private final Query fromQuery;
// id of the context rather than the context itself in order not to hold references to index readers
private final Object indexReaderContextId;
/**
* @param field The field that should contain terms that are specified in the previous parameter
* @param terms The terms that matching documents should have. The terms must be sorted by natural order.
* @param toField The field that should contain terms that are specified in the next parameter.
* @param terms The terms that matching documents should have. The terms must be sorted by natural order.
* @param indexReaderContextId Refers to the top level index reader used to create the set of terms in the previous parameter.
*/
TermsQuery(String field, Query fromQuery, BytesRefHash terms) {
super(field);
this.fromQuery = fromQuery;
TermsQuery(String toField, BytesRefHash terms, String fromField, Query fromQuery, Object indexReaderContextId) {
super(toField);
this.terms = terms;
ords = terms.sort();
this.fromField = fromField;
this.fromQuery = fromQuery;
this.indexReaderContextId = indexReaderContextId;
}
@Override
@ -63,6 +72,7 @@ class TermsQuery extends MultiTermQuery {
public String toString(String string) {
return "TermsQuery{" +
"field=" + field +
"fromQuery=" + fromQuery.toString(field) +
'}';
}
@ -77,18 +87,15 @@ class TermsQuery extends MultiTermQuery {
}
TermsQuery other = (TermsQuery) obj;
if (!fromQuery.equals(other.fromQuery)) {
return false;
}
return true;
return Objects.equals(field, other.field) &&
Objects.equals(fromField, other.fromField) &&
Objects.equals(fromQuery, other.fromQuery) &&
Objects.equals(indexReaderContextId, other.indexReaderContextId);
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result += prime * fromQuery.hashCode();
return result;
return classHash() + Objects.hash(field, fromField, fromQuery, indexReaderContextId);
}
static class SeekingTermSetTermsEnum extends FilteredTermsEnum {

View File

@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -922,6 +923,251 @@ public class TestJoinUtil extends LuceneTestCase {
dir.close();
}
public void testEquals() throws Exception {
final int numDocs = atLeast(random(), 50);
try (final Directory dir = newDirectory()) {
try (final RandomIndexWriter w = new RandomIndexWriter(random(), dir,
newIndexWriterConfig(new MockAnalyzer(random()))
.setMergePolicy(newLogMergePolicy()))) {
boolean multiValued = random().nextBoolean();
String joinField = multiValued ? "mvField" : "svField";
for (int id = 0; id < numDocs; id++) {
Document doc = new Document();
doc.add(new TextField("id", "" + id, Field.Store.NO));
doc.add(new TextField("name", "name" + (id % 7), Field.Store.NO));
if (multiValued) {
int numValues = 1 + random().nextInt(2);
for (int i = 0; i < numValues; i++) {
doc.add(new SortedSetDocValuesField(joinField, new BytesRef("" + random().nextInt(13))));
}
} else {
doc.add(new SortedDocValuesField(joinField, new BytesRef("" + random().nextInt(13))));
}
w.addDocument(doc);
}
Set<ScoreMode> scoreModes = EnumSet.allOf(ScoreMode.class);
ScoreMode scoreMode1 = RandomPicks.randomFrom(random(), scoreModes);
scoreModes.remove(scoreMode1);
ScoreMode scoreMode2 = RandomPicks.randomFrom(random(), scoreModes);
final Query x;
try (IndexReader r = w.getReader()) {
IndexSearcher indexSearcher = new IndexSearcher(r);
x = JoinUtil.createJoinQuery(joinField, multiValued, joinField,
new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1);
assertEquals("identical calls to createJoinQuery",
x, JoinUtil.createJoinQuery(joinField, multiValued, joinField,
new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1));
assertFalse("score mode (" + scoreMode1 + " != " + scoreMode2 + "), but queries are equal",
x.equals(JoinUtil.createJoinQuery(joinField, multiValued, joinField,
new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode2)));
assertFalse("from fields (joinField != \"other_field\") but queries equals",
x.equals(JoinUtil.createJoinQuery(joinField, multiValued, "other_field",
new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1)));
assertFalse("from fields (\"other_field\" != joinField) but queries equals",
x.equals(JoinUtil.createJoinQuery("other_field", multiValued, joinField,
new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1)));
assertFalse("fromQuery (name:name5 != name:name6) but queries equals",
x.equals(JoinUtil.createJoinQuery("other_field", multiValued, joinField,
new TermQuery(new Term("name", "name6")),
indexSearcher, scoreMode1)));
}
for (int i = 0; i < 13; i++) {
Document doc = new Document();
doc.add(new TextField("id", "new_id" , Field.Store.NO));
doc.add(new TextField("name", "name5", Field.Store.NO));
if (multiValued) {
int numValues = 1 + random().nextInt(2);
for (int j = 0; j < numValues; j++) {
doc.add(new SortedSetDocValuesField(joinField, new BytesRef("" + i)));
}
} else {
doc.add(new SortedDocValuesField(joinField, new BytesRef("" + i)));
}
w.addDocument(doc);
}
try (IndexReader r = w.getReader()) {
IndexSearcher indexSearcher = new IndexSearcher(r);
assertFalse("Query shouldn't be equal, because different index readers ",
x.equals(JoinUtil.createJoinQuery(joinField, multiValued, joinField,
new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1)));
}
}
}
}
public void testEquals_globalOrdinalsJoin() throws Exception {
final int numDocs = atLeast(random(), 50);
try (final Directory dir = newDirectory()) {
try (final RandomIndexWriter w = new RandomIndexWriter(random(), dir,
newIndexWriterConfig(new MockAnalyzer(random()))
.setMergePolicy(newLogMergePolicy()))) {
String joinField = "field";
for (int id = 0; id < numDocs; id++) {
Document doc = new Document();
doc.add(new TextField("id", "" + id, Field.Store.NO));
doc.add(new TextField("name", "name" + (id % 7), Field.Store.NO));
doc.add(new SortedDocValuesField(joinField, new BytesRef("" + random().nextInt(13))));
w.addDocument(doc);
}
Set<ScoreMode> scoreModes = EnumSet.allOf(ScoreMode.class);
ScoreMode scoreMode1 = RandomPicks.randomFrom(random(), scoreModes);
scoreModes.remove(scoreMode1);
ScoreMode scoreMode2 = RandomPicks.randomFrom(random(), scoreModes);
final Query x;
try (IndexReader r = w.getReader()) {
SortedDocValues[] values = new SortedDocValues[r.leaves().size()];
for (int i = 0; i < values.length; i++) {
LeafReader leafReader = r.leaves().get(i).reader();
values[i] = DocValues.getSorted(leafReader, joinField);
}
MultiDocValues.OrdinalMap ordinalMap = MultiDocValues.OrdinalMap.build(
null, values, PackedInts.DEFAULT
);
IndexSearcher indexSearcher = new IndexSearcher(r);
x = JoinUtil.createJoinQuery(joinField, new TermQuery(new Term("name", "name5")), new MatchAllDocsQuery(),
indexSearcher, scoreMode1, ordinalMap);
assertEquals("identical calls to createJoinQuery",
x, JoinUtil.createJoinQuery(joinField, new TermQuery(new Term("name", "name5")), new MatchAllDocsQuery(),
indexSearcher, scoreMode1, ordinalMap));
assertFalse("score mode (" + scoreMode1 + " != " + scoreMode2 + "), but queries are equal",
x.equals(JoinUtil.createJoinQuery(joinField, new TermQuery(new Term("name", "name5")), new MatchAllDocsQuery(),
indexSearcher, scoreMode2, ordinalMap)));
assertFalse("fromQuery (name:name5 != name:name6) but queries equals",
x.equals(JoinUtil.createJoinQuery(joinField, new TermQuery(new Term("name", "name6")), new MatchAllDocsQuery(),
indexSearcher, scoreMode1, ordinalMap)));
}
for (int i = 0; i < 13; i++) {
Document doc = new Document();
doc.add(new TextField("id", "new_id" , Field.Store.NO));
doc.add(new TextField("name", "name5", Field.Store.NO));
doc.add(new SortedDocValuesField(joinField, new BytesRef("" + i)));
w.addDocument(doc);
}
try (IndexReader r = w.getReader()) {
SortedDocValues[] values = new SortedDocValues[r.leaves().size()];
for (int i = 0; i < values.length; i++) {
LeafReader leafReader = r.leaves().get(i).reader();
values[i] = DocValues.getSorted(leafReader, joinField);
}
MultiDocValues.OrdinalMap ordinalMap = MultiDocValues.OrdinalMap.build(
null, values, PackedInts.DEFAULT
);
IndexSearcher indexSearcher = new IndexSearcher(r);
assertFalse("Query shouldn't be equal, because different index readers ",
x.equals(JoinUtil.createJoinQuery(joinField, new TermQuery(new Term("name", "name5")), new MatchAllDocsQuery(),
indexSearcher, scoreMode1, ordinalMap)));
}
}
}
}
public void testEquals_numericJoin() throws Exception {
final int numDocs = atLeast(random(), 50);
try (final Directory dir = newDirectory()) {
try (final RandomIndexWriter w = new RandomIndexWriter(random(), dir,
newIndexWriterConfig(new MockAnalyzer(random()))
.setMergePolicy(newLogMergePolicy()))) {
boolean multiValued = random().nextBoolean();
String joinField = multiValued ? "mvField" : "svField";
for (int id = 0; id < numDocs; id++) {
Document doc = new Document();
doc.add(new TextField("id", "" + id, Field.Store.NO));
doc.add(new TextField("name", "name" + (id % 7), Field.Store.NO));
if (multiValued) {
int numValues = 1 + random().nextInt(2);
for (int i = 0; i < numValues; i++) {
doc.add(new IntPoint(joinField, random().nextInt(13)));
doc.add(new SortedNumericDocValuesField(joinField, random().nextInt(13)));
}
} else {
doc.add(new IntPoint(joinField, random().nextInt(13)));
doc.add(new NumericDocValuesField(joinField, random().nextInt(13)));
}
w.addDocument(doc);
}
Set<ScoreMode> scoreModes = EnumSet.allOf(ScoreMode.class);
ScoreMode scoreMode1 = scoreModes.toArray(new ScoreMode[0])[random().nextInt(scoreModes.size())];
scoreModes.remove(scoreMode1);
ScoreMode scoreMode2 = scoreModes.toArray(new ScoreMode[0])[random().nextInt(scoreModes.size())];
final Query x;
try (IndexReader r = w.getReader()) {
IndexSearcher indexSearcher = new IndexSearcher(r);
x = JoinUtil.createJoinQuery(joinField, multiValued, joinField,
Integer.class, new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1);
assertEquals("identical calls to createJoinQuery",
x, JoinUtil.createJoinQuery(joinField, multiValued, joinField,
Integer.class, new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1));
assertFalse("score mode (" + scoreMode1 + " != " + scoreMode2 + "), but queries are equal",
x.equals(JoinUtil.createJoinQuery(joinField, multiValued, joinField,
Integer.class, new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode2)));
assertFalse("from fields (joinField != \"other_field\") but queries equals",
x.equals(JoinUtil.createJoinQuery(joinField, multiValued, "other_field",
Integer.class, new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1)));
assertFalse("from fields (\"other_field\" != joinField) but queries equals",
x.equals(JoinUtil.createJoinQuery("other_field", multiValued, joinField,
Integer.class, new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1)));
assertFalse("fromQuery (name:name5 != name:name6) but queries equals",
x.equals(JoinUtil.createJoinQuery("other_field", multiValued, joinField,
Integer.class, new TermQuery(new Term("name", "name6")),
indexSearcher, scoreMode1)));
}
for (int i = 0; i < 13; i++) {
Document doc = new Document();
doc.add(new TextField("id", "new_id" , Field.Store.NO));
doc.add(new TextField("name", "name5", Field.Store.NO));
if (multiValued) {
int numValues = 1 + random().nextInt(2);
for (int j = 0; j < numValues; j++) {
doc.add(new SortedNumericDocValuesField(joinField, i));
doc.add(new IntPoint(joinField, i));
}
} else {
doc.add(new NumericDocValuesField(joinField, i));
doc.add(new IntPoint(joinField, i));
}
w.addDocument(doc);
}
try (IndexReader r = w.getReader()) {
IndexSearcher indexSearcher = new IndexSearcher(r);
assertFalse("Query shouldn't be equal, because different index readers ",
x.equals(JoinUtil.createJoinQuery(joinField, multiValued, joinField,
Integer.class, new TermQuery(new Term("name", "name5")),
indexSearcher, scoreMode1)));
}
}
}
}
@Test
@Slow
public void testSingleValueRandomJoin() throws Exception {

View File

@ -158,6 +158,15 @@ public class TestScoreJoinQPNoScore extends SolrTestCaseJ4 {
}
public void testNotEquals() throws SyntaxError, IOException{
try (SolrQueryRequest req = req("*:*")) {
Query x = QParser.getParser("{!join from=dept_id_s to=dept_ss score=none}text_t:develop", req).getQuery();
Query y = QParser.getParser("{!join from=dept_ss to=dept_ss score=none}text_t:develop", req).getQuery();
assertFalse("diff from fields produce equal queries",
x.equals(y));
}
}
public void testJoinQueryType() throws SyntaxError, IOException{
SolrQueryRequest req = null;
try{