/** * 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.filter; import com.linkedin.pinot.core.common.Block; import com.linkedin.pinot.core.common.BlockId; import com.linkedin.pinot.core.common.DataSource; import com.linkedin.pinot.core.common.Predicate; import com.linkedin.pinot.core.operator.blocks.BaseFilterBlock; import com.linkedin.pinot.core.operator.blocks.BitmapBlock; import com.linkedin.pinot.core.operator.filter.predicate.PredicateEvaluator; import com.linkedin.pinot.core.operator.filter.predicate.PredicateEvaluatorProvider; import com.linkedin.pinot.core.segment.index.readers.InvertedIndexReader; import java.util.ArrayList; import java.util.List; import org.roaringbitmap.buffer.ImmutableRoaringBitmap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class BitmapBasedFilterOperator extends BaseFilterOperator { private static final Logger LOGGER = LoggerFactory.getLogger(BitmapBasedFilterOperator.class); private static final String OPERATOR_NAME = "BitmapBasedFilterOperator"; private final PredicateEvaluator predicateEvaluator; private final Predicate predicate; private DataSource dataSource; private BitmapBlock bitmapBlock; private int startDocId; private int endDocId; /** * @param predicate * @param dataSource * @param startDocId inclusive * @param endDocId inclusive */ public BitmapBasedFilterOperator(Predicate predicate, DataSource dataSource, int startDocId, int endDocId) { this.predicate = predicate; this.predicateEvaluator = PredicateEvaluatorProvider.getPredicateFunctionFor(predicate, dataSource); this.dataSource = dataSource; this.startDocId = startDocId; this.endDocId = endDocId; } @Override public boolean open() { return true; } @Override public BaseFilterBlock nextFilterBlock(BlockId BlockId) { InvertedIndexReader invertedIndex = dataSource.getInvertedIndex(); Block dataSourceBlock = dataSource.nextBlock(); int[] dictionaryIds; boolean exclusion = false; switch (predicate.getType()) { case EQ: case IN: case RANGE: dictionaryIds = predicateEvaluator.getMatchingDictionaryIds(); break; case NEQ: case NOT_IN: exclusion = true; dictionaryIds = predicateEvaluator.getNonMatchingDictionaryIds(); break; case REGEXP_LIKE: default: throw new UnsupportedOperationException("Regex is not supported"); } // For realtime use case, it is possible that inverted index has not yet generated for the given dict id, so we // filter out null bitmaps int length = dictionaryIds.length; List<ImmutableRoaringBitmap> bitmaps = new ArrayList<>(length); for (int dictionaryId : dictionaryIds) { ImmutableRoaringBitmap bitmap = invertedIndex.getImmutable(dictionaryId); if (bitmap != null) { bitmaps.add(bitmap); } } // Log size diff to verify the fix int numBitmaps = bitmaps.size(); if (numBitmaps != length) { LOGGER.info("Not all inverted indexes are generated, numDictIds: {}, numBitmaps: {}", length, numBitmaps); } bitmapBlock = new BitmapBlock(dataSource.getOperatorName(), dataSourceBlock.getMetadata(), startDocId, endDocId, bitmaps.toArray(new ImmutableRoaringBitmap[numBitmaps]), exclusion); return bitmapBlock; } @Override public boolean isResultEmpty() { return predicateEvaluator.alwaysFalse(); } @Override public boolean close() { return true; } @Override public String getOperatorName() { return OPERATOR_NAME; } }