/** * 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.predicate; import com.linkedin.pinot.common.data.FieldSpec; import com.linkedin.pinot.core.common.predicate.NEqPredicate; import com.linkedin.pinot.core.segment.index.readers.Dictionary; /** * Factory for NotEquals predicate evaluators. */ public class NotEqualsPredicateEvaluatorFactory { // Private constructor private NotEqualsPredicateEvaluatorFactory() { } /** * Returns a new instance of dictionary based NEQ evaluator. * * @param predicate NEQ predicate * @param dictionary Dictionary * @return Dictionary based NEQ predicate evaluator */ public static PredicateEvaluator newDictionaryBasedEvaluator(NEqPredicate predicate, Dictionary dictionary) { return new DictionaryBasedNotEqualsPredicateEvaluator(predicate, dictionary); } /** * Returns a new instance of no-dictionary based NEQ evaluator. * * @param predicate NEQ predicate * @param dataType Data type of input value * @return No-Dictionary based NEQ predicate evaluator */ public static PredicateEvaluator newNoDictionaryBasedEvaluator(NEqPredicate predicate, FieldSpec.DataType dataType) { switch (dataType) { case INT: return new IntNoDictionaryNeqEvaluator(predicate); case LONG: return new LongNoDictionaryNeqEvaluator(predicate); case FLOAT: return new FloatNoDictionaryNeqEvaluator(predicate); case DOUBLE: return new DoubleNoDictionaryNeqEvaluator(predicate); case STRING: return new StringNoDictionaryNeqEvaluator(predicate); default: throw new UnsupportedOperationException("Data type not supported for index without dictionary: " + dataType); } } /** * * No dictionary NEQ evaluator for int data type. */ private static final class IntNoDictionaryNeqEvaluator extends BasePredicateEvaluator { private int _neqValue; private IntNoDictionaryNeqEvaluator(NEqPredicate predicate) { _neqValue = Integer.parseInt(predicate.getNotEqualsValue()); } @Override public boolean apply(int inputValue) { return (_neqValue != inputValue); } @Override public boolean apply(int[] inputValues) { return apply(inputValues, inputValues.length); } @Override public boolean apply(int[] inputValues, int length) { // we cannot do binary search since the multi-value columns are not sorted in the raw segment for (int i = 0; i < length; i++) { int inputValue = inputValues[i]; if (_neqValue == inputValue) { return false; } } return true; } } /** * * No dictionary NEQ evaluator for long data type. */ private static final class LongNoDictionaryNeqEvaluator extends BasePredicateEvaluator { private long _neqValue; private LongNoDictionaryNeqEvaluator(NEqPredicate predicate) { _neqValue = Long.parseLong(predicate.getNotEqualsValue()); } @Override public boolean apply(long inputValue) { return (_neqValue != inputValue); } @Override public boolean apply(long[] inputValues) { return apply(inputValues, inputValues.length); } @Override public boolean apply(long[] inputValues, int length) { // We cannot do binary search since the multi-value columns are not sorted in the raw segment for (int i = 0; i < length; i++) { long inputValue = inputValues[i]; if (_neqValue == inputValue) { return false; } } return true; } } /** * * No dictionary NEQ evaluator for float data type. */ private static final class FloatNoDictionaryNeqEvaluator extends BasePredicateEvaluator { private float _neqValue; private FloatNoDictionaryNeqEvaluator(NEqPredicate predicate) { _neqValue = Float.parseFloat(predicate.getNotEqualsValue()); } @Override public boolean apply(float inputValue) { return (_neqValue != inputValue); } @Override public boolean apply(float[] inputValues) { return apply(inputValues, inputValues.length); } @Override public boolean apply(float[] inputValues, int length) { // We cannot do binary search since the multi-value columns are not sorted in the raw segment for (int i = 0; i < length; i++) { float inputValue = inputValues[i]; if (_neqValue == inputValue) { return false; } } return true; } } /** * * No dictionary NEQ evaluator for double data type. */ private static final class DoubleNoDictionaryNeqEvaluator extends BasePredicateEvaluator { private double _neqValue; private DoubleNoDictionaryNeqEvaluator(NEqPredicate predicate) { _neqValue = Double.parseDouble(predicate.getNotEqualsValue()); } @Override public boolean apply(double inputValue) { return (_neqValue != inputValue); } @Override public boolean apply(double[] inputValues) { return apply(inputValues, inputValues.length); } @Override public boolean apply(double[] inputValues, int length) { // We cannot do binary search since the multi-value columns are not sorted in the raw segment for (int i = 0; i < length; i++) { double inputValue = inputValues[i]; if (_neqValue == inputValue) { return false; } } return true; } } /** * * No dictionary NEQ evaluator for string data type. */ private static final class StringNoDictionaryNeqEvaluator extends BasePredicateEvaluator { private String _neqValue; private StringNoDictionaryNeqEvaluator(NEqPredicate predicate) { _neqValue = predicate.getNotEqualsValue(); } @Override public boolean apply(String inputValue) { return (!inputValue.equals(_neqValue)); } @Override public boolean apply(String[] inputValues) { return apply(inputValues, inputValues.length); } @Override public boolean apply(String[] inputValues, int length) { // We cannot do binary search since the multi-value columns are not sorted in the raw segment for (int i = 0; i < length; i++) { String inputValue = inputValues[i]; if (_neqValue.equals(inputValue)) { return false; } } return true; } } /** * Dictionary based implementation for {@link NotEqualsPredicateEvaluatorFactory} */ private static final class DictionaryBasedNotEqualsPredicateEvaluator extends BasePredicateEvaluator { private int _neqDictValue; private int[] _matchingDictIds; private Dictionary _dictionary; private int[] _nonMatchingDictIds; private DictionaryBasedNotEqualsPredicateEvaluator(NEqPredicate predicate, Dictionary dictionary) { _dictionary = dictionary; _neqDictValue = dictionary.indexOf(predicate.getNotEqualsValue()); if (_neqDictValue > -1) { _nonMatchingDictIds = new int[]{_neqDictValue}; } else { _nonMatchingDictIds = new int[0]; } } @Override public boolean apply(int dictionaryId) { return _neqDictValue != dictionaryId; } @Override public boolean apply(int[] dictionaryIds) { if (_neqDictValue < 0) { return true; } //we cannot do binary search since the multi-value columns are not sorted in the raw segment for (int dictId : dictionaryIds) { if (dictId == _neqDictValue) { return false; } } return true; } @Override public int[] getMatchingDictionaryIds() { // This is expensive for NOT EQ predicate, some operators need this for now. Eventually we should remove the need for exposing matching dict ids if (_matchingDictIds == null) { int size; if (_neqDictValue >= 0) { size = _dictionary.length() - 1; } else { size = _dictionary.length(); } _matchingDictIds = new int[size]; int count = 0; for (int i = 0; i < _dictionary.length(); i++) { if (i != _neqDictValue) { _matchingDictIds[count] = i; count = count + 1; } } } return _matchingDictIds; } @Override public int[] getNonMatchingDictionaryIds() { return _nonMatchingDictIds; } @Override public boolean apply(int[] dictionaryIds, int length) { if (_neqDictValue < 0) { return true; } for (int i = 0; i < length; i++) { int dictId = dictionaryIds[i]; if (dictId == _neqDictValue) { return false; } } return true; } @Override public boolean alwaysFalse() { return _nonMatchingDictIds.length == _dictionary.length(); } } }