@Test
  public void testSvValues() throws IOException {
    int numDocs = 1000000;
    int numOrdinals = numDocs / 4;
    Map<Integer, Long> controlDocToOrdinal = new HashMap<>();
    OrdinalsBuilder builder = new OrdinalsBuilder(numDocs);
    long ordinal = builder.currentOrdinal();
    for (int doc = 0; doc < numDocs; doc++) {
      if (doc % numOrdinals == 0) {
        ordinal = builder.nextOrdinal();
      }
      controlDocToOrdinal.put(doc, ordinal);
      builder.addDoc(doc);
    }

    Ordinals ords = builder.build(ImmutableSettings.EMPTY);
    assertThat(ords, instanceOf(SinglePackedOrdinals.class));
    RandomAccessOrds docs = ords.ordinals();
    final SortedDocValues singleOrds = DocValues.unwrapSingleton(docs);
    assertNotNull(singleOrds);

    for (Map.Entry<Integer, Long> entry : controlDocToOrdinal.entrySet()) {
      assertThat(entry.getValue(), equalTo((long) singleOrds.getOrd(entry.getKey())));
    }
  }
 @Override
 public Explanation explain(LeafReaderContext context, int doc) throws IOException {
   SortedDocValues values = DocValues.getSorted(context.reader(), joinField);
   if (values != null) {
     int segmentOrd = values.getOrd(doc);
     if (segmentOrd != -1) {
       BytesRef joinValue = values.lookupOrd(segmentOrd);
       return Explanation.match(
           queryNorm, "Score based on join value " + joinValue.utf8ToString());
     }
   }
   return Explanation.noMatch("Not a match");
 }
Beispiel #3
0
  @Override
  public FunctionValues getValues(Map context, AtomicReaderContext readerContext)
      throws IOException {
    final SortedDocValues sindex = FieldCache.DEFAULT.getTermsIndex(readerContext.reader(), field);

    // figure out what ord maps to true
    int nord = sindex.getValueCount();
    BytesRef br = new BytesRef();
    // if no values in the segment, default trueOrd to something other then -1 (missing)
    int tord = -2;
    for (int i = 0; i < nord; i++) {
      sindex.lookupOrd(i, br);
      if (br.length == 1 && br.bytes[br.offset] == 'T') {
        tord = i;
        break;
      }
    }

    final int trueOrd = tord;

    return new BoolDocValues(this) {
      @Override
      public boolean boolVal(int doc) {
        return sindex.getOrd(doc) == trueOrd;
      }

      @Override
      public boolean exists(int doc) {
        return sindex.getOrd(doc) != -1;
      }

      @Override
      public ValueFiller getValueFiller() {
        return new ValueFiller() {
          private final MutableValueBool mval = new MutableValueBool();

          @Override
          public MutableValue getValue() {
            return mval;
          }

          @Override
          public void fillValue(int doc) {
            int ord = sindex.getOrd(doc);
            mval.value = (ord == trueOrd);
            mval.exists = (ord != -1);
          }
        };
      }
    };
  }
  @Override
  public Weight createWeight(IndexSearcher searcher) throws IOException {
    SearchContext sc = SearchContext.current();
    IndexParentChildFieldData globalIfd =
        parentChildIndexFieldData.loadGlobal(searcher.getIndexReader());
    assert rewrittenChildQuery != null;
    assert rewriteIndexReader == searcher.getIndexReader()
        : "not equal, rewriteIndexReader="
            + rewriteIndexReader
            + " searcher.getIndexReader()="
            + searcher.getIndexReader();

    final long valueCount;
    List<LeafReaderContext> leaves = searcher.getIndexReader().leaves();
    if (globalIfd == null || leaves.isEmpty()) {
      return Queries.newMatchNoDocsQuery().createWeight(searcher);
    } else {
      AtomicParentChildFieldData afd = globalIfd.load(leaves.get(0));
      SortedDocValues globalValues = afd.getOrdinalsValues(parentType);
      valueCount = globalValues.getValueCount();
    }

    if (valueCount == 0) {
      return Queries.newMatchNoDocsQuery().createWeight(searcher);
    }

    Query childQuery = rewrittenChildQuery;
    IndexSearcher indexSearcher = new IndexSearcher(searcher.getIndexReader());
    indexSearcher.setSimilarity(searcher.getSimilarity());
    ParentOrdCollector collector = new ParentOrdCollector(globalIfd, valueCount, parentType);
    indexSearcher.search(childQuery, collector);

    final long remaining = collector.foundParents();
    if (remaining == 0) {
      return Queries.newMatchNoDocsQuery().createWeight(searcher);
    }

    Filter shortCircuitFilter = null;
    if (remaining <= shortCircuitParentDocSet) {
      shortCircuitFilter =
          ParentIdsFilter.createShortCircuitFilter(
              nonNestedDocsFilter,
              sc,
              parentType,
              collector.values,
              collector.parentOrds,
              remaining);
    }
    return new ParentWeight(parentFilter, globalIfd, shortCircuitFilter, collector, remaining);
  }
    @Override
    public void collect(int docId) throws IOException {
      int globalDoc = docId + this.docBase;
      int ord = values.getOrd(globalDoc);

      if (ord > -1) {
        float score = scorer.score();
        if (score > scores[ord]) {
          ords[ord] = globalDoc;
          scores[ord] = score;
        }
      } else if (this.collapsedSet.get(globalDoc)) {
        // The doc is elevated so score does not matter
        // We just want to be sure it doesn't fall into the null policy
      } else if (nullPolicy == CollapsingPostFilter.NULL_POLICY_COLLAPSE) {
        float score = scorer.score();
        if (score > nullScore) {
          nullScore = score;
          nullDoc = globalDoc;
        }
      } else if (nullPolicy == CollapsingPostFilter.NULL_POLICY_EXPAND) {
        collapsedSet.set(globalDoc);
        nullScores.add(scorer.score());
      }
    }
 public FieldValueCollapse(
     int maxDoc,
     String field,
     int nullPolicy,
     boolean max,
     boolean needsScores,
     IntIntOpenHashMap boostDocs,
     SortedDocValues values) {
   this.field = field;
   this.nullPolicy = nullPolicy;
   this.max = max;
   this.needsScores = needsScores;
   this.collapsedSet = new FixedBitSet(maxDoc);
   this.boostDocs = boostDocs;
   if (this.boostDocs != null) {
     IntOpenHashSet boostG = new IntOpenHashSet();
     Iterator<IntIntCursor> it = boostDocs.iterator();
     while (it.hasNext()) {
       IntIntCursor cursor = it.next();
       int i = cursor.key;
       this.collapsedSet.set(i);
       int ord = values.getOrd(i);
       if (ord > -1) {
         boostG.add(ord);
       }
     }
     this.boostOrds = boostG.toArray();
     Arrays.sort(this.boostOrds);
   }
 }
 @Override
 protected SegmentResult createSegmentResult() throws IOException {
   return new SegmentResult(
       segmentFacetCounts,
       segmentTotalCount,
       facetFieldTermsIndex.termsEnum(),
       startFacetOrd,
       endFacetOrd);
 }
 @Override
 public Explanation explain(LeafReaderContext context, int doc) throws IOException {
   SortedDocValues values = DocValues.getSorted(context.reader(), joinField);
   if (values != null) {
     int segmentOrd = values.getOrd(doc);
     if (segmentOrd != -1) {
       final float score;
       if (globalOrds != null) {
         long globalOrd = globalOrds.getGlobalOrds(context.ord).get(segmentOrd);
         score = collector.score((int) globalOrd);
       } else {
         score = collector.score(segmentOrd);
       }
       BytesRef joinValue = values.lookupOrd(segmentOrd);
       return Explanation.match(score, "Score based on join value " + joinValue.utf8ToString());
     }
   }
   return Explanation.noMatch("Not a match");
 }
 @Override
 public void collect(int doc) throws IOException {
   if (values != null) {
     int globalOrdinal = values.getOrd(doc);
     // TODO: oversize the long bitset and remove the branch
     if (globalOrdinal >= 0) {
       parentOrds.set(globalOrdinal);
     }
   }
 }
  /**
   * "typical" single-valued faceting: not too many unique values, no prefixing. maps to global
   * ordinals as a separate step
   */
  static void accumSingleSeg(
      int counts[], SortedDocValues si, DocIdSetIterator disi, int subIndex, OrdinalMap map)
      throws IOException {
    // First count in seg-ord space:
    final int segCounts[];
    if (map == null) {
      segCounts = counts;
    } else {
      segCounts = new int[1 + si.getValueCount()];
    }

    int doc;
    while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
      segCounts[1 + si.getOrd(doc)]++;
    }

    // migrate to global ords (if necessary)
    if (map != null) {
      migrateGlobal(counts, segCounts, subIndex, map);
    }
  }
    public void finish() throws IOException {
      if (contexts.length == 0) {
        return;
      }

      int currentContext = 0;
      int currentDocBase = 0;
      int nextDocBase =
          currentContext + 1 < contexts.length ? contexts[currentContext + 1].docBase : maxDoc;
      leafDelegate = delegate.getLeafCollector(contexts[currentContext]);
      DummyScorer dummy = new DummyScorer();
      leafDelegate.setScorer(dummy);
      DocIdSetIterator it =
          new BitSetIterator(fieldValueCollapse.getCollapsedSet(), 0); // cost is not useful here
      int docId = -1;
      int nullScoreIndex = 0;
      float[] scores = fieldValueCollapse.getScores();
      FloatArrayList nullScores = fieldValueCollapse.getNullScores();
      float nullScore = fieldValueCollapse.getNullScore();
      while ((docId = it.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {

        if (this.needsScores) {
          int ord = values.getOrd(docId);
          if (ord > -1) {
            dummy.score = scores[ord];
          } else if (boostDocs != null && boostDocs.containsKey(docId)) {
            // It's an elevated doc so no score is needed
            dummy.score = 0F;
          } else if (nullPolicy == CollapsingPostFilter.NULL_POLICY_COLLAPSE) {
            dummy.score = nullScore;
          } else if (nullPolicy == CollapsingPostFilter.NULL_POLICY_EXPAND) {
            dummy.score = nullScores.get(nullScoreIndex++);
          }
        }

        while (docId >= nextDocBase) {
          currentContext++;
          currentDocBase = contexts[currentContext].docBase;
          nextDocBase =
              currentContext + 1 < contexts.length ? contexts[currentContext + 1].docBase : maxDoc;
          leafDelegate = delegate.getLeafCollector(contexts[currentContext]);
          leafDelegate.setScorer(dummy);
        }

        int contextDoc = docId - currentDocBase;
        dummy.docId = contextDoc;
        leafDelegate.collect(contextDoc);
      }

      if (delegate instanceof DelegatingCollector) {
        ((DelegatingCollector) delegate).finish();
      }
    }
  @Override
  public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
    final int off = readerContext.docBase;
    final LeafReader r;
    Object o = context.get("searcher");
    if (o instanceof SolrIndexSearcher) {
      SolrIndexSearcher is = (SolrIndexSearcher) o;
      SchemaField sf = is.getSchema().getFieldOrNull(field);
      if (sf != null
          && sf.hasDocValues() == false
          && sf.multiValued() == false
          && sf.getType().getNumericType() != null) {
        // it's a single-valued numeric field: we must currently create insanity :(
        List<LeafReaderContext> leaves = is.getIndexReader().leaves();
        LeafReader insaneLeaves[] = new LeafReader[leaves.size()];
        int upto = 0;
        for (LeafReaderContext raw : leaves) {
          insaneLeaves[upto++] = Insanity.wrapInsanity(raw.reader(), field);
        }
        r = SlowCompositeReaderWrapper.wrap(new MultiReader(insaneLeaves));
      } else {
        // reuse ordinalmap
        r = ((SolrIndexSearcher) o).getLeafReader();
      }
    } else {
      IndexReader topReader = ReaderUtil.getTopLevelContext(readerContext).reader();
      r = SlowCompositeReaderWrapper.wrap(topReader);
    }
    // if it's e.g. tokenized/multivalued, emulate old behavior of single-valued fc
    final SortedDocValues sindex =
        SortedSetSelector.wrap(DocValues.getSortedSet(r, field), SortedSetSelector.Type.MIN);
    final int end = sindex.getValueCount();

    return new IntDocValues(this) {
      @Override
      public int intVal(int doc) {
        return (end - sindex.getOrd(doc + off) - 1);
      }
    };
  }
 @Override
 public void collect(int doc) throws IOException {
   if (doc > index.docID()) {
     index.advance(doc);
   }
   int key;
   if (doc == index.docID()) {
     key = index.ordValue();
   } else {
     key = -1;
   }
   if (!ordSet.exists(key)) {
     ordSet.put(key);
     final BytesRef term;
     if (key == -1) {
       term = null;
     } else {
       term = BytesRef.deepCopyOf(index.lookupOrd(key));
     }
     groups.add(term);
   }
 }
 public CollapsingScoreCollector(
     int maxDoc,
     int segments,
     SortedDocValues values,
     int nullPolicy,
     IntIntOpenHashMap boostDocs) {
   this.maxDoc = maxDoc;
   this.contexts = new LeafReaderContext[segments];
   this.collapsedSet = new FixedBitSet(maxDoc);
   this.boostDocs = boostDocs;
   if (this.boostDocs != null) {
     // Set the elevated docs now.
     IntOpenHashSet boostG = new IntOpenHashSet();
     Iterator<IntIntCursor> it = this.boostDocs.iterator();
     while (it.hasNext()) {
       IntIntCursor cursor = it.next();
       int i = cursor.key;
       this.collapsedSet.set(i);
       int ord = values.getOrd(i);
       if (ord > -1) {
         boostG.add(ord);
       }
     }
     boostOrds = boostG.toArray();
     Arrays.sort(boostOrds);
   }
   this.values = values;
   int valueCount = values.getValueCount();
   this.ords = new int[valueCount];
   Arrays.fill(this.ords, -1);
   this.scores = new float[valueCount];
   Arrays.fill(this.scores, -Float.MAX_VALUE);
   this.nullPolicy = nullPolicy;
   if (nullPolicy == CollapsingPostFilter.NULL_POLICY_EXPAND) {
     nullScores = new FloatArrayList();
   }
 }
    @Override
    protected boolean match(int doc) {
      if (parentWeight.remaining == 0) {
        throw new CollectionTerminatedException();
      }

      long parentOrd = ordinals.getOrd(doc);
      if (parentOrd >= 0) {
        boolean match = parentOrds.get(parentOrd);
        if (match) {
          parentWeight.remaining--;
        }
        return match;
      }
      return false;
    }
    @Override
    public void collect(int doc) throws IOException {
      if (doc > facetFieldTermsIndex.docID()) {
        facetFieldTermsIndex.advance(doc);
      }

      int facetOrd;
      if (doc == facetFieldTermsIndex.docID()) {
        facetOrd = facetFieldTermsIndex.ordValue();
      } else {
        facetOrd = -1;
      }

      if (facetOrd < startFacetOrd || facetOrd >= endFacetOrd) {
        return;
      }

      if (doc > groupFieldTermsIndex.docID()) {
        groupFieldTermsIndex.advance(doc);
      }

      int groupOrd;
      if (doc == groupFieldTermsIndex.docID()) {
        groupOrd = groupFieldTermsIndex.ordValue();
      } else {
        groupOrd = -1;
      }
      int segmentGroupedFacetsIndex =
          groupOrd * (facetFieldTermsIndex.getValueCount() + 1) + facetOrd;
      if (segmentGroupedFacetHits.exists(segmentGroupedFacetsIndex)) {
        return;
      }

      segmentTotalCount++;
      segmentFacetCounts[facetOrd + 1]++;

      segmentGroupedFacetHits.put(segmentGroupedFacetsIndex);

      BytesRef groupKey;
      if (groupOrd == -1) {
        groupKey = null;
      } else {
        groupKey = BytesRef.deepCopyOf(groupFieldTermsIndex.lookupOrd(groupOrd));
      }

      BytesRef facetKey;
      if (facetOrd == -1) {
        facetKey = null;
      } else {
        facetKey = BytesRef.deepCopyOf(facetFieldTermsIndex.lookupOrd(facetOrd));
      }

      groupedFacetHits.add(new GroupedFacetHit(groupKey, facetKey));
    }
 /** accumulates per-segment single-valued facet counts */
 static void accumSingle(
     int counts[],
     int startTermIndex,
     SortedDocValues si,
     DocIdSetIterator disi,
     int subIndex,
     OrdinalMap map)
     throws IOException {
   if (startTermIndex == -1 && (map == null || si.getValueCount() < disi.cost() * 10)) {
     // no prefixing, not too many unique values wrt matching docs (lucene/facets heuristic):
     //   collect separately per-segment, then map to global ords
     accumSingleSeg(counts, si, disi, subIndex, map);
   } else {
     // otherwise: do collect+map on the fly
     accumSingleGeneric(counts, startTermIndex, si, disi, subIndex, map);
   }
 }
    @Override
    protected void doSetNextReader(LeafReaderContext context) throws IOException {
      if (segmentFacetCounts != null) {
        segmentResults.add(createSegmentResult());
      }

      groupFieldTermsIndex = DocValues.getSorted(context.reader(), groupField);
      facetFieldTermsIndex = DocValues.getSorted(context.reader(), facetField);

      // 1+ to allow for the -1 "not set":
      segmentFacetCounts = new int[facetFieldTermsIndex.getValueCount() + 1];
      segmentTotalCount = 0;

      segmentGroupedFacetHits.clear();
      for (GroupedFacetHit groupedFacetHit : groupedFacetHits) {
        int facetOrd =
            groupedFacetHit.facetValue == null
                ? -1
                : facetFieldTermsIndex.lookupTerm(groupedFacetHit.facetValue);
        if (groupedFacetHit.facetValue != null && facetOrd < 0) {
          continue;
        }

        int groupOrd =
            groupedFacetHit.groupValue == null
                ? -1
                : groupFieldTermsIndex.lookupTerm(groupedFacetHit.groupValue);
        if (groupedFacetHit.groupValue != null && groupOrd < 0) {
          continue;
        }

        int segmentGroupedFacetsIndex =
            groupOrd * (facetFieldTermsIndex.getValueCount() + 1) + facetOrd;
        segmentGroupedFacetHits.put(segmentGroupedFacetsIndex);
      }

      if (facetPrefix != null) {
        startFacetOrd = facetFieldTermsIndex.lookupTerm(facetPrefix);
        if (startFacetOrd < 0) {
          // Points to the ord one higher than facetPrefix
          startFacetOrd = -startFacetOrd - 1;
        }
        BytesRefBuilder facetEndPrefix = new BytesRefBuilder();
        facetEndPrefix.append(facetPrefix);
        facetEndPrefix.append(UnicodeUtil.BIG_TERM);
        endFacetOrd = facetFieldTermsIndex.lookupTerm(facetEndPrefix.get());
        assert endFacetOrd < 0;
        endFacetOrd = -endFacetOrd - 1; // Points to the ord one higher than facetEndPrefix
      } else {
        startFacetOrd = -1;
        endFacetOrd = facetFieldTermsIndex.getValueCount();
      }
    }
 // specialized since the single-valued case is different
 static void accumSingle(
     int counts[],
     int startTermIndex,
     SortedDocValues si,
     DocIdSetIterator disi,
     int subIndex,
     OrdinalMap map)
     throws IOException {
   int doc;
   while ((doc = disi.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
     int term = si.getOrd(doc);
     if (map != null && term >= 0) {
       term = (int) map.getGlobalOrd(subIndex, term);
     }
     int arrIdx = term - startTermIndex;
     if (arrIdx >= 0 && arrIdx < counts.length) counts[arrIdx]++;
   }
 }
  @Override
  protected void doSetNextReader(LeafReaderContext context) throws IOException {
    index = DocValues.getSorted(context.reader(), groupField);

    // Clear ordSet and fill it with previous encountered groups that can occur in the current
    // segment.
    ordSet.clear();
    for (BytesRef countedGroup : groups) {
      if (countedGroup == null) {
        ordSet.put(-1);
      } else {
        int ord = index.lookupTerm(countedGroup);
        if (ord >= 0) {
          ordSet.put(ord);
        }
      }
    }
  }
  /**
   * Merges the sorted docvalues from <code>toMerge</code>.
   *
   * <p>The default implementation calls {@link #addSortedField}, passing an Iterable that merges
   * ordinals and values and filters deleted documents .
   */
  public void mergeSortedField(
      FieldInfo fieldInfo, final MergeState mergeState, List<SortedDocValues> toMerge)
      throws IOException {

    mergeState.checkAbort.work(mergeState.segmentInfo.getDocCount());

    final AtomicReader readers[] = mergeState.readers.toArray(new AtomicReader[toMerge.size()]);
    final SortedDocValues dvs[] = toMerge.toArray(new SortedDocValues[toMerge.size()]);

    // step 1: iterate thru each sub and mark terms still in use
    TermsEnum liveTerms[] = new TermsEnum[dvs.length];
    long[] weights = new long[liveTerms.length];
    for (int sub = 0; sub < liveTerms.length; sub++) {
      AtomicReader reader = readers[sub];
      SortedDocValues dv = dvs[sub];
      Bits liveDocs = reader.getLiveDocs();
      if (liveDocs == null) {
        liveTerms[sub] = dv.termsEnum();
        weights[sub] = dv.getValueCount();
      } else {
        LongBitSet bitset = new LongBitSet(dv.getValueCount());
        for (int i = 0; i < reader.maxDoc(); i++) {
          if (liveDocs.get(i)) {
            int ord = dv.getOrd(i);
            if (ord >= 0) {
              bitset.set(ord);
            }
          }
        }
        liveTerms[sub] = new BitsFilteredTermsEnum(dv.termsEnum(), bitset);
        weights[sub] = bitset.cardinality();
      }
    }

    // step 2: create ordinal map (this conceptually does the "merging")
    final OrdinalMap map = OrdinalMap.build(this, liveTerms, weights, PackedInts.COMPACT);

    // step 3: add field
    addSortedField(
        fieldInfo,
        // ord -> value
        new Iterable<BytesRef>() {
          @Override
          public Iterator<BytesRef> iterator() {
            return new Iterator<BytesRef>() {
              int currentOrd;

              @Override
              public boolean hasNext() {
                return currentOrd < map.getValueCount();
              }

              @Override
              public BytesRef next() {
                if (!hasNext()) {
                  throw new NoSuchElementException();
                }
                int segmentNumber = map.getFirstSegmentNumber(currentOrd);
                int segmentOrd = (int) map.getFirstSegmentOrd(currentOrd);
                final BytesRef term = dvs[segmentNumber].lookupOrd(segmentOrd);
                currentOrd++;
                return term;
              }

              @Override
              public void remove() {
                throw new UnsupportedOperationException();
              }
            };
          }
        },
        // doc -> ord
        new Iterable<Number>() {
          @Override
          public Iterator<Number> iterator() {
            return new Iterator<Number>() {
              int readerUpto = -1;
              int docIDUpto;
              int nextValue;
              AtomicReader currentReader;
              Bits currentLiveDocs;
              LongValues currentMap;
              boolean nextIsSet;

              @Override
              public boolean hasNext() {
                return nextIsSet || setNext();
              }

              @Override
              public void remove() {
                throw new UnsupportedOperationException();
              }

              @Override
              public Number next() {
                if (!hasNext()) {
                  throw new NoSuchElementException();
                }
                assert nextIsSet;
                nextIsSet = false;
                // TODO make a mutable number
                return nextValue;
              }

              private boolean setNext() {
                while (true) {
                  if (readerUpto == readers.length) {
                    return false;
                  }

                  if (currentReader == null || docIDUpto == currentReader.maxDoc()) {
                    readerUpto++;
                    if (readerUpto < readers.length) {
                      currentReader = readers[readerUpto];
                      currentLiveDocs = currentReader.getLiveDocs();
                      currentMap = map.getGlobalOrds(readerUpto);
                    }
                    docIDUpto = 0;
                    continue;
                  }

                  if (currentLiveDocs == null || currentLiveDocs.get(docIDUpto)) {
                    nextIsSet = true;
                    int segOrd = dvs[readerUpto].getOrd(docIDUpto);
                    nextValue = segOrd == -1 ? -1 : (int) currentMap.get(segOrd);
                    docIDUpto++;
                    return true;
                  }

                  docIDUpto++;
                }
              }
            };
          }
        });
  }
  public void testDocValuesMemoryIndexVsNormalIndex() throws Exception {
    Document doc = new Document();
    long randomLong = random().nextLong();
    doc.add(new NumericDocValuesField("numeric", randomLong));
    if (random().nextBoolean()) {
      doc.add(new LegacyLongField("numeric", randomLong, Field.Store.NO));
    }
    int numValues = atLeast(5);
    for (int i = 0; i < numValues; i++) {
      randomLong = random().nextLong();
      doc.add(new SortedNumericDocValuesField("sorted_numeric", randomLong));
      if (random().nextBoolean()) {
        // randomly duplicate field/value
        doc.add(new SortedNumericDocValuesField("sorted_numeric", randomLong));
      }
      if (random().nextBoolean()) {
        doc.add(new LegacyLongField("numeric", randomLong, Field.Store.NO));
      }
    }
    BytesRef randomTerm = new BytesRef(randomTerm());
    doc.add(new BinaryDocValuesField("binary", randomTerm));
    if (random().nextBoolean()) {
      doc.add(new StringField("binary", randomTerm, Field.Store.NO));
    }
    randomTerm = new BytesRef(randomTerm());
    doc.add(new SortedDocValuesField("sorted", randomTerm));
    if (random().nextBoolean()) {
      doc.add(new StringField("sorted", randomTerm, Field.Store.NO));
    }
    numValues = atLeast(5);
    for (int i = 0; i < numValues; i++) {
      randomTerm = new BytesRef(randomTerm());
      doc.add(new SortedSetDocValuesField("sorted_set", randomTerm));
      if (random().nextBoolean()) {
        // randomly duplicate field/value
        doc.add(new SortedSetDocValuesField("sorted_set", randomTerm));
      }
      if (random().nextBoolean()) {
        // randomily just add a normal string field
        doc.add(new StringField("sorted_set", randomTerm, Field.Store.NO));
      }
    }

    MockAnalyzer mockAnalyzer = new MockAnalyzer(random());
    MemoryIndex memoryIndex = MemoryIndex.fromDocument(doc, mockAnalyzer);
    IndexReader indexReader = memoryIndex.createSearcher().getIndexReader();
    LeafReader leafReader = indexReader.leaves().get(0).reader();

    Directory dir = newDirectory();
    IndexWriter writer = new IndexWriter(dir, newIndexWriterConfig(random(), mockAnalyzer));
    writer.addDocument(doc);
    writer.close();
    IndexReader controlIndexReader = DirectoryReader.open(dir);
    LeafReader controlLeafReader = controlIndexReader.leaves().get(0).reader();

    NumericDocValues numericDocValues = leafReader.getNumericDocValues("numeric");
    NumericDocValues controlNumericDocValues = controlLeafReader.getNumericDocValues("numeric");
    assertEquals(controlNumericDocValues.get(0), numericDocValues.get(0));

    SortedNumericDocValues sortedNumericDocValues =
        leafReader.getSortedNumericDocValues("sorted_numeric");
    sortedNumericDocValues.setDocument(0);
    SortedNumericDocValues controlSortedNumericDocValues =
        controlLeafReader.getSortedNumericDocValues("sorted_numeric");
    controlSortedNumericDocValues.setDocument(0);
    assertEquals(controlSortedNumericDocValues.count(), sortedNumericDocValues.count());
    for (int i = 0; i < controlSortedNumericDocValues.count(); i++) {
      assertEquals(controlSortedNumericDocValues.valueAt(i), sortedNumericDocValues.valueAt(i));
    }

    BinaryDocValues binaryDocValues = leafReader.getBinaryDocValues("binary");
    BinaryDocValues controlBinaryDocValues = controlLeafReader.getBinaryDocValues("binary");
    assertEquals(controlBinaryDocValues.get(0), binaryDocValues.get(0));

    SortedDocValues sortedDocValues = leafReader.getSortedDocValues("sorted");
    SortedDocValues controlSortedDocValues = controlLeafReader.getSortedDocValues("sorted");
    assertEquals(controlSortedDocValues.getValueCount(), sortedDocValues.getValueCount());
    assertEquals(controlSortedDocValues.get(0), sortedDocValues.get(0));
    assertEquals(controlSortedDocValues.getOrd(0), sortedDocValues.getOrd(0));
    assertEquals(controlSortedDocValues.lookupOrd(0), sortedDocValues.lookupOrd(0));

    SortedSetDocValues sortedSetDocValues = leafReader.getSortedSetDocValues("sorted_set");
    sortedSetDocValues.setDocument(0);
    SortedSetDocValues controlSortedSetDocValues =
        controlLeafReader.getSortedSetDocValues("sorted_set");
    controlSortedSetDocValues.setDocument(0);
    assertEquals(controlSortedSetDocValues.getValueCount(), sortedSetDocValues.getValueCount());
    for (long controlOrd = controlSortedSetDocValues.nextOrd();
        controlOrd != SortedSetDocValues.NO_MORE_ORDS;
        controlOrd = controlSortedSetDocValues.nextOrd()) {
      assertEquals(controlOrd, sortedSetDocValues.nextOrd());
      assertEquals(
          controlSortedSetDocValues.lookupOrd(controlOrd),
          sortedSetDocValues.lookupOrd(controlOrd));
    }
    assertEquals(SortedSetDocValues.NO_MORE_ORDS, sortedSetDocValues.nextOrd());

    indexReader.close();
    controlIndexReader.close();
    dir.close();
  }
 public void collect(int docId) throws IOException {
   int globalDoc = docId + this.docBase;
   int ord = values.getOrd(globalDoc);
   fieldValueCollapse.collapse(ord, docId, globalDoc);
 }
    public CollapsingFieldValueCollector(
        int maxDoc,
        int segments,
        SortedDocValues values,
        int nullPolicy,
        String field,
        boolean max,
        boolean needsScores,
        FieldType fieldType,
        IntIntOpenHashMap boostDocs,
        FunctionQuery funcQuery,
        IndexSearcher searcher)
        throws IOException {

      this.maxDoc = maxDoc;
      this.contexts = new LeafReaderContext[segments];
      this.values = values;
      int valueCount = values.getValueCount();
      this.nullPolicy = nullPolicy;
      this.needsScores = needsScores;
      this.boostDocs = boostDocs;
      if (funcQuery != null) {
        this.fieldValueCollapse =
            new ValueSourceCollapse(
                maxDoc,
                field,
                nullPolicy,
                new int[valueCount],
                max,
                this.needsScores,
                boostDocs,
                funcQuery,
                searcher,
                values);
      } else {
        if (fieldType instanceof TrieIntField) {
          this.fieldValueCollapse =
              new IntValueCollapse(
                  maxDoc,
                  field,
                  nullPolicy,
                  new int[valueCount],
                  max,
                  this.needsScores,
                  boostDocs,
                  values);
        } else if (fieldType instanceof TrieLongField) {
          this.fieldValueCollapse =
              new LongValueCollapse(
                  maxDoc,
                  field,
                  nullPolicy,
                  new int[valueCount],
                  max,
                  this.needsScores,
                  boostDocs,
                  values);
        } else if (fieldType instanceof TrieFloatField) {
          this.fieldValueCollapse =
              new FloatValueCollapse(
                  maxDoc,
                  field,
                  nullPolicy,
                  new int[valueCount],
                  max,
                  this.needsScores,
                  boostDocs,
                  values);
        } else {
          throw new IOException("min/max must be either TrieInt, TrieLong or TrieFloat.");
        }
      }
    }
    @Override
    public void finish() throws IOException {
      if (contexts.length == 0) {
        return;
      }

      if (nullScore > 0) {
        this.collapsedSet.set(nullDoc);
      }

      if (this.boostOrds != null) {
        for (int i = 0; i < this.boostOrds.length; i++) {
          ords[boostOrds[i]] = -1;
        }
      }

      for (int i = 0; i < ords.length; i++) {
        int doc = ords[i];
        if (doc > -1) {
          collapsedSet.set(doc);
        }
      }

      int currentContext = 0;
      int currentDocBase = 0;
      int nextDocBase =
          currentContext + 1 < contexts.length ? contexts[currentContext + 1].docBase : maxDoc;
      leafDelegate = delegate.getLeafCollector(contexts[currentContext]);
      DummyScorer dummy = new DummyScorer();
      leafDelegate.setScorer(dummy);
      DocIdSetIterator it = new BitSetIterator(collapsedSet, 0L); // cost is not useful here
      int docId = -1;
      int nullScoreIndex = 0;
      while ((docId = it.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {

        int ord = values.getOrd(docId);

        if (ord > -1) {
          dummy.score = scores[ord];
        } else if (this.boostDocs != null && boostDocs.containsKey(docId)) {
          // Elevated docs don't need a score.
          dummy.score = 0F;
        } else if (nullPolicy == CollapsingPostFilter.NULL_POLICY_COLLAPSE) {
          dummy.score = nullScore;
        } else if (nullPolicy == CollapsingPostFilter.NULL_POLICY_EXPAND) {
          dummy.score = nullScores.get(nullScoreIndex++);
        }

        while (docId >= nextDocBase) {
          currentContext++;
          currentDocBase = contexts[currentContext].docBase;
          nextDocBase =
              currentContext + 1 < contexts.length ? contexts[currentContext + 1].docBase : maxDoc;
          leafDelegate = delegate.getLeafCollector(contexts[currentContext]);
          leafDelegate.setScorer(dummy);
        }

        int contextDoc = docId - currentDocBase;
        dummy.docId = contextDoc;
        leafDelegate.collect(contextDoc);
      }

      if (delegate instanceof DelegatingCollector) {
        ((DelegatingCollector) delegate).finish();
      }
    }