/**
* 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.broker.requesthandler;
import com.linkedin.pinot.common.request.BrokerRequest;
import com.linkedin.pinot.common.request.FilterOperator;
import com.linkedin.pinot.common.utils.request.FilterQueryTree;
import com.linkedin.pinot.common.utils.request.RequestUtils;
import com.linkedin.pinot.pql.parsers.Pql2Compiler;
import org.testng.Assert;
import org.testng.annotations.Test;
public class FilterOptimizerTest {
private final BrokerRequestOptimizer _optimizer = new BrokerRequestOptimizer();
// Testing positive cases of flattening the query tree.
@Test
public void testPositive() throws Exception {
Pql2Compiler pql2Compiler = new Pql2Compiler();
BrokerRequest req;
String timeColumn = null;
FilterQueryTree tree;
int numLeaves;
int numOps;
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE (A = 4 AND B = 5) AND (C=7)");
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, timeColumn));
Assert.assertEquals(tree.getChildren().size(), 3);
Assert.assertEquals(tree.getOperator(), FilterOperator.AND);
for (FilterQueryTree node: tree.getChildren()) {
Assert.assertNull(node.getChildren());
Assert.assertEquals(node.getOperator(), FilterOperator.EQUALITY);
}
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE ((A = 4 AND B = 5) AND (C=7)) AND D=8");
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, timeColumn));
Assert.assertEquals(tree.getChildren().size(), 4);
Assert.assertEquals(tree.getOperator(), FilterOperator.AND);
for (FilterQueryTree node: tree.getChildren()) {
Assert.assertNull(node.getChildren());
Assert.assertEquals(node.getOperator(), FilterOperator.EQUALITY);
}
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE (A = 4 OR B = 5) OR (C=7)");
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, timeColumn));
Assert.assertEquals(tree.getChildren().size(), 3);
Assert.assertEquals(tree.getOperator(), FilterOperator.OR);
for (FilterQueryTree node: tree.getChildren()) {
Assert.assertNull(node.getChildren());
Assert.assertEquals(node.getOperator(), FilterOperator.EQUALITY);
}
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE ((A = 4 OR B = 5) OR (C=7)) OR D=8");
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, timeColumn));
Assert.assertEquals(tree.getChildren().size(), 4);
Assert.assertEquals(tree.getOperator(), FilterOperator.OR);
for (FilterQueryTree node: tree.getChildren()) {
Assert.assertNull(node.getChildren());
Assert.assertEquals(node.getOperator(), FilterOperator.EQUALITY);
}
// 3-level test case
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE ((A = 4 OR (B = 5 OR D = 9)) OR (C=7)) OR E=8");
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, timeColumn));
Assert.assertEquals(tree.getChildren().size(), 5);
Assert.assertEquals(tree.getOperator(), FilterOperator.OR);
for (FilterQueryTree node: tree.getChildren()) {
Assert.assertNull(node.getChildren());
Assert.assertEquals(node.getOperator(), FilterOperator.EQUALITY);
}
// Mixed case.
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE ((A = 4 OR (B = 5 AND D = 9)) OR (C=7)) OR E=8");
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, timeColumn));
Assert.assertEquals(tree.getChildren().size(), 4);
Assert.assertEquals(tree.getOperator(), FilterOperator.OR);
numLeaves = 0;
numOps = 0;
for (FilterQueryTree node: tree.getChildren()) {
if (node.getOperator().equals(FilterOperator.EQUALITY)) {
Assert.assertNull(node.getChildren());
numLeaves++;
} else {
Assert.assertNotNull(node.getChildren());
Assert.assertEquals(node.getOperator(), FilterOperator.AND);
numOps++;
}
}
Assert.assertEquals(1, numOps);
Assert.assertEquals(3, numLeaves);
final int maxNodesAtTopLevel = FlattenNestedPredicatesFilterQueryTreeOptimizer.MAX_OPTIMIZING_DEPTH;
String whereClause = constructWhereClause(FilterOperator.OR, maxNodesAtTopLevel + 50);
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE " + whereClause);
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, timeColumn));
Assert.assertEquals(tree.getChildren().size(), maxNodesAtTopLevel+1);
Assert.assertEquals(tree.getOperator(), FilterOperator.OR);
numLeaves = 0;
numOps = 0;
for (FilterQueryTree node: tree.getChildren()) {
if (node.getOperator().equals(FilterOperator.EQUALITY)) {
Assert.assertNull(node.getChildren());
numLeaves++;
} else {
Assert.assertNotNull(node.getChildren());
numOps++;
}
}
Assert.assertEquals(maxNodesAtTopLevel, numLeaves);
Assert.assertEquals(1, numOps);
}
// Tests cases where we should not do any flattening.
@Test
public void testNegative() throws Exception {
Pql2Compiler pql2Compiler = new Pql2Compiler();
BrokerRequest req;
FilterQueryTree tree;
int numLeaves;
int numOps;
req = pql2Compiler.compileToBrokerRequest("SELECT * FROM T WHERE (A = 4 AND (B = 5 OR D = 9))");
tree = RequestUtils.generateFilterQueryTree(_optimizer.optimize(req, null /* timeColumn */));
Assert.assertEquals(tree.getChildren().size(), 2);
Assert.assertEquals(tree.getOperator(), FilterOperator.AND);
numOps = 0;
numLeaves = 0;
for (FilterQueryTree node: tree.getChildren()) {
if (node.getOperator().equals(FilterOperator.OR)) {
Assert.assertEquals(2, node.getChildren().size());
numOps++;
} else {
numLeaves++;
}
}
Assert.assertEquals(1, numOps);
Assert.assertEquals(1, numLeaves);
}
private String constructWhereClause(FilterOperator operator, int depth) {
if (depth == 1) {
return "(A = " + depth + ")";
} else {
return "(A" + depth + " = " + depth + " " + operator + " " + constructWhereClause(operator, depth-1) + ")";
}
}
}