blob: 10720e4753af0b562595d85df4d12e6ed93dfa07 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.drill.exec.store.mongo;
import java.io.IOException;
import java.util.List;
import org.apache.drill.common.FunctionNames;
import org.apache.drill.common.expression.BooleanOperator;
import org.apache.drill.common.expression.FunctionCall;
import org.apache.drill.common.expression.LogicalExpression;
import org.apache.drill.common.expression.SchemaPath;
import org.apache.drill.common.expression.visitors.AbstractExprVisitor;
import org.apache.drill.exec.store.mongo.common.MongoCompareOp;
import org.bson.Document;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MongoFilterBuilder extends
AbstractExprVisitor<MongoScanSpec, Void, RuntimeException> implements
DrillMongoConstants {
private static final Logger logger = LoggerFactory
.getLogger(MongoFilterBuilder.class);
final MongoGroupScan groupScan;
final LogicalExpression le;
private boolean allExpressionsConverted = true;
public MongoFilterBuilder(MongoGroupScan groupScan,
LogicalExpression conditionExp) {
this.groupScan = groupScan;
this.le = conditionExp;
}
public MongoScanSpec parseTree() {
MongoScanSpec parsedSpec = le.accept(this, null);
if (parsedSpec != null) {
parsedSpec = mergeScanSpecs(FunctionNames.AND, this.groupScan.getScanSpec(),
parsedSpec);
}
return parsedSpec;
}
private MongoScanSpec mergeScanSpecs(String functionName,
MongoScanSpec leftScanSpec, MongoScanSpec rightScanSpec) {
Document newFilter = new Document();
switch (functionName) {
case FunctionNames.AND:
if (leftScanSpec.getFilters() != null
&& rightScanSpec.getFilters() != null) {
newFilter = MongoUtils.andFilterAtIndex(leftScanSpec.getFilters(),
rightScanSpec.getFilters());
} else if (leftScanSpec.getFilters() != null) {
newFilter = leftScanSpec.getFilters();
} else {
newFilter = rightScanSpec.getFilters();
}
break;
case FunctionNames.OR:
newFilter = MongoUtils.orFilterAtIndex(leftScanSpec.getFilters(),
rightScanSpec.getFilters());
}
return new MongoScanSpec(groupScan.getScanSpec().getDbName(), groupScan
.getScanSpec().getCollectionName(), newFilter);
}
public boolean isAllExpressionsConverted() {
return allExpressionsConverted;
}
@Override
public MongoScanSpec visitUnknown(LogicalExpression e, Void value)
throws RuntimeException {
allExpressionsConverted = false;
return null;
}
@Override
public MongoScanSpec visitBooleanOperator(BooleanOperator op, Void value) {
List<LogicalExpression> args = op.args();
MongoScanSpec nodeScanSpec = null;
String functionName = op.getName();
for (int i = 0; i < args.size(); ++i) {
switch (functionName) {
case FunctionNames.AND:
case FunctionNames.OR:
if (nodeScanSpec == null) {
nodeScanSpec = args.get(i).accept(this, null);
} else {
MongoScanSpec scanSpec = args.get(i).accept(this, null);
if (scanSpec != null) {
nodeScanSpec = mergeScanSpecs(functionName, nodeScanSpec, scanSpec);
} else {
allExpressionsConverted = false;
}
}
break;
}
}
return nodeScanSpec;
}
@Override
public MongoScanSpec visitFunctionCall(FunctionCall call, Void value)
throws RuntimeException {
MongoScanSpec nodeScanSpec = null;
String functionName = call.getName();
List<LogicalExpression> args = call.args();
if (MongoCompareFunctionProcessor.isCompareFunction(functionName)) {
MongoCompareFunctionProcessor processor = MongoCompareFunctionProcessor
.process(call);
if (processor.isSuccess()) {
try {
nodeScanSpec = createMongoScanSpec(processor.getFunctionName(),
processor.getPath(), processor.getValue());
} catch (Exception e) {
logger.error(" Failed to creare Filter ", e);
// throw new RuntimeException(e.getMessage(), e);
}
}
} else {
switch (functionName) {
case FunctionNames.AND:
case FunctionNames.OR:
MongoScanSpec leftScanSpec = args.get(0).accept(this, null);
MongoScanSpec rightScanSpec = args.get(1).accept(this, null);
if (leftScanSpec != null && rightScanSpec != null) {
nodeScanSpec = mergeScanSpecs(functionName, leftScanSpec,
rightScanSpec);
} else {
allExpressionsConverted = false;
if (FunctionNames.AND.equals(functionName)) {
nodeScanSpec = leftScanSpec == null ? rightScanSpec : leftScanSpec;
}
}
break;
}
}
if (nodeScanSpec == null) {
allExpressionsConverted = false;
}
return nodeScanSpec;
}
private MongoScanSpec createMongoScanSpec(String functionName,
SchemaPath field, Object fieldValue) throws ClassNotFoundException,
IOException {
// extract the field name
String fieldName = field.getRootSegmentPath();
MongoCompareOp compareOp = null;
switch (functionName) {
case FunctionNames.EQ:
compareOp = MongoCompareOp.EQUAL;
break;
case FunctionNames.NE:
compareOp = MongoCompareOp.NOT_EQUAL;
break;
case FunctionNames.GE:
compareOp = MongoCompareOp.GREATER_OR_EQUAL;
break;
case FunctionNames.GT:
compareOp = MongoCompareOp.GREATER;
break;
case FunctionNames.LE:
compareOp = MongoCompareOp.LESS_OR_EQUAL;
break;
case FunctionNames.LT:
compareOp = MongoCompareOp.LESS;
break;
case FunctionNames.IS_NULL:
case "isNull":
case "is null":
compareOp = MongoCompareOp.IFNULL;
break;
case FunctionNames.IS_NOT_NULL:
case "isNotNull":
case "is not null":
compareOp = MongoCompareOp.IFNOTNULL;
break;
}
if (compareOp != null) {
Document queryFilter = new Document();
if (compareOp == MongoCompareOp.IFNULL) {
queryFilter.put(fieldName,
new Document(MongoCompareOp.EQUAL.getCompareOp(), null));
} else if (compareOp == MongoCompareOp.IFNOTNULL) {
queryFilter.put(fieldName,
new Document(MongoCompareOp.NOT_EQUAL.getCompareOp(), null));
} else {
queryFilter.put(fieldName, new Document(compareOp.getCompareOp(),
fieldValue));
}
return new MongoScanSpec(groupScan.getScanSpec().getDbName(), groupScan
.getScanSpec().getCollectionName(), queryFilter);
}
return null;
}
}