/** * 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.transform; import com.linkedin.pinot.common.request.transform.TransformExpressionTree; import com.linkedin.pinot.core.common.BlockValSet; import com.linkedin.pinot.core.operator.blocks.ProjectionBlock; import com.linkedin.pinot.core.operator.docvalsets.ConstantBlockValSet; import com.linkedin.pinot.core.operator.docvalsets.TransformBlockValSet; import com.linkedin.pinot.core.operator.transform.function.TransformFunction; import com.linkedin.pinot.core.operator.transform.function.TransformFunctionFactory; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.Nonnull; /** * Class for evaluating transform expression. */ public class DefaultExpressionEvaluator implements TransformExpressionEvaluator { private final List<TransformExpressionTree> _expressionTrees; /** * Constructor for the class. * @param expressionTrees List of expression trees to evaluate */ public DefaultExpressionEvaluator(@Nonnull List<TransformExpressionTree> expressionTrees) { _expressionTrees = expressionTrees; } /** * {@inheritDoc} * * @param projectionBlock Projection block to evaluate the expression for. */ @Override public Map<String, BlockValSet> evaluate(ProjectionBlock projectionBlock) { Map<String, BlockValSet> resultsMap = new HashMap<>(_expressionTrees.size()); for (TransformExpressionTree expressionTree : _expressionTrees) { // Enough to evaluate an expression once. if (!resultsMap.containsKey(expressionTree.toString())) { resultsMap.put(expressionTree.toString(), evaluateExpression(projectionBlock, expressionTree)); } } return resultsMap; } /** * Helper (recursive) method that walks the expression tree bottom up evaluating * transforms at each level. * * @param projectionBlock Projection block for which to evaluate the expression for * @param expressionTree Expression tree to evaluate * @return Result of the expression transform */ private BlockValSet evaluateExpression(ProjectionBlock projectionBlock, TransformExpressionTree expressionTree) { TransformFunction function = getTransformFunction(expressionTree.getTransformName()); int numDocs = projectionBlock.getNumDocs(); String expressionString = expressionTree.toString(); TransformExpressionTree.ExpressionType expressionType = expressionTree.getExpressionType(); switch (expressionType) { case FUNCTION: List<TransformExpressionTree> children = expressionTree.getChildren(); int numChildren = children.size(); BlockValSet[] transformArgs = new BlockValSet[numChildren]; for (int i = 0; i < numChildren; i++) { transformArgs[i] = evaluateExpression(projectionBlock, children.get(i)); } return new TransformBlockValSet(function, numDocs, transformArgs); case IDENTIFIER: return projectionBlock.getBlockValueSet(expressionString); case LITERAL: return new ConstantBlockValSet(expressionString, numDocs); default: throw new IllegalArgumentException("Illegal expression type in expression evaluator: " + expressionType); } } /** * Helper method to get the transform function from the factory * * @param transformName Name of transform function * @return Instance of transform function */ private TransformFunction getTransformFunction(String transformName) { return (transformName != null) ? TransformFunctionFactory.get(transformName) : null; } }