/**
* 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.utils.StringUtil;
import com.linkedin.pinot.common.utils.request.RequestUtils;
import com.linkedin.pinot.pql.parsers.Pql2Compiler;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* Test for MultipleOrEqualitiesToInClauseFilterQueryTreeOptimizer.
*/
public class MultipleOrEqualitiesToInClauseFilterQueryTreeOptimizerTest {
@Test
public void testSimpleOrCase() {
// a = 1 OR a = 2 OR a = 3 should be rewritten to a IN (1, 2, 3)
checkForIdenticalFilterQueryTrees(
"select * from a where a = 1 OR a = 2 OR a = 3",
"select * from a where a IN (1, 2, 3)"
);
}
@Test
public void testWithOtherClause() {
// a = 1 OR a = 2 OR (a = 3 AND b = 4) -> a IN (1, 2) OR (a = 3 AND b = 4)
checkForIdenticalFilterQueryTrees(
"select * from a where a = 1 OR a = 2 OR a = 3 AND b = 4",
"select * from a where a IN (1, 2) OR (a = 3 AND b = 4)"
);
}
@Test
public void testTwoOrClauses() {
// a = 1 OR a = 2 OR a = 3 OR b = 4 OR b = 5 OR b = 6 -> a IN (1,2,3) OR b IN (4,5,6)
checkForIdenticalFilterQueryTrees(
"select * from a where a = 1 OR a = 2 OR a = 3 OR b = 4 OR b = 5 OR b = 6",
"select * from a where a IN (1,2,3) OR b IN (4,5,6)"
);
}
@Test
public void testMultipleOutOfOrderClauses() {
// a = 1 OR a = 2 OR a = 3 OR b = 4 OR b = 5 OR b = 6 -> a IN (1,2,3) OR b IN (4,5,6)
checkForIdenticalFilterQueryTrees(
"select * from a where a = 1 OR b = 4 OR a = 2 OR b = 5 OR a = 3 OR b = 6",
"select * from a where a IN (1,2,3) OR b IN (4,5,6)"
);
}
@Test
public void testDuplicatesAndPullup() {
// a = 1 OR a = 1 -> a = 1 (equality predicate, not IN clause, eg. a IN (1))
// This should also remove the OR node
checkForIdenticalFilterQueryTrees(
"select * from a where a = 1 OR a = 1",
"select * from a where a = 1"
);
// (a = 1 OR a = 1) AND b = 2 -> a = 1 AND b = 2 (no OR node either)
// These are reordered due to an implementation detail
checkOptimizedFilterQueryTreeForQuery(
"select * from a where (a = 1 OR a = 1) AND b = 2",
"AND\n" +
" b EQUALITY [2]\n" +
" a EQUALITY [1]"
);
}
@Test
public void testEqualityAndInMerge() {
// a = 1 OR a IN (2,3,4) -> a IN (1,2,3,4)
checkForIdenticalFilterQueryTrees(
"select * from a where a = 1 OR a IN (2,3,4)",
"select * from a where a IN (1,2,3,4)"
);
}
@Test
public void testSingularInClauseDedupeAndCollapse() {
// a IN (1,1) -> a = 1
checkForIdenticalFilterQueryTrees(
"select * from a where a IN (1, 1) OR a = 1",
"select * from a where a = 1"
);
}
private static final Pql2Compiler COMPILER = new Pql2Compiler();
public static final BrokerRequestOptimizer OPTIMIZER = new BrokerRequestOptimizer();
private String stripIds(String filterQueryTree) {
String[] lines = filterQueryTree.split("\n");
// Strip all FilterQueryTree ids
for (int i = 0; i < lines.length; i++) {
lines[i] = lines[i].replaceAll(" \\(.*", "");
}
return StringUtil.join("\n", lines);
}
private String filterQueryTreeForQuery(String query) {
BrokerRequest brokerRequest = OPTIMIZER.optimize(COMPILER.compileToBrokerRequest(query), null /* timeColumn */);
return RequestUtils.generateFilterQueryTree(brokerRequest).toString();
}
private void checkOptimizedFilterQueryTreeForQuery(String query, String optimizedFilterQueryTree) {
String queryFilterTree = stripIds(filterQueryTreeForQuery(query));
Assert.assertEquals(queryFilterTree, stripIds(optimizedFilterQueryTree), "Optimized filter query trees are different for query " + query);
}
private void checkForIdenticalFilterQueryTrees(String query, String optimizedQuery) {
String queryFilterTree = stripIds(filterQueryTreeForQuery(query));
String optimizedFilterQueryTree = stripIds(filterQueryTreeForQuery(optimizedQuery));
Assert.assertEquals(queryFilterTree, stripIds(optimizedFilterQueryTree), "Optimized filter query trees are different for query " + query);
}
}