/**
* 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;
import com.google.common.base.Preconditions;
import com.linkedin.pinot.core.common.Block;
import com.linkedin.pinot.core.common.BlockDocIdIterator;
import com.linkedin.pinot.core.common.BlockId;
import com.linkedin.pinot.core.common.Constants;
import com.linkedin.pinot.core.common.Operator;
import com.linkedin.pinot.core.operator.blocks.DocIdSetBlock;
import com.linkedin.pinot.core.operator.docidsets.FilterBlockDocIdSet;
import com.linkedin.pinot.core.operator.filter.BaseFilterOperator;
import com.linkedin.pinot.core.plan.DocIdSetPlanNode;
/**
* BReusableFilteredDocIdSetOperator will take a filter Operator and get the matched docId set.
* Internally, cached a given size of docIds, so this Operator could be replicated
* for many ColumnarReaderDataSource.
*/
public class BReusableFilteredDocIdSetOperator extends BaseOperator {
private static final String OPERATOR_NAME = "BReusableFilteredDocIdSetOperator";
private static final ThreadLocal<int[]> DOC_ID_ARRAY = new ThreadLocal<int[]>() {
@Override
protected int[] initialValue() {
return new int[DocIdSetPlanNode.MAX_DOC_PER_CALL];
}
};
private final BaseFilterOperator _filterOperator;
private final int _maxSizeOfDocIdSet;
private FilterBlockDocIdSet _filterBlockDocIdSet;
private BlockDocIdIterator _blockDocIdIterator;
private int _currentDocId = 0;
/**
* @param filterOperator
* @param docSize
* @param maxSizeOfDocIdSet must be less than {@link DocIdSetPlanNode}. MAX_DOC_PER_CALL which is
* 10000
*/
public BReusableFilteredDocIdSetOperator(Operator filterOperator, int docSize,
int maxSizeOfDocIdSet) {
Preconditions.checkArgument(maxSizeOfDocIdSet <= DocIdSetPlanNode.MAX_DOC_PER_CALL);
_maxSizeOfDocIdSet = maxSizeOfDocIdSet;
_filterOperator = (BaseFilterOperator) filterOperator;
}
@Override
public boolean open() {
_filterOperator.open();
return true;
}
@Override
public DocIdSetBlock getNextBlock() {
// Handle limit 0 clause safely.
// For limit 0, _docIdArray will be zero sized
if (_currentDocId == Constants.EOF) {
return null;
}
int[] docIdArray = DOC_ID_ARRAY.get();
// Initialize filter block doc id set.
if (_filterBlockDocIdSet == null) {
_filterBlockDocIdSet = (FilterBlockDocIdSet) _filterOperator.nextBlock().getBlockDocIdSet();
_blockDocIdIterator = _filterBlockDocIdSet.iterator();
}
int pos = 0;
for (int i = 0; i < _maxSizeOfDocIdSet; i++) {
_currentDocId = _blockDocIdIterator.next();
if (_currentDocId == Constants.EOF) {
break;
}
docIdArray[pos++] = _currentDocId;
}
if (pos > 0) {
return new DocIdSetBlock(docIdArray, pos);
} else {
return null;
}
}
@Override
public Block getNextBlock(BlockId blockId) {
throw new UnsupportedOperationException();
}
@Override
public String getOperatorName() {
return OPERATOR_NAME;
}
@Override
public boolean close() {
_filterOperator.close();
return true;
}
@Override
public ExecutionStatistics getExecutionStatistics() {
return new ExecutionStatistics(0L, _filterBlockDocIdSet.getNumEntriesScannedInFilter(), 0L, 0L);
}
}