You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ge...@apache.org on 2020/12/22 13:33:15 UTC

[lucene-solr] branch master updated: SOLR-15049: Add TopLevelJoinQuery optimization for 'self-joins' (#2146)

This is an automated email from the ASF dual-hosted git repository.

gerlowskija pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git


The following commit(s) were added to refs/heads/master by this push:
     new 8b272a0  SOLR-15049: Add TopLevelJoinQuery optimization for 'self-joins' (#2146)
8b272a0 is described below

commit 8b272a0960b619664ae9abe4ea2812330f0b2d5d
Author: Jason Gerlowski <ge...@apache.org>
AuthorDate: Tue Dec 22 08:32:52 2020 -0500

    SOLR-15049: Add TopLevelJoinQuery optimization for 'self-joins' (#2146)
---
 solr/CHANGES.txt                                   |  2 ++
 .../org/apache/solr/search/JoinQParserPlugin.java  | 14 ++++++++++-
 .../org/apache/solr/search/TopLevelJoinQuery.java  | 28 ++++++++++++++++++++--
 3 files changed, 41 insertions(+), 3 deletions(-)

diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 3648914..4a53a8c 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -230,6 +230,8 @@ Optimizations
 ---------------------
 * SOLR-14975: Optimize CoreContainer.getAllCoreNames, getLoadedCoreNames and getCoreDescriptors. (Bruno Roustant)
 
+* SOLR-15049: Optimize same-core, same-field joins in TopLevelJoinQuery (Jason Gerlowski)
+
 Bug Fixes
 ---------------------
 * SOLR-14946: Fix responseHeader being returned in response when omitHeader=true and EmbeddedSolrServer is used
diff --git a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
index 5149b02..952a1eb 100644
--- a/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/JoinQParserPlugin.java
@@ -90,7 +90,7 @@ public class JoinQParserPlugin extends QParserPlugin {
       @Override
       Query makeFilter(QParser qparser, JoinQParserPlugin plugin) throws SyntaxError {
         final JoinParams jParams = parseJoin(qparser);
-        final JoinQuery q = new TopLevelJoinQuery(jParams.fromField, jParams.toField, jParams.fromCore, jParams.fromQuery);
+        final JoinQuery q = createTopLevelJoin(jParams);
         q.fromCoreOpenTime = jParams.fromCoreOpenTime;
         return q;
       }
@@ -99,6 +99,18 @@ public class JoinQParserPlugin extends QParserPlugin {
       Query makeJoinDirectFromParams(JoinParams jParams) {
         return new TopLevelJoinQuery(jParams.fromField, jParams.toField, null, jParams.fromQuery);
       }
+
+      private JoinQuery createTopLevelJoin(JoinParams jParams) {
+        if (isSelfJoin(jParams)) {
+          return new TopLevelJoinQuery.SelfJoin(jParams.fromField, jParams.fromQuery);
+        }
+        return new TopLevelJoinQuery(jParams.fromField, jParams.toField, jParams.fromCore, jParams.fromQuery);
+      }
+
+      private boolean isSelfJoin(JoinParams jparams) {
+        return jparams.fromCore == null &&
+                (jparams.fromField != null && jparams.fromField.equals(jparams.toField));
+      }
     },
     crossCollection {
       @Override
diff --git a/solr/core/src/java/org/apache/solr/search/TopLevelJoinQuery.java b/solr/core/src/java/org/apache/solr/search/TopLevelJoinQuery.java
index 428c229..8ae1d48 100644
--- a/solr/core/src/java/org/apache/solr/search/TopLevelJoinQuery.java
+++ b/solr/core/src/java/org/apache/solr/search/TopLevelJoinQuery.java
@@ -162,7 +162,7 @@ public class TopLevelJoinQuery extends JoinQuery {
     return fromOrdBitSet;
   }
 
-  private BitsetBounds convertFromOrdinalsIntoToField(LongBitSet fromOrdBitSet, SortedSetDocValues fromDocValues,
+  protected BitsetBounds convertFromOrdinalsIntoToField(LongBitSet fromOrdBitSet, SortedSetDocValues fromDocValues,
                                                       LongBitSet toOrdBitSet, SortedSetDocValues toDocValues) throws IOException {
     long fromOrdinal = 0;
     long firstToOrd = BitsetBounds.NO_MATCHES;
@@ -208,7 +208,7 @@ public class TopLevelJoinQuery extends JoinQuery {
     return -(low + 1);  // key not found.
   }
 
-  private static class BitsetBounds {
+  protected static class BitsetBounds {
     public static final long NO_MATCHES = -1L;
     public final long lower;
     public final long upper;
@@ -218,4 +218,28 @@ public class TopLevelJoinQuery extends JoinQuery {
       this.upper = upper;
     }
   }
+
+  /**
+   * A {@link TopLevelJoinQuery} implementation optimized for when 'from' and 'to' cores and fields match and no ordinal-
+   * conversion is necessary.
+   */
+  static class SelfJoin extends TopLevelJoinQuery {
+    public SelfJoin(String joinField, Query subQuery) {
+      super(joinField, joinField, null, subQuery);
+    }
+
+    protected BitsetBounds convertFromOrdinalsIntoToField(LongBitSet fromOrdBitSet, SortedSetDocValues fromDocValues,
+                                                          LongBitSet toOrdBitSet, SortedSetDocValues toDocValues) throws IOException {
+
+      // 'from' and 'to' ordinals are identical for self-joins.
+      toOrdBitSet.or(fromOrdBitSet);
+
+      // Calculate boundary ords used for other optimizations
+      final long firstToOrd = toOrdBitSet.nextSetBit(0);
+      final long lastToOrd = toOrdBitSet.prevSetBit(toOrdBitSet.length() - 1);
+      return new BitsetBounds(firstToOrd, lastToOrd);
+    }
+  }
 }
+
+