Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id 688A2200C7E for ; Tue, 23 May 2017 21:05:30 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id 67038160BC3; Tue, 23 May 2017 19:05:30 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id B61A9160BA4 for ; Tue, 23 May 2017 21:05:28 +0200 (CEST) Received: (qmail 83996 invoked by uid 500); 23 May 2017 19:05:27 -0000 Mailing-List: contact commits-help@lucene.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@lucene.apache.org Delivered-To: mailing list commits@lucene.apache.org Received: (qmail 83987 invoked by uid 99); 23 May 2017 19:05:27 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 23 May 2017 19:05:27 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id C257FDFD70; Tue, 23 May 2017 19:05:27 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: mvg@apache.org To: commits@lucene.apache.org Message-Id: X-Mailer: ASF-Git Admin Mailer Subject: lucene-solr:branch_6_6: LUCENE-7810: Fix equals() and hashCode() methods of several join queries. Date: Tue, 23 May 2017 19:05:27 +0000 (UTC) archived-at: Tue, 23 May 2017 19:05:30 -0000 Repository: lucene-solr Updated Branches: refs/heads/branch_6_6 5de10a64b -> 7f6256760 LUCENE-7810: Fix equals() and hashCode() methods of several join queries. Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/7f625676 Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/7f625676 Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/7f625676 Branch: refs/heads/branch_6_6 Commit: 7f62567609e4ffd3181a9c3e713c9736c6604fa6 Parents: 5de10a6 Author: Martijn van Groningen Authored: Mon May 22 17:56:08 2017 +0200 Committer: Martijn van Groningen Committed: Tue May 23 20:55:43 2017 +0200 ---------------------------------------------------------------------- lucene/CHANGES.txt | 3 + .../lucene/search/join/GlobalOrdinalsQuery.java | 6 +- .../join/GlobalOrdinalsWithScoreQuery.java | 18 +- .../org/apache/lucene/search/join/JoinUtil.java | 39 +-- .../join/PointInSetIncludingScoreQuery.java | 10 +- .../search/join/TermsIncludingScoreQuery.java | 108 +++----- .../apache/lucene/search/join/TermsQuery.java | 39 +-- .../apache/lucene/search/join/TestJoinUtil.java | 246 +++++++++++++++++++ .../search/join/TestScoreJoinQPNoScore.java | 9 + 9 files changed, 367 insertions(+), 111 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/CHANGES.txt ---------------------------------------------------------------------- diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 438a369..8d87567 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -53,6 +53,9 @@ Bug Fixes * LUCENE-7847: Fixed all-docs-match optimization of range queries on range fields. (Adrien Grand) +* LUCENE-7810: Fix equals() and hashCode() methods of several join queries. + (Hossman, Adrien Grand, Martijn van Groningen) + Improvements * LUCENE-7782: OfflineSorter now passes the total number of items it http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java ---------------------------------------------------------------------- diff --git a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java index 00c7ae0..89ea942 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsQuery.java @@ -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 http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java ---------------------------------------------------------------------- diff --git a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java index 7e54921..5b9ba88 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/GlobalOrdinalsWithScoreQuery.java @@ -17,7 +17,6 @@ package org.apache.lucene.search.join; import 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); + } return new W(this, toQuery.createWeight(searcher, false)); } @@ -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(); http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java ---------------------------------------------------------------------- diff --git a/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java b/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java index bca5f61..c65649f 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/JoinUtil.java @@ -105,15 +105,15 @@ public final class JoinUtil { termsWithScoreCollector = GenericTermsCollector.createCollectorSV(svFunction, scoreMode); } - return createJoinQuery(multipleValuesPerDocument, toField, fromQuery, fromSearcher, scoreMode, + return createJoinQuery(multipleValuesPerDocument, toField, fromQuery, fromField, fromSearcher, scoreMode, termsWithScoreCollector); - + } - + /** * @deprecated Because {@link LegacyNumericType} is deprecated, instead use {@link #createJoinQuery(String, boolean, String, Class, Query, IndexSearcher, ScoreMode)} * - * Method for query time joining for numeric fields. It supports multi- and single- values longs and ints. + * Method for query time joining for numeric fields. It supports multi- and single- values longs and ints. * All considerations from {@link JoinUtil#createJoinQuery(String, boolean, String, Query, IndexSearcher, ScoreMode)} are applicable here too, * though memory consumption might be higher. *

@@ -138,9 +138,9 @@ public final class JoinUtil { Query fromQuery, IndexSearcher fromSearcher, ScoreMode scoreMode) throws IOException { - + final GenericTermsCollector termsCollector; - + if (multipleValuesPerDocument) { Function mvFunction = DocValuesTermsCollector.sortedNumericAsSortedSetDocValues(fromField,numericType); termsCollector = GenericTermsCollector.createCollectorMV(mvFunction, scoreMode); @@ -148,10 +148,10 @@ public final class JoinUtil { Function svFunction = DocValuesTermsCollector.numericAsBinaryDocValues(fromField,numericType); termsCollector = GenericTermsCollector.createCollectorSV(svFunction, scoreMode); } - - return createJoinQuery(multipleValuesPerDocument, toField, fromQuery, fromSearcher, scoreMode, + + return createJoinQuery(multipleValuesPerDocument, toField, fromQuery, fromField, fromSearcher, scoreMode, termsCollector); - + } /** @@ -385,7 +385,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) { @@ -402,25 +402,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)); @@ -530,7 +531,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; @@ -539,7 +541,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()); } } http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java ---------------------------------------------------------------------- diff --git a/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java index f80f1ae..861632b 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/PointInSetIncludingScoreQuery.java @@ -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; @@ -287,6 +289,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; @@ -301,7 +304,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 && http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java ---------------------------------------------------------------------- diff --git a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java index 94df35b..b33a56f 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/TermsIncludingScoreQuery.java @@ -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; @@ -37,52 +36,39 @@ import org.apache.lucene.util.BitSetIterator; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.BytesRefHash; import org.apache.lucene.util.FixedBitSet; -import org.apache.lucene.util.LegacyNumericUtils; 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; - - TermsIncludingScoreQuery(String field, boolean multipleValuesPerDocument, BytesRefHash terms, float[] scores, Query originalQuery) { - this.field = field; + private final ScoreMode scoreMode; + private final String toField; + private final boolean multipleValuesPerDocument; + private final BytesRefHash terms; + private final float[] scores; + private final int[] ords; + + // 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 @@ -92,22 +78,26 @@ 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) throws IOException { - final Weight originalWeight = originalQuery.createWeight(searcher, needsScores); + 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); + } + final Weight fromWeight = fromQuery.createWeight(searcher, needsScores); return new Weight(TermsIncludingScoreQuery.this) { @Override @@ -115,7 +105,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(); @@ -135,17 +125,17 @@ class TermsIncludingScoreQuery extends Query { @Override public float getValueForNormalization() throws IOException { - return originalWeight.getValueForNormalization(); + return fromWeight.getValueForNormalization(); } @Override public void normalize(float norm, float boost) { - originalWeight.normalize(norm, boost); + fromWeight.normalize(norm, boost); } @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; } @@ -163,7 +153,7 @@ class TermsIncludingScoreQuery extends Query { }; } - + class SVInOrderScorer extends Scorer { final DocIdSetIterator matchingDocsIterator; @@ -249,23 +239,5 @@ 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()+" "); - try { - out.print(Long.toHexString(LegacyNumericUtils.prefixCodedToLong(ref))+"L"); - } catch (Exception e) { - try { - out.print(Integer.toHexString(LegacyNumericUtils.prefixCodedToInt(ref))+"i"); - } catch (Exception ee) { - } - } - out.println(" score="+scores[ords[i]]); - out.println(""); - } - } + } http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java ---------------------------------------------------------------------- diff --git a/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java b/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java index 63561c3..3ff0a5c 100644 --- a/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java +++ b/lucene/join/src/java/org/apache/lucene/search/join/TermsQuery.java @@ -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 { http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java ---------------------------------------------------------------------- diff --git a/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java b/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java index 9452847..e089cd5 100644 --- a/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java +++ b/lucene/join/src/test/org/apache/lucene/search/join/TestJoinUtil.java @@ -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; @@ -934,6 +935,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 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 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 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 { http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7f625676/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPNoScore.java ---------------------------------------------------------------------- diff --git a/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPNoScore.java b/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPNoScore.java index e36f37b..0ee7bdb 100644 --- a/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPNoScore.java +++ b/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPNoScore.java @@ -199,6 +199,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{