/** * 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.query.aggregation.groupby; import com.linkedin.pinot.common.data.FieldSpec; import com.linkedin.pinot.core.common.BlockValSet; import com.linkedin.pinot.core.operator.blocks.TransformBlock; import it.unimi.dsi.fastutil.doubles.Double2IntMap; import it.unimi.dsi.fastutil.doubles.Double2IntOpenHashMap; import it.unimi.dsi.fastutil.floats.Float2IntMap; import it.unimi.dsi.fastutil.floats.Float2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.util.Iterator; import java.util.Map; /** * Implementation of {@link GroupKeyGenerator} interface for single group by column, * in absence of dictionary for the group by column. * */ public class NoDictionarySingleColumnGroupKeyGenerator implements GroupKeyGenerator { private final String _groupByColumn; private final Map _groupKeyMap; private int _numGroupKeys = 0; /** * Constructor for the class. * * @param groupByColumn Column for which to generate group-by keys */ public NoDictionarySingleColumnGroupKeyGenerator(String groupByColumn, FieldSpec.DataType dataType) { _groupByColumn = groupByColumn; _groupKeyMap = createGroupKeyMap(dataType); } @Override public int getGlobalGroupKeyUpperBound() { // Since there's no dictionary, we cannot find the cardinality return Integer.MAX_VALUE; } @Override public void generateKeysForBlock(TransformBlock transformBlock, int[] docIdToGroupKey) { BlockValSet blockValSet = transformBlock.getBlockValueSet(_groupByColumn); FieldSpec.DataType dataType = blockValSet.getValueType(); int numDocs = transformBlock.getNumDocs(); switch (dataType) { case INT: int[] intValues = blockValSet.getIntValuesSV(); for (int i = 0; i < numDocs; i++) { docIdToGroupKey[i] = getKeyForValue(intValues[i]); } break; case LONG: long[] longValues = blockValSet.getLongValuesSV(); for (int i = 0; i < numDocs; i++) { docIdToGroupKey[i] = getKeyForValue(longValues[i]); } break; case FLOAT: float[] floatValues = blockValSet.getFloatValuesSV(); for (int i = 0; i < numDocs; i++) { docIdToGroupKey[i] = getKeyForValue(floatValues[i]); } break; case DOUBLE: double[] doubleValues = blockValSet.getDoubleValuesSV(); for (int i = 0; i < numDocs; i++) { docIdToGroupKey[i] = getKeyForValue(doubleValues[i]); } break; case STRING: String[] stringValues = blockValSet.getStringValuesSV(); for (int i = 0; i < numDocs; i++) { docIdToGroupKey[i] = getKeyForValue(stringValues[i]); } break; default: throw new IllegalArgumentException("Illegal data type for no-dictionary key generator: " + dataType); } } /** * Helper method to create the group-key map, depending on the data type. * Uses primitive maps when possible. * * @param keyType DataType for the key * @return Map */ private Map createGroupKeyMap(FieldSpec.DataType keyType) { Map map; switch (keyType) { case INT: Int2IntMap intMap = new Int2IntOpenHashMap(); intMap.defaultReturnValue(INVALID_ID); map = intMap; break; case LONG: Long2IntOpenHashMap longMap = new Long2IntOpenHashMap(); longMap.defaultReturnValue(INVALID_ID); map = longMap; break; case FLOAT: Float2IntOpenHashMap floatMap = new Float2IntOpenHashMap(); floatMap.defaultReturnValue(INVALID_ID); map = floatMap; break; case DOUBLE: Double2IntOpenHashMap doubleMap = new Double2IntOpenHashMap(); doubleMap.defaultReturnValue(INVALID_ID); map = doubleMap; break; case STRING: Object2IntOpenHashMap<String> stringMap = new Object2IntOpenHashMap<>(); stringMap.defaultReturnValue(INVALID_ID); map = stringMap; break; default: throw new IllegalArgumentException("Illegal data type for no-dictionary key generator: " + keyType); } return map; } @Override public void generateKeysForBlock(TransformBlock transformBlock, int[][] docIdToGroupKeys) { // TODO: Support generating keys for multi-valued columns. throw new UnsupportedOperationException("Operation not supported"); } @Override public int getCurrentGroupKeyUpperBound() { return _groupKeyMap.size(); } @Override public Iterator<GroupKey> getUniqueGroupKeys() { return new GroupKeyIterator(_groupKeyMap); } @Override public void purgeKeys(int[] keysToPurge) { // TODO: Implement purging. throw new UnsupportedOperationException("Purging keys not yet supported in GroupKeyGenerator without dictionary."); } @SuppressWarnings("unchecked") private int getKeyForValue(int value) { Int2IntMap map = (Int2IntMap) _groupKeyMap; int groupId = map.get(value); if (groupId == INVALID_ID) { groupId = _numGroupKeys; map.put(value, _numGroupKeys++); } return groupId; } @SuppressWarnings("unchecked") private int getKeyForValue(long value) { Long2IntMap map = (Long2IntMap) _groupKeyMap; int groupId = map.get(value); if (groupId == INVALID_ID) { groupId = _numGroupKeys; map.put(value, _numGroupKeys++); } return groupId; } @SuppressWarnings("unchecked") private int getKeyForValue(float value) { Float2IntMap map = (Float2IntMap) _groupKeyMap; int groupId = map.get(value); if (groupId == INVALID_ID) { groupId = _numGroupKeys; map.put(value, _numGroupKeys++); } return groupId; } @SuppressWarnings("unchecked") private int getKeyForValue(double value) { Double2IntMap map = (Double2IntMap) _groupKeyMap; int groupId = map.get(value); if (groupId == INVALID_ID) { groupId = _numGroupKeys; map.put(value, _numGroupKeys++); } return groupId; } @SuppressWarnings("unchecked") private int getKeyForValue(String value) { Object2IntMap<String> map = (Object2IntMap<String>) _groupKeyMap; int groupId = map.getInt(value); if (groupId == INVALID_ID) { groupId = _numGroupKeys; map.put(value, _numGroupKeys++); } return groupId; } /** * Iterator for {Group-Key, Group-id) pair. */ class GroupKeyIterator implements Iterator<GroupKey> { Iterator<Map.Entry<Object, Integer>> _iterator; GroupKey _groupKey; @SuppressWarnings("unchecked") public GroupKeyIterator(Map map) { _iterator = (Iterator<Map.Entry<Object, Integer>>) map.entrySet().iterator(); _groupKey = new GroupKey(INVALID_ID, null); } @Override public boolean hasNext() { return _iterator.hasNext(); } @Override public GroupKey next() { Map.Entry<Object, Integer> entry = _iterator.next(); _groupKey.setFirst(entry.getValue()); _groupKey.setSecond(entry.getKey().toString()); return _groupKey; } @Override public void remove() { throw new UnsupportedOperationException(); } } }