mkhludnev commented on code in PR #4749:
URL: https://github.com/apache/solr/pull/4749#discussion_r3825787793


##########
solr/core/src/java/org/apache/solr/search/join/aijoin/AIJoinQuery.java:
##########
@@ -0,0 +1,252 @@
+/*
+ * 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.
+ */
+package org.apache.solr.search.join.aijoin;
+
+import static org.apache.solr.search.join.aijoin.AIJoinUtil.cacheImpl;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.function.Predicate;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.internal.hppc.IntHashSet;
+import org.apache.lucene.search.BulkScorer;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.QueryVisitor;
+import org.apache.lucene.search.ScoreMode;
+import org.apache.lucene.search.ScorerSupplier;
+import org.apache.lucene.search.Weight;
+import org.apache.solr.search.join.aijoin.AIJoinIndex.JoinSegmentReference;
+import org.jspecify.annotations.NonNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Joins the from-side index to the to-side index this query is executed 
against, resolving
+ * from-side docs matching {@code fromQuery} to to-side docs through the 
auxiliary join index
+ * managed by {@link AIJoinIndex}: there, each (from-segment, to-segment) pair 
owns a SORTED_NUMERIC
+ * column named by both sides' persistent keys, whose doc number is the 
from-side doc id and whose
+ * value is the matching to-side doc id. Pair columns missing from the join 
index are built on
+ * demand at weight creation, so no explicit build step exists; obtain 
instances via {@link
+ * AIJoinIndex#newJoinQuery}. Matches score a constant.
+ */
+class AIJoinQuery extends Query {
+  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  final AIJoinIndex joinIndex;
+  final String fromField;
+  final Query fromQuery;
+  protected final IndexSearcher fromSearcher;
+  final String toField;
+  private final ExecutorService fromExecutorService;
+
+  AIJoinQuery(
+      AIJoinIndex joinIndex,
+      String fromField,
+      Query fromQuery,
+      IndexSearcher fromSearcher,
+      String toField,
+      ExecutorService fromExecutorService) {
+    this.joinIndex = Objects.requireNonNull(joinIndex, "joinIndex");
+    this.fromField = Objects.requireNonNull(fromField, "fromField");
+    this.fromQuery = Objects.requireNonNull(fromQuery, "fromQuery");
+    this.fromSearcher = Objects.requireNonNull(fromSearcher, "fromSearcher");
+    this.toField = Objects.requireNonNull(toField, "toField");
+    this.fromExecutorService = fromExecutorService;
+  }
+
+  private AIJoinUtil.CacheAndCount computeDocIdSet(Weight fromWeight, 
LeafReaderContext ctx)
+      throws IOException {
+    // TODO figure out how to steal cached from side filters
+    //    if (fromWeight!=null && 
fromWeight.getClass().getSimpleName().contains("Caching") ){
+    //      System.out.println("fromWeight is CachingWeight");
+    //    }
+    ScorerSupplier supplier = fromWeight.scorerSupplier(ctx);
+    if (supplier == null) {
+      return null; // NO matches ???
+    }
+    // TODO handle already cached WeightWrapper
+    BulkScorer scorer = supplier.bulkScorer();
+    return cacheImpl(scorer, ctx.reader().maxDoc(), 
ctx.reader().getLiveDocs());
+  }
+
+  @SuppressWarnings("ReferenceEquality")
+  @Override
+  public Query rewrite(IndexSearcher indexSearcher) throws IOException {
+    // the from-side selection rewrites against the from-side searcher, not 
against the (to-side)
+    // searcher this query is executed with
+    Query rewrittenFrom = fromQuery.rewrite(fromSearcher);
+    if (rewrittenFrom != fromQuery) { // TODO check MatchNoDocs ?
+      return new AIJoinQuery(
+          joinIndex, fromField, rewrittenFrom, fromSearcher, toField, 
fromExecutorService);
+    }
+    return super.rewrite(indexSearcher);
+  }
+
+  @Override
+  public Weight createWeight(IndexSearcher toSideSearcher, ScoreMode 
scoreMode, float boost)
+      throws IOException {
+    @NonNull Map<String, AIJoinIndex.SegmentsTuple> neededPairs =
+        getRequiredColumNames(toSideSearcher);
+
+    joinIndex.onCreateWeight(neededPairs.keySet(), fromSearcher, 
toSideSearcher); // ignoring fields
+    //
+    // DON'T write'em upfront
+    //
+    // WAS:
+    // joinIndex.ensureJoinSegments(neededPairs, fromSearcher, fromField, 
toSideSearcher, toField);
+    Predicate<String> isNeeded = neededPairs::containsKey;
+
+    Map<String, JoinSegmentReference> existingJoinSegments;
+    IndexSearcher joinSearcher = this.joinIndex.acquire();
+    try {
+      existingJoinSegments = 
AIJoinIndex.extractExistingJoinColumns(joinSearcher, isNeeded);
+    } finally {
+      this.joinIndex.release(joinSearcher);
+    }
+    int pairsNeeded = neededPairs.size();
+    neededPairs.keySet().removeAll(existingJoinSegments.keySet());
+    IntHashSet fromOrdsToLoad = new IntHashSet(neededPairs.size());
+    neededPairs.values().stream()
+        .mapToInt(AIJoinIndex.SegmentsTuple::fromLeafOrd)
+        .forEach(fromOrdsToLoad::add);
+    if (AIJoinUtil.diagnosticsEnabled(log)) {
+      // pairsMissing > 0 on a repeat query means those pairs were never 
persisted by a previous
+      // run (writeBatch never captured them), so their from-segments' FK 
columns get reloaded
+      // here; pairsClaimed counts missing pairs some build already 
claimed/completed in-process,
+      // i.e. reloads that are pure waste
+      AIJoinUtil.logDiagnostic(
+          log,
+          "AIJOIN evt=weight pairsNeeded={} pairsExisting={} pairsMissing={} 
pairsClaimed={}"
+              + " fkOrdsToLoad={} missingPairs={}",
+          pairsNeeded,
+          existingJoinSegments.size(),
+          neededPairs.size(),
+          joinIndex.countClaimedBuilds(neededPairs.keySet()),
+          fromOrdsToLoad.size(),
+          neededPairs.keySet());
+    }
+    Future<FromLeafJoinContext>[] fromFutures = loadFromSide(fromOrdsToLoad);
+    // TODO this might produce too many small tasks
+    return new AIJoinWeight(
+        this,
+        joinSearcher,
+        existingJoinSegments,

Review Comment:
   if searcher  [hasn't 
changed](https://github.com/mkhludnev/solr/blob/670de4ba9353ab0b39df9511af258786a55099cf/solr/core/src/java/org/apache/solr/search/join/aijoin/ToLeafJoinContext.java#L607)
 after reopen, it reads its' objects legitimately 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to