You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ro...@apache.org on 2021/05/25 11:07:29 UTC

[lucene] branch main updated: LUCENE-9204: Make ConjunctionDISI package-private and add ConjunctionUtils factory class (#148)

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

romseygeek pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/lucene.git


The following commit(s) were added to refs/heads/main by this push:
     new 5e0e7a5  LUCENE-9204: Make ConjunctionDISI package-private and add ConjunctionUtils factory class (#148)
5e0e7a5 is described below

commit 5e0e7a5479bca798ccfe385629a0ca2ba5870bc0
Author: Alan Woodward <ro...@apache.org>
AuthorDate: Tue May 25 12:07:20 2021 +0100

    LUCENE-9204: Make ConjunctionDISI package-private and add ConjunctionUtils factory class (#148)
    
    ConjunctionDISI is really an internal implementation of DocIdSetIterator,
    and would ideally be package-private. However, it is used in a few
    other places:
    * directly in ConjunctionSpans
    * as a utility in the facet and join modules
    
    This commit adds a public helper class ConjunctionUtils that allows easy
    intersection of iterators for use by other modules. This means that
    ConjunctionDISI itself can become package-private. It also removes
    a reference to Spans from core classes, which will make it easier to
    migrate Spans to the queries module.  ConjuctionSpans and
    ConjunctionIntervalIterator now use the public Utils class, and intervals
    no longer need their own ConjunctionDISI implementation.
---
 .../org/apache/lucene/search/ConjunctionDISI.java  |  77 +-----------
 .../apache/lucene/search/ConjunctionScorer.java    |   5 +-
 .../org/apache/lucene/search/ConjunctionUtils.java | 100 ++++++++++++++++
 .../apache/lucene/search/ExactPhraseMatcher.java   |   2 +-
 .../apache/lucene/search/SloppyPhraseMatcher.java  |   2 +-
 .../src/java/org/apache/lucene/search/Weight.java  |   2 +-
 .../lucene/search/spans/ConjunctionSpans.java      |  28 ++++-
 .../apache/lucene/search/TestConjunctionDISI.java  |  12 +-
 .../apache/lucene/facet/LongValueFacetCounts.java  |   7 +-
 .../lucene/facet/StringValueFacetCounts.java       |   4 +-
 .../ConcurrentSortedSetDocValuesFacetCounts.java   |   4 +-
 .../sortedset/SortedSetDocValuesFacetCounts.java   |   4 +-
 .../facet/taxonomy/FastTaxonomyFacetCounts.java    |   4 +-
 .../lucene/search/join/ToParentDocValues.java      |   4 +-
 .../lucene/queries/intervals/ConjunctionDISI.java  | 129 ---------------------
 .../intervals/ConjunctionIntervalIterator.java     |   3 +-
 16 files changed, 157 insertions(+), 230 deletions(-)

diff --git a/lucene/core/src/java/org/apache/lucene/search/ConjunctionDISI.java b/lucene/core/src/java/org/apache/lucene/search/ConjunctionDISI.java
index 9fb778d..e00bc75 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConjunctionDISI.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConjunctionDISI.java
@@ -22,7 +22,6 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
-import org.apache.lucene.search.spans.Spans;
 import org.apache.lucene.util.ArrayUtil;
 import org.apache.lucene.util.BitSet;
 import org.apache.lucene.util.BitSetIterator;
@@ -32,71 +31,16 @@ import org.apache.lucene.util.CollectionUtil;
  * A conjunction of DocIdSetIterators. Requires that all of its sub-iterators must be on the same
  * document all the time. This iterates over the doc ids that are present in each given
  * DocIdSetIterator. <br>
- * Public only for use in {@link org.apache.lucene.search.spans}.
  *
  * @lucene.internal
  */
-public final class ConjunctionDISI extends DocIdSetIterator {
-
-  /**
-   * Create a conjunction over the provided {@link Scorer}s. Note that the returned {@link
-   * DocIdSetIterator} might leverage two-phase iteration in which case it is possible to retrieve
-   * the {@link TwoPhaseIterator} using {@link TwoPhaseIterator#unwrap}.
-   */
-  public static DocIdSetIterator intersectScorers(Collection<Scorer> scorers) {
-    if (scorers.size() < 2) {
-      throw new IllegalArgumentException("Cannot make a ConjunctionDISI of less than 2 iterators");
-    }
-    final List<DocIdSetIterator> allIterators = new ArrayList<>();
-    final List<TwoPhaseIterator> twoPhaseIterators = new ArrayList<>();
-    for (Scorer scorer : scorers) {
-      addScorer(scorer, allIterators, twoPhaseIterators);
-    }
-
-    return createConjunction(allIterators, twoPhaseIterators);
-  }
-
-  /**
-   * Create a conjunction over the provided DocIdSetIterators. Note that the returned {@link
-   * DocIdSetIterator} might leverage two-phase iteration in which case it is possible to retrieve
-   * the {@link TwoPhaseIterator} using {@link TwoPhaseIterator#unwrap}.
-   */
-  public static DocIdSetIterator intersectIterators(List<DocIdSetIterator> iterators) {
-    if (iterators.size() < 2) {
-      throw new IllegalArgumentException("Cannot make a ConjunctionDISI of less than 2 iterators");
-    }
-    final List<DocIdSetIterator> allIterators = new ArrayList<>();
-    final List<TwoPhaseIterator> twoPhaseIterators = new ArrayList<>();
-    for (DocIdSetIterator iterator : iterators) {
-      addIterator(iterator, allIterators, twoPhaseIterators);
-    }
-
-    return createConjunction(allIterators, twoPhaseIterators);
-  }
-
-  /**
-   * Create a conjunction over the provided {@link Spans}. Note that the returned {@link
-   * DocIdSetIterator} might leverage two-phase iteration in which case it is possible to retrieve
-   * the {@link TwoPhaseIterator} using {@link TwoPhaseIterator#unwrap}.
-   */
-  public static DocIdSetIterator intersectSpans(List<Spans> spanList) {
-    if (spanList.size() < 2) {
-      throw new IllegalArgumentException("Cannot make a ConjunctionDISI of less than 2 iterators");
-    }
-    final List<DocIdSetIterator> allIterators = new ArrayList<>();
-    final List<TwoPhaseIterator> twoPhaseIterators = new ArrayList<>();
-    for (Spans spans : spanList) {
-      addSpans(spans, allIterators, twoPhaseIterators);
-    }
-
-    return createConjunction(allIterators, twoPhaseIterators);
-  }
+final class ConjunctionDISI extends DocIdSetIterator {
 
   /**
    * Adds the scorer, possibly splitting up into two phases or collapsing if it is another
    * conjunction
    */
-  private static void addScorer(
+  static void addScorer(
       Scorer scorer,
       List<DocIdSetIterator> allIterators,
       List<TwoPhaseIterator> twoPhaseIterators) {
@@ -108,18 +52,7 @@ public final class ConjunctionDISI extends DocIdSetIterator {
     }
   }
 
-  /** Adds the Spans. */
-  private static void addSpans(
-      Spans spans, List<DocIdSetIterator> allIterators, List<TwoPhaseIterator> twoPhaseIterators) {
-    TwoPhaseIterator twoPhaseIter = spans.asTwoPhaseIterator();
-    if (twoPhaseIter != null) {
-      addTwoPhaseIterator(twoPhaseIter, allIterators, twoPhaseIterators);
-    } else { // no approximation support, use the iterator as-is
-      addIterator(spans, allIterators, twoPhaseIterators);
-    }
-  }
-
-  private static void addIterator(
+  static void addIterator(
       DocIdSetIterator disi,
       List<DocIdSetIterator> allIterators,
       List<TwoPhaseIterator> twoPhaseIterators) {
@@ -144,7 +77,7 @@ public final class ConjunctionDISI extends DocIdSetIterator {
     }
   }
 
-  private static void addTwoPhaseIterator(
+  static void addTwoPhaseIterator(
       TwoPhaseIterator twoPhaseIter,
       List<DocIdSetIterator> allIterators,
       List<TwoPhaseIterator> twoPhaseIterators) {
@@ -158,7 +91,7 @@ public final class ConjunctionDISI extends DocIdSetIterator {
     }
   }
 
-  private static DocIdSetIterator createConjunction(
+  static DocIdSetIterator createConjunction(
       List<DocIdSetIterator> allIterators, List<TwoPhaseIterator> twoPhaseIterators) {
 
     // check that all sub-iterators are on the same doc ID
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
index b0a1a9a..34b00a4 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ConjunctionScorer.java
@@ -31,11 +31,10 @@ class ConjunctionScorer extends Scorer {
    * Create a new {@link ConjunctionScorer}, note that {@code scorers} must be a subset of {@code
    * required}.
    */
-  ConjunctionScorer(Weight weight, Collection<Scorer> required, Collection<Scorer> scorers)
-      throws IOException {
+  ConjunctionScorer(Weight weight, Collection<Scorer> required, Collection<Scorer> scorers) {
     super(weight);
     assert required.containsAll(scorers);
-    this.disi = ConjunctionDISI.intersectScorers(required);
+    this.disi = ConjunctionUtils.intersectScorers(required);
     this.scorers = scorers.toArray(new Scorer[scorers.size()]);
     this.required = required;
   }
diff --git a/lucene/core/src/java/org/apache/lucene/search/ConjunctionUtils.java b/lucene/core/src/java/org/apache/lucene/search/ConjunctionUtils.java
new file mode 100644
index 0000000..1b4c442
--- /dev/null
+++ b/lucene/core/src/java/org/apache/lucene/search/ConjunctionUtils.java
@@ -0,0 +1,100 @@
+/*
+ * 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.lucene.search;
+
+import static org.apache.lucene.search.ConjunctionDISI.addScorer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/** Helper methods for building conjunction iterators */
+public final class ConjunctionUtils {
+
+  /**
+   * Create a conjunction over the provided {@link Scorer}s. Note that the returned {@link
+   * DocIdSetIterator} might leverage two-phase iteration in which case it is possible to retrieve
+   * the {@link TwoPhaseIterator} using {@link TwoPhaseIterator#unwrap}.
+   */
+  public static DocIdSetIterator intersectScorers(Collection<Scorer> scorers) {
+    if (scorers.size() < 2) {
+      throw new IllegalArgumentException("Cannot make a ConjunctionDISI of less than 2 iterators");
+    }
+    final List<DocIdSetIterator> allIterators = new ArrayList<>();
+    final List<TwoPhaseIterator> twoPhaseIterators = new ArrayList<>();
+    for (Scorer scorer : scorers) {
+      addScorer(scorer, allIterators, twoPhaseIterators);
+    }
+
+    return ConjunctionDISI.createConjunction(allIterators, twoPhaseIterators);
+  }
+
+  /**
+   * Create a conjunction over the provided DocIdSetIterators. Note that the returned {@link
+   * DocIdSetIterator} might leverage two-phase iteration in which case it is possible to retrieve
+   * the {@link TwoPhaseIterator} using {@link TwoPhaseIterator#unwrap}.
+   */
+  public static DocIdSetIterator intersectIterators(List<? extends DocIdSetIterator> iterators) {
+    if (iterators.size() < 2) {
+      throw new IllegalArgumentException("Cannot make a ConjunctionDISI of less than 2 iterators");
+    }
+    final List<DocIdSetIterator> allIterators = new ArrayList<>();
+    final List<TwoPhaseIterator> twoPhaseIterators = new ArrayList<>();
+    for (DocIdSetIterator iterator : iterators) {
+      addIterator(iterator, allIterators, twoPhaseIterators);
+    }
+
+    return ConjunctionDISI.createConjunction(allIterators, twoPhaseIterators);
+  }
+
+  /**
+   * Create a conjunction over the provided set of DocIdSetIterators and TwoPhaseIterators, using
+   * two-phase iterator where possible. Note that the returned {@link DocIdSetIterator} might
+   * leverage two-phase iteration in which case it is possible to retrieve the {@link
+   * TwoPhaseIterator} using {@link TwoPhaseIterator#unwrap}.
+   *
+   * @param allIterators a list of DocIdSetIterators to combine
+   * @param twoPhaseIterators a list of TwoPhaseIterators to combine
+   */
+  public static DocIdSetIterator createConjunction(
+      List<DocIdSetIterator> allIterators, List<TwoPhaseIterator> twoPhaseIterators) {
+    return ConjunctionDISI.createConjunction(allIterators, twoPhaseIterators);
+  }
+
+  /**
+   * Given a two-phase iterator, find any sub-iterators and add them to the provided
+   * DocIdSetIterator and TwoPhaseIterator lists
+   */
+  public static void addTwoPhaseIterator(
+      TwoPhaseIterator twoPhaseIter,
+      List<DocIdSetIterator> allIterators,
+      List<TwoPhaseIterator> twoPhaseIterators) {
+    ConjunctionDISI.addTwoPhaseIterator(twoPhaseIter, allIterators, twoPhaseIterators);
+  }
+
+  /**
+   * Given a DocIdSetIterator, find any sub-iterators or two-phase iterators and add them to the
+   * provided DocIdSetIterator and TwoPhaseIterator lists
+   */
+  public static void addIterator(
+      DocIdSetIterator disi,
+      List<DocIdSetIterator> allIterators,
+      List<TwoPhaseIterator> twoPhaseIterators) {
+    ConjunctionDISI.addIterator(disi, allIterators, twoPhaseIterators);
+  }
+}
diff --git a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseMatcher.java b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseMatcher.java
index 9818af9..ca125e7 100644
--- a/lucene/core/src/java/org/apache/lucene/search/ExactPhraseMatcher.java
+++ b/lucene/core/src/java/org/apache/lucene/search/ExactPhraseMatcher.java
@@ -62,7 +62,7 @@ public final class ExactPhraseMatcher extends PhraseMatcher {
     super(matchCost);
 
     final DocIdSetIterator approximation =
-        ConjunctionDISI.intersectIterators(
+        ConjunctionUtils.intersectIterators(
             Arrays.stream(postings).map(p -> p.postings).collect(Collectors.toList()));
     final ImpactsSource impactsSource =
         mergeImpacts(Arrays.stream(postings).map(p -> p.impacts).toArray(ImpactsEnum[]::new));
diff --git a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseMatcher.java b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseMatcher.java
index 4af3193..259ea8f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseMatcher.java
+++ b/lucene/core/src/java/org/apache/lucene/search/SloppyPhraseMatcher.java
@@ -100,7 +100,7 @@ public final class SloppyPhraseMatcher extends PhraseMatcher {
     }
 
     approximation =
-        ConjunctionDISI.intersectIterators(
+        ConjunctionUtils.intersectIterators(
             Arrays.stream(postings).map(p -> p.postings).collect(Collectors.toList()));
     // What would be a good upper bound of the sloppy frequency? A sum of the
     // sub frequencies would be correct, but it is usually so much higher than
diff --git a/lucene/core/src/java/org/apache/lucene/search/Weight.java b/lucene/core/src/java/org/apache/lucene/search/Weight.java
index 7d42942..df1fa34 100644
--- a/lucene/core/src/java/org/apache/lucene/search/Weight.java
+++ b/lucene/core/src/java/org/apache/lucene/search/Weight.java
@@ -215,7 +215,7 @@ public abstract class Weight implements SegmentCacheable {
         }
         // filter scorerIterator to keep only competitive docs as defined by collector
         filteredIterator =
-            ConjunctionDISI.intersectIterators(Arrays.asList(scorerIterator, collectorIterator));
+            ConjunctionUtils.intersectIterators(Arrays.asList(scorerIterator, collectorIterator));
       }
       if (filteredIterator.docID() == -1 && min == 0 && max == DocIdSetIterator.NO_MORE_DOCS) {
         scoreAll(collector, filteredIterator, twoPhase, acceptDocs);
diff --git a/lucene/core/src/java/org/apache/lucene/search/spans/ConjunctionSpans.java b/lucene/core/src/java/org/apache/lucene/search/spans/ConjunctionSpans.java
index 290705a..f32052c 100644
--- a/lucene/core/src/java/org/apache/lucene/search/spans/ConjunctionSpans.java
+++ b/lucene/core/src/java/org/apache/lucene/search/spans/ConjunctionSpans.java
@@ -17,8 +17,9 @@
 package org.apache.lucene.search.spans;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
-import org.apache.lucene.search.ConjunctionDISI;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.TwoPhaseIterator;
 
@@ -35,7 +36,7 @@ abstract class ConjunctionSpans extends Spans {
       throw new IllegalArgumentException("Less than 2 subSpans.size():" + subSpans.size());
     }
     this.subSpans = subSpans.toArray(new Spans[subSpans.size()]);
-    this.conjunction = ConjunctionDISI.intersectSpans(subSpans);
+    this.conjunction = intersectSpans(subSpans);
     this.atFirstInCurrentDoc = true; // ensure for doc -1 that start/end positions are -1
   }
 
@@ -109,4 +110,27 @@ abstract class ConjunctionSpans extends Spans {
   public Spans[] getSubSpans() {
     return subSpans;
   }
+
+  private static DocIdSetIterator intersectSpans(List<Spans> spanList) {
+    if (spanList.size() < 2) {
+      throw new IllegalArgumentException("Cannot make a ConjunctionDISI of less than 2 iterators");
+    }
+    final List<DocIdSetIterator> allIterators = new ArrayList<>();
+    final List<TwoPhaseIterator> twoPhaseIterators = new ArrayList<>();
+    for (Spans spans : spanList) {
+      addSpans(spans, allIterators, twoPhaseIterators);
+    }
+
+    return ConjunctionUtils.createConjunction(allIterators, twoPhaseIterators);
+  }
+
+  private static void addSpans(
+      Spans spans, List<DocIdSetIterator> allIterators, List<TwoPhaseIterator> twoPhaseIterators) {
+    TwoPhaseIterator twoPhaseIter = spans.asTwoPhaseIterator();
+    if (twoPhaseIter != null) {
+      ConjunctionUtils.addTwoPhaseIterator(twoPhaseIter, allIterators, twoPhaseIterators);
+    } else { // no approximation support, use the iterator as-is
+      ConjunctionUtils.addIterator(spans, allIterators, twoPhaseIterators);
+    }
+  }
 }
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestConjunctionDISI.java b/lucene/core/src/test/org/apache/lucene/search/TestConjunctionDISI.java
index a5677b0..7d5b46a 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestConjunctionDISI.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestConjunctionDISI.java
@@ -261,7 +261,7 @@ public class TestConjunctionDISI extends LuceneTestCase {
       }
 
       final DocIdSetIterator conjunction =
-          ConjunctionDISI.intersectScorers(Arrays.asList(iterators));
+          ConjunctionUtils.intersectScorers(Arrays.asList(iterators));
       assertEquals(intersect(sets), toBitSet(maxDoc, conjunction));
     }
   }
@@ -298,7 +298,7 @@ public class TestConjunctionDISI extends LuceneTestCase {
       }
 
       final DocIdSetIterator conjunction =
-          ConjunctionDISI.intersectScorers(Arrays.asList(iterators));
+          ConjunctionUtils.intersectScorers(Arrays.asList(iterators));
       TwoPhaseIterator twoPhaseIterator = TwoPhaseIterator.unwrap(conjunction);
       assertEquals(hasApproximation, twoPhaseIterator != null);
       if (hasApproximation) {
@@ -354,7 +354,7 @@ public class TestConjunctionDISI extends LuceneTestCase {
           conjunction = newIterator;
         } else {
           final DocIdSetIterator conj =
-              ConjunctionDISI.intersectScorers(Arrays.asList(conjunction, newIterator));
+              ConjunctionUtils.intersectScorers(Arrays.asList(conjunction, newIterator));
           conjunction = scorer(conj, TwoPhaseIterator.unwrap(conj));
         }
       }
@@ -412,7 +412,7 @@ public class TestConjunctionDISI extends LuceneTestCase {
                   new FakeWeight(),
                   0f,
                   ScoreMode.TOP_SCORES,
-                  ConjunctionDISI.intersectScorers(subIterators));
+                  ConjunctionUtils.intersectScorers(subIterators));
         }
         scorers.set(subSeqStart, subConjunction);
         int toRemove = subSeqEnd - subSeqStart - 1;
@@ -427,7 +427,7 @@ public class TestConjunctionDISI extends LuceneTestCase {
                 new FakeWeight(), 0f, ScoreMode.TOP_SCORES, DocIdSetIterator.all(maxDoc)));
       }
 
-      final DocIdSetIterator conjunction = ConjunctionDISI.intersectScorers(scorers);
+      final DocIdSetIterator conjunction = ConjunctionUtils.intersectScorers(scorers);
       assertEquals(intersect(sets), toBitSet(maxDoc, conjunction));
     }
   }
@@ -451,7 +451,7 @@ public class TestConjunctionDISI extends LuceneTestCase {
       iterators[i] = new BitDocIdSet(set).iterator();
     }
     final DocIdSetIterator conjunction =
-        ConjunctionDISI.intersectIterators(Arrays.asList(iterators));
+        ConjunctionUtils.intersectIterators(Arrays.asList(iterators));
     int idx = TestUtil.nextInt(random(), 0, iterators.length - 1);
     iterators[idx]
         .nextDoc(); // illegally advancing one of the sub-iterators outside of the conjunction
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/LongValueFacetCounts.java b/lucene/facet/src/java/org/apache/lucene/facet/LongValueFacetCounts.java
index cd331ac..c6acbcb 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/LongValueFacetCounts.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/LongValueFacetCounts.java
@@ -30,7 +30,7 @@ import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.NumericDocValues;
 import org.apache.lucene.index.SortedNumericDocValues;
-import org.apache.lucene.search.ConjunctionDISI;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.LongValues;
 import org.apache.lucene.search.LongValuesSource;
@@ -138,7 +138,6 @@ public class LongValueFacetCounts extends Facets {
 
   /** Counts from the field's indexed doc values. */
   private void count(String field, List<MatchingDocs> matchingDocs) throws IOException {
-
     for (MatchingDocs hits : matchingDocs) {
 
       SortedNumericDocValues multiValues = DocValues.getSortedNumeric(hits.context.reader(), field);
@@ -147,7 +146,7 @@ public class LongValueFacetCounts extends Facets {
       if (singleValues != null) {
 
         DocIdSetIterator it =
-            ConjunctionDISI.intersectIterators(Arrays.asList(hits.bits.iterator(), singleValues));
+            ConjunctionUtils.intersectIterators(Arrays.asList(hits.bits.iterator(), singleValues));
 
         for (int doc = it.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = it.nextDoc()) {
           increment(singleValues.longValue());
@@ -156,7 +155,7 @@ public class LongValueFacetCounts extends Facets {
       } else {
 
         DocIdSetIterator it =
-            ConjunctionDISI.intersectIterators(Arrays.asList(hits.bits.iterator(), multiValues));
+            ConjunctionUtils.intersectIterators(Arrays.asList(hits.bits.iterator(), multiValues));
 
         for (int doc = it.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = it.nextDoc()) {
           int limit = multiValues.docValueCount();
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/StringValueFacetCounts.java b/lucene/facet/src/java/org/apache/lucene/facet/StringValueFacetCounts.java
index 8b874bc..1c66042 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/StringValueFacetCounts.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/StringValueFacetCounts.java
@@ -29,7 +29,7 @@ import org.apache.lucene.index.MultiDocValues;
 import org.apache.lucene.index.OrdinalMap;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.index.SortedSetDocValues;
-import org.apache.lucene.search.ConjunctionDISI;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.util.BytesRef;
@@ -335,7 +335,7 @@ public class StringValueFacetCounts extends Facets {
     if (hits == null) {
       it = segValues;
     } else {
-      it = ConjunctionDISI.intersectIterators(Arrays.asList(hits.bits.iterator(), segValues));
+      it = ConjunctionUtils.intersectIterators(Arrays.asList(hits.bits.iterator(), segValues));
     }
 
     // TODO: yet another option is to count all segs
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/sortedset/ConcurrentSortedSetDocValuesFacetCounts.java b/lucene/facet/src/java/org/apache/lucene/facet/sortedset/ConcurrentSortedSetDocValuesFacetCounts.java
index 8e11176..7978832 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/sortedset/ConcurrentSortedSetDocValuesFacetCounts.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/sortedset/ConcurrentSortedSetDocValuesFacetCounts.java
@@ -44,7 +44,7 @@ import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues;
 import org.apache.lucene.index.OrdinalMap;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.index.SortedSetDocValues;
-import org.apache.lucene.search.ConjunctionDISI;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.util.BytesRef;
@@ -189,7 +189,7 @@ public class ConcurrentSortedSetDocValuesFacetCounts extends Facets {
         // count all
         it = segValues;
       } else {
-        it = ConjunctionDISI.intersectIterators(Arrays.asList(hits.bits.iterator(), segValues));
+        it = ConjunctionUtils.intersectIterators(Arrays.asList(hits.bits.iterator(), segValues));
       }
 
       if (ordinalMap != null) {
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java b/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java
index 5fe1b8c..2ea56c8 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/sortedset/SortedSetDocValuesFacetCounts.java
@@ -39,7 +39,7 @@ import org.apache.lucene.index.MultiDocValues.MultiSortedSetDocValues;
 import org.apache.lucene.index.OrdinalMap;
 import org.apache.lucene.index.ReaderUtil;
 import org.apache.lucene.index.SortedSetDocValues;
-import org.apache.lucene.search.ConjunctionDISI;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.util.BytesRef;
@@ -163,7 +163,7 @@ public class SortedSetDocValuesFacetCounts extends Facets {
     if (hits == null) {
       it = segValues;
     } else {
-      it = ConjunctionDISI.intersectIterators(Arrays.asList(hits.bits.iterator(), segValues));
+      it = ConjunctionUtils.intersectIterators(Arrays.asList(hits.bits.iterator(), segValues));
     }
 
     // TODO: yet another option is to count all segs
diff --git a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/FastTaxonomyFacetCounts.java b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/FastTaxonomyFacetCounts.java
index e20451e..4b6332b 100644
--- a/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/FastTaxonomyFacetCounts.java
+++ b/lucene/facet/src/java/org/apache/lucene/facet/taxonomy/FastTaxonomyFacetCounts.java
@@ -25,7 +25,7 @@ import org.apache.lucene.facet.FacetsConfig;
 import org.apache.lucene.index.BinaryDocValues;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.search.ConjunctionDISI;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.MatchAllDocsQuery;
 import org.apache.lucene.util.Bits;
@@ -76,7 +76,7 @@ public class FastTaxonomyFacetCounts extends IntTaxonomyFacets {
       }
 
       DocIdSetIterator it =
-          ConjunctionDISI.intersectIterators(Arrays.asList(hits.bits.iterator(), dv));
+          ConjunctionUtils.intersectIterators(Arrays.asList(hits.bits.iterator(), dv));
 
       for (int doc = it.nextDoc(); doc != DocIdSetIterator.NO_MORE_DOCS; doc = it.nextDoc()) {
         final BytesRef bytesRef = dv.binaryValue();
diff --git a/lucene/join/src/java/org/apache/lucene/search/join/ToParentDocValues.java b/lucene/join/src/java/org/apache/lucene/search/join/ToParentDocValues.java
index 409b7cb..bcf409e 100644
--- a/lucene/join/src/java/org/apache/lucene/search/join/ToParentDocValues.java
+++ b/lucene/join/src/java/org/apache/lucene/search/join/ToParentDocValues.java
@@ -20,7 +20,7 @@ import java.io.IOException;
 import java.util.Arrays;
 import org.apache.lucene.index.NumericDocValues;
 import org.apache.lucene.index.SortedDocValues;
-import org.apache.lucene.search.ConjunctionDISI;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.join.BlockJoinSelector.Type;
 import org.apache.lucene.util.BitSet;
@@ -177,7 +177,7 @@ class ToParentDocValues extends DocIdSetIterator {
   private ToParentDocValues(
       DocIdSetIterator values, BitSet parents, DocIdSetIterator children, Accumulator collect) {
     this.parents = parents;
-    childWithValues = ConjunctionDISI.intersectIterators(Arrays.asList(children, values));
+    childWithValues = ConjunctionUtils.intersectIterators(Arrays.asList(children, values));
     this.collector = collect;
   }
 
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/intervals/ConjunctionDISI.java b/lucene/queries/src/java/org/apache/lucene/queries/intervals/ConjunctionDISI.java
deleted file mode 100644
index 24bc58c..0000000
--- a/lucene/queries/src/java/org/apache/lucene/queries/intervals/ConjunctionDISI.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * 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.lucene.queries.intervals;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.lucene.util.CollectionUtil;
-
-/**
- * A conjunction of DocIdSetIterators. This iterates over the doc ids that are present in each given
- * DocIdSetIterator.
- *
- * @lucene.internal
- */
-final class ConjunctionDISI extends DocIdSetIterator {
-
-  /** Create a conjunction over the provided DocIdSetIterators. */
-  public static DocIdSetIterator intersectIterators(List<? extends DocIdSetIterator> iterators) {
-    if (iterators.size() < 2) {
-      throw new IllegalArgumentException("Cannot make a ConjunctionDISI of less than 2 iterators");
-    }
-    final List<DocIdSetIterator> allIterators = new ArrayList<>();
-    for (DocIdSetIterator iterator : iterators) {
-      addIterator(iterator, allIterators);
-    }
-
-    return new ConjunctionDISI(allIterators);
-  }
-
-  private static void addIterator(DocIdSetIterator disi, List<DocIdSetIterator> allIterators) {
-    if (disi.getClass() == ConjunctionDISI.class) { // Check for exactly this class for collapsing
-      ConjunctionDISI conjunction = (ConjunctionDISI) disi;
-      // subconjuctions have already split themselves into two phase iterators and others, so we can
-      // take those iterators as they are and move them up to this conjunction
-      allIterators.add(conjunction.lead1);
-      allIterators.add(conjunction.lead2);
-      Collections.addAll(allIterators, conjunction.others);
-    } else {
-      allIterators.add(disi);
-    }
-  }
-
-  final DocIdSetIterator lead1, lead2;
-  final DocIdSetIterator[] others;
-
-  private ConjunctionDISI(List<? extends DocIdSetIterator> iterators) {
-    assert iterators.size() >= 2;
-    // Sort the array the first time to allow the least frequent DocsEnum to
-    // lead the matching.
-    CollectionUtil.timSort(iterators, Comparator.comparingLong(DocIdSetIterator::cost));
-    lead1 = iterators.get(0);
-    lead2 = iterators.get(1);
-    others = iterators.subList(2, iterators.size()).toArray(new DocIdSetIterator[0]);
-  }
-
-  private int doNext(int doc) throws IOException {
-    advanceHead:
-    for (; ; ) {
-      assert doc == lead1.docID();
-
-      // find agreement between the two iterators with the lower costs
-      // we special case them because they do not need the
-      // 'other.docID() < doc' check that the 'others' iterators need
-      final int next2 = lead2.advance(doc);
-      if (next2 != doc) {
-        doc = lead1.advance(next2);
-        if (next2 != doc) {
-          continue;
-        }
-      }
-
-      // then find agreement with other iterators
-      for (DocIdSetIterator other : others) {
-        // other.doc may already be equal to doc if we "continued advanceHead"
-        // on the previous iteration and the advance on the lead scorer exactly matched.
-        if (other.docID() < doc) {
-          final int next = other.advance(doc);
-
-          if (next > doc) {
-            // iterator beyond the current doc - advance lead and continue to the new highest doc.
-            doc = lead1.advance(next);
-            continue advanceHead;
-          }
-        }
-      }
-
-      // success - all iterators are on the same doc
-      return doc;
-    }
-  }
-
-  @Override
-  public int advance(int target) throws IOException {
-    return doNext(lead1.advance(target));
-  }
-
-  @Override
-  public int docID() {
-    return lead1.docID();
-  }
-
-  @Override
-  public int nextDoc() throws IOException {
-    return doNext(lead1.nextDoc());
-  }
-
-  @Override
-  public long cost() {
-    return lead1.cost(); // overestimate
-  }
-}
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/intervals/ConjunctionIntervalIterator.java b/lucene/queries/src/java/org/apache/lucene/queries/intervals/ConjunctionIntervalIterator.java
index 8b54734..a670c34 100644
--- a/lucene/queries/src/java/org/apache/lucene/queries/intervals/ConjunctionIntervalIterator.java
+++ b/lucene/queries/src/java/org/apache/lucene/queries/intervals/ConjunctionIntervalIterator.java
@@ -19,6 +19,7 @@ package org.apache.lucene.queries.intervals;
 
 import java.io.IOException;
 import java.util.List;
+import org.apache.lucene.search.ConjunctionUtils;
 import org.apache.lucene.search.DocIdSetIterator;
 
 abstract class ConjunctionIntervalIterator extends IntervalIterator {
@@ -28,7 +29,7 @@ abstract class ConjunctionIntervalIterator extends IntervalIterator {
   final float cost;
 
   ConjunctionIntervalIterator(List<IntervalIterator> subIterators) {
-    this.approximation = ConjunctionDISI.intersectIterators(subIterators);
+    this.approximation = ConjunctionUtils.intersectIterators(subIterators);
     this.subIterators = subIterators;
     float costsum = 0;
     for (IntervalIterator it : subIterators) {