/*
 * 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.index;

import java.io.IOException;
import java.util.List;

import org.apache.lucene.index.MergeState.DocMap;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.PriorityQueue;
import org.apache.lucene.util.packed.PackedInts;
import org.apache.lucene.util.packed.PackedLongValues;

final class MultiSorter {
  
  
Does a merge sort of the leaves of the incoming reader, returning DocMap to map each leaf's documents into the merged segment. The documents for each incoming leaf reader must already be sorted by the same sort! Returns null if the merge sort is not needed (segments are already in index sort order).
/** Does a merge sort of the leaves of the incoming reader, returning {@link DocMap} to map each leaf's * documents into the merged segment. The documents for each incoming leaf reader must already be sorted by the same sort! * Returns null if the merge sort is not needed (segments are already in index sort order). **/
static MergeState.DocMap[] sort(Sort sort, List<CodecReader> readers) throws IOException { // TODO: optimize if only 1 reader is incoming, though that's a rare case SortField fields[] = sort.getSort(); final IndexSorter.ComparableProvider[][] comparables = new IndexSorter.ComparableProvider[fields.length][]; final int[] reverseMuls = new int[fields.length]; for(int i=0;i<fields.length;i++) { IndexSorter sorter = fields[i].getIndexSorter(); if (sorter == null) { throw new IllegalArgumentException("Cannot use sort field " + fields[i] + " for index sorting"); } comparables[i] = sorter.getComparableProviders(readers); reverseMuls[i] = fields[i].getReverse() ? -1 : 1; } int leafCount = readers.size(); PriorityQueue<LeafAndDocID> queue = new PriorityQueue<LeafAndDocID>(leafCount) { @Override public boolean lessThan(LeafAndDocID a, LeafAndDocID b) { for(int i=0;i<comparables.length;i++) { int cmp = Long.compare(a.valuesAsComparableLongs[i], b.valuesAsComparableLongs[i]); if (cmp != 0) { return reverseMuls[i] * cmp < 0; } } // tie-break by docID natural order: if (a.readerIndex != b.readerIndex) { return a.readerIndex < b.readerIndex; } else { return a.docID < b.docID; } } }; PackedLongValues.Builder[] builders = new PackedLongValues.Builder[leafCount]; for(int i=0;i<leafCount;i++) { CodecReader reader = readers.get(i); LeafAndDocID leaf = new LeafAndDocID(i, reader.getLiveDocs(), reader.maxDoc(), comparables.length); for(int j=0;j<comparables.length;j++) { leaf.valuesAsComparableLongs[j] = comparables[j][i].getAsComparableLong(leaf.docID); } queue.add(leaf); builders[i] = PackedLongValues.monotonicBuilder(PackedInts.COMPACT); } // merge sort: int mappedDocID = 0; int lastReaderIndex = 0; boolean isSorted = true; while (queue.size() != 0) { LeafAndDocID top = queue.top(); if (lastReaderIndex > top.readerIndex) { // merge sort is needed isSorted = false; } lastReaderIndex = top.readerIndex; builders[top.readerIndex].add(mappedDocID); if (top.liveDocs == null || top.liveDocs.get(top.docID)) { mappedDocID++; } top.docID++; if (top.docID < top.maxDoc) { for(int j=0;j<comparables.length;j++) { top.valuesAsComparableLongs[j] = comparables[j][top.readerIndex].getAsComparableLong(top.docID); } queue.updateTop(); } else { queue.pop(); } } if (isSorted) { return null; } MergeState.DocMap[] docMaps = new MergeState.DocMap[leafCount]; for(int i=0;i<leafCount;i++) { final PackedLongValues remapped = builders[i].build(); final Bits liveDocs = readers.get(i).getLiveDocs(); docMaps[i] = new MergeState.DocMap() { @Override public int get(int docID) { if (liveDocs == null || liveDocs.get(docID)) { return (int) remapped.get(docID); } else { return -1; } } }; } return docMaps; } private static class LeafAndDocID { final int readerIndex; final Bits liveDocs; final int maxDoc; final long[] valuesAsComparableLongs; int docID; public LeafAndDocID(int readerIndex, Bits liveDocs, int maxDoc, int numComparables) { this.readerIndex = readerIndex; this.liveDocs = liveDocs; this.maxDoc = maxDoc; this.valuesAsComparableLongs = new long[numComparables]; } } }