/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* Licensed 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 com.linkedin.pinot.core.operator.dociditerators;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.concurrent.atomic.AtomicLong;
import com.linkedin.pinot.common.utils.Pairs;
import com.linkedin.pinot.common.utils.Pairs.IntPair;
import com.linkedin.pinot.core.common.BlockDocIdIterator;
import com.linkedin.pinot.core.common.Constants;
public final class OrDocIdIterator implements BlockDocIdIterator {
private BlockDocIdIterator[] docIdIterators;
final PriorityQueue<IntPair> queue;
final boolean[] iteratorIsInQueue;
int currentDocId = -1;
private int minDocId;
private int maxDocId;
private AtomicLong timeMeasure = new AtomicLong(0);
IntPair[] pointers;
/**
* @param docIdIterators
*/
public OrDocIdIterator(BlockDocIdIterator[] docIdIterators) {
this.docIdIterators = docIdIterators;
queue =
new PriorityQueue<IntPair>(docIdIterators.length, new Pairs.AscendingIntPairComparator());
iteratorIsInQueue = new boolean[docIdIterators.length];
pointers= new IntPair[docIdIterators.length];
for (int i = 0; i < docIdIterators.length; i++) {
pointers[i] = new IntPair(0, i);
}
}
@Override
public int advance(int targetDocId) {
if (currentDocId == Constants.EOF) {
return Constants.EOF;
}
if (targetDocId < minDocId) {
targetDocId = minDocId;
} else if (targetDocId > maxDocId) {
currentDocId = Constants.EOF;
return currentDocId;
}
long start = System.nanoTime();
// Remove iterators that are before the target document id from the queue
Iterator<IntPair> iterator = queue.iterator();
while (iterator.hasNext()) {
IntPair pair = iterator.next();
if (pair.getLeft() < targetDocId) {
iterator.remove();
iteratorIsInQueue[pair.getRight()] = false;
}
}
// Advance all iterators that are not in the queue to the target document id
for (int i = 0; i < docIdIterators.length; i++) {
if (!iteratorIsInQueue[i]) {
int nextDocId = docIdIterators[i].advance(targetDocId);
if (nextDocId != Constants.EOF) {
pointers[i].setLeft(nextDocId);
queue.add(pointers[i]);
}
iteratorIsInQueue[i] = true;
}
}
// Return the first element
if (queue.size() > 0) {
currentDocId = queue.peek().getLeft();
} else {
currentDocId = Constants.EOF;
}
long end = System.nanoTime();
timeMeasure.addAndGet(end - start);
return currentDocId;
}
@Override
public int next() {
long start = System.currentTimeMillis();
if (currentDocId == Constants.EOF) {
return currentDocId;
}
while (queue.size() > 0 && queue.peek().getLeft() <= currentDocId) {
IntPair pair = queue.remove();
iteratorIsInQueue[pair.getRight()] = false;
}
currentDocId++;
// Grab the next value from each iterator, if it's not in the queue
for (int i = 0; i < docIdIterators.length; i++) {
if (!iteratorIsInQueue[i]) {
int nextDocId = docIdIterators[i].advance(currentDocId);
if (nextDocId != Constants.EOF) {
if (!(nextDocId <= maxDocId && nextDocId >= minDocId) && nextDocId >= currentDocId) {
throw new RuntimeException("next Doc : " + nextDocId
+ " should never crossing the range : [ " + minDocId + ", " + maxDocId + " ]");
}
queue.add(new IntPair(nextDocId, i));
}
iteratorIsInQueue[i] = true;
}
}
if (queue.size() > 0) {
currentDocId = queue.peek().getLeft();
} else {
currentDocId = Constants.EOF;
}
long end = System.currentTimeMillis();
timeMeasure.addAndGet(end - start);
// Remove this after tracing is added
// if (currentDocId == Constants.EOF) {
// LOGGER.debug("OR took:" + timeMeasure.get());
// }
return currentDocId;
}
@Override
public int currentDocId() {
return currentDocId;
}
public void setStartDocId(int minDocId) {
this.minDocId = minDocId;
}
public void setEndDocId(int maxDocId) {
this.maxDocId = maxDocId;
}
}