Commit 69af0ae7 by Jeff Hagelberg Committed by Dave Kantor

ATLAS-1195 Clean up DSL Translation (jnhagelb via dkantor)

parent aa15cd0a
/**
* 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.atlas.groovy;
/**
* Abstract implementation of GroovyExpression that adds a convenient
* toString method.
*
*/
public abstract class AbstractGroovyExpression implements GroovyExpression {
@Override
public String toString() {
GroovyGenerationContext ctx = new GroovyGenerationContext();
ctx.setParametersAllowed(false);
generateGroovy(ctx);
return ctx.getQuery();
}
}
/**
* 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.atlas.groovy;
import org.apache.atlas.AtlasException;
/**
* Represents an arithmetic expression such as a+b.
*/
public class ArithmeticExpression extends BinaryExpression {
/**
* Allowed arithmetic operators.
*/
public enum ArithmeticOperator {
PLUS("+"),
MINUS("-"),
TIMES("*"),
DIVIDE("/"),
MODULUS("%");
private String groovyValue;
ArithmeticOperator(String groovyValue) {
this.groovyValue = groovyValue;
}
public String getGroovyValue() {
return groovyValue;
}
public static ArithmeticOperator lookup(String groovyValue) throws AtlasException {
for(ArithmeticOperator op : ArithmeticOperator.values()) {
if (op.getGroovyValue().equals(groovyValue)) {
return op;
}
}
throw new AtlasException("Unknown Operator:" + groovyValue);
}
}
public ArithmeticExpression(GroovyExpression left, ArithmeticOperator op, GroovyExpression right) {
super(left, op.getGroovyValue(), right);
}
}
/**
* 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.atlas.groovy;
/**
* Represents any kind of binary expression. This could
* be an arithmetic expression, such as a + 3, a boolean
* expression such as a < 5, or even a comparison function
* such as a <=> b.
*
*/
public abstract class BinaryExpression extends AbstractGroovyExpression {
private GroovyExpression left;
private GroovyExpression right;
private String op;
public BinaryExpression(GroovyExpression left, String op, GroovyExpression right) {
this.left = left;
this.op = op;
this.right = right;
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
left.generateGroovy(context);
context.append(" ");
context.append(op);
context.append(" ");
right.generateGroovy(context);
}
}
/**
* 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.atlas.groovy;
/**
* Groovy expression that represents a cast.
*/
public class CastExpression extends AbstractGroovyExpression {
private GroovyExpression expr;
private String className;
public CastExpression(GroovyExpression expr, String className) {
this.expr = expr;
this.className =className;
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
context.append("((");
context.append(className);
context.append(")");
expr.generateGroovy(context);
context.append(")");
}
}
/**
* 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.atlas.groovy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Groovy expression that represents a Groovy closure.
*/
public class ClosureExpression extends AbstractGroovyExpression {
private List<String> varNames = new ArrayList<>();
private GroovyExpression body;
public ClosureExpression(GroovyExpression body, String... varNames) {
this.body = body;
this.varNames.addAll(Arrays.asList(varNames));
}
public ClosureExpression(List<String> varNames, GroovyExpression body) {
this.body = body;
this.varNames.addAll(varNames);
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
context.append("{");
if (!varNames.isEmpty()) {
Iterator<String> varIt = varNames.iterator();
while(varIt.hasNext()) {
String varName = varIt.next();
context.append(varName);
if (varIt.hasNext()) {
context.append(", ");
}
}
context.append("->");
}
body.generateGroovy(context);
context.append("}");
}
}
/**
* 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.atlas.groovy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Groovy expression that represents a block of code
* that contains 0 or more statements that are delimited
* by semicolons.
*/
public class CodeBlockExpression extends AbstractGroovyExpression {
private List<GroovyExpression> body = new ArrayList<>();
public void addStatement(GroovyExpression expr) {
body.add(expr);
}
public void addStatements(List<GroovyExpression> exprs) {
body.addAll(exprs);
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
/*
* the L:{} represents a groovy code block; the label is needed
* to distinguish it from a groovy closure.
*/
context.append("L:{");
Iterator<GroovyExpression> stmtIt = body.iterator();
while(stmtIt.hasNext()) {
GroovyExpression stmt = stmtIt.next();
stmt.generateGroovy(context);
if (stmtIt.hasNext()) {
context.append(";");
}
}
context.append("}");
}
}
/**
* 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.atlas.groovy;
import org.apache.atlas.AtlasException;
/**
* Represents an expression that compares two expressions
* using a comparison operator (==, <, >, etc).
*
*/
public class ComparisonExpression extends BinaryExpression {
/**
* Allowed comparison operators.
*
*/
public enum ComparisonOperator {
GREATER_THAN_EQ_(">="),
GREATER_THAN(">"),
EQUALS("=="),
NOT_EQUALS("!="),
LESS_THAN("<"),
LESS_THAN_EQ("<=");
private String groovyValue;
ComparisonOperator(String groovyValue) {
this.groovyValue = groovyValue;
}
public String getGroovyValue() {
return groovyValue;
}
public static ComparisonOperator lookup(String groovyValue) throws AtlasException {
for(ComparisonOperator op : ComparisonOperator.values()) {
if (op.getGroovyValue().equals(groovyValue)) {
return op;
}
}
throw new AtlasException("Unknown Operator:" + groovyValue);
}
}
public ComparisonExpression(GroovyExpression left, ComparisonOperator op, GroovyExpression right) {
super(left, op.getGroovyValue(), right);
}
}
/**
* 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.atlas.groovy;
/**
* Represents an expression that compares two expressions using
* the Groovy "spaceship" operator. This is basically the
* same as calling left.compareTo(right), except that it has
* built-in null handling and some other nice features.
*
*/
public class ComparisonOperatorExpression extends BinaryExpression {
public ComparisonOperatorExpression(GroovyExpression left, GroovyExpression right) {
super(left, "<=>", right);
}
}
/**
* 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.atlas.groovy;
/**
* Groovy expression that accesses a field in an object.
*/
public class FieldExpression extends AbstractGroovyExpression {
private GroovyExpression target;
private String fieldName;
public FieldExpression(GroovyExpression target, String fieldName) {
this.target = target;
this.fieldName = fieldName;
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
target.generateGroovy(context);
context.append(".'");
context.append(fieldName);
context.append("'");
}
}
/**
* 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.atlas.groovy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Groovy expression that calls a method on an object.
*/
public class FunctionCallExpression extends AbstractGroovyExpression {
// null for global functions
private GroovyExpression target;
private String functionName;
private List<GroovyExpression> arguments = new ArrayList<GroovyExpression>();
public FunctionCallExpression(String functionName, List<? extends GroovyExpression> arguments) {
this.target = null;
this.functionName = functionName;
this.arguments.addAll(arguments);
}
public FunctionCallExpression(GroovyExpression target, String functionName,
List<? extends GroovyExpression> arguments) {
this.target = target;
this.functionName = functionName;
this.arguments.addAll(arguments);
}
public FunctionCallExpression(String functionName, GroovyExpression... arguments) {
this.target = null;
this.functionName = functionName;
this.arguments.addAll(Arrays.asList(arguments));
}
public FunctionCallExpression(GroovyExpression target, String functionName, GroovyExpression... arguments) {
this.target = target;
this.functionName = functionName;
this.arguments.addAll(Arrays.asList(arguments));
}
public void addArgument(GroovyExpression expr) {
arguments.add(expr);
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
if (target != null) {
target.generateGroovy(context);
context.append(".");
}
context.append(functionName);
context.append("(");
Iterator<GroovyExpression> it = arguments.iterator();
while (it.hasNext()) {
GroovyExpression expr = it.next();
expr.generateGroovy(context);
if (it.hasNext()) {
context.append(", ");
}
}
context.append(")");
}
}
/**
* 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.atlas.groovy;
/**
* Represents an expression in the Groovy programming language, which
* is the language that Gremlin scripts are written and interpreted in.
*/
public interface GroovyExpression {
/**
* Generates a groovy script from the expression.
*
* @param context
*/
void generateGroovy(GroovyGenerationContext context);
}
/**
* 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.atlas.groovy;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Context used when generating Groovy queries. Keeps track of query parameters
* that are created during the process as well as the portion of the query that
* has been generated so far.
*
*/
public class GroovyGenerationContext {
private boolean parametersAllowed = true;
private int parameterCount = 0;
private Map<String, Object> parameterValues = new HashMap<>();
//used to build up the groovy script
private StringBuilder generatedQuery = new StringBuilder();
public void setParametersAllowed(boolean value) {
this.parametersAllowed = value;
}
public GroovyExpression addParameter(Object value) {
if (this.parametersAllowed) {
String parameterName = "p" + (++parameterCount);
this.parameterValues.put(parameterName, value);
return new IdentifierExpression(parameterName);
} else {
LiteralExpression expr = new LiteralExpression(value);
expr.setTranslateToParameter(false);
return expr;
}
}
public StringBuilder append(String gremlin) {
this.generatedQuery.append(gremlin);
return generatedQuery;
}
public String getQuery() {
return generatedQuery.toString();
}
public Map<String, Object> getParameters() {
return Collections.unmodifiableMap(parameterValues);
}
}
/**
* 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.atlas.groovy;
/**
* Groovy expression that references the variable with the given name.
*
*/
public class IdentifierExpression extends AbstractGroovyExpression {
private String varName;
public IdentifierExpression(String varName) {
this.varName = varName;
}
public String getVariableName() {
return varName;
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
context.append(varName);
}
}
/**
* 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.atlas.groovy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* Groovy expression that represents a list literal.
*/
public class ListExpression extends AbstractGroovyExpression {
private List<GroovyExpression> values = new ArrayList<>();
public ListExpression(GroovyExpression... values) {
this.values = Arrays.asList(values);
}
public ListExpression(List<? extends GroovyExpression> values) {
this.values.addAll(values);
}
public void addArgument(GroovyExpression expr) {
values.add(expr);
}
public void generateGroovy(GroovyGenerationContext context) {
context.append("[");
Iterator<GroovyExpression> it = values.iterator();
while(it.hasNext()) {
GroovyExpression expr = it.next();
expr.generateGroovy(context);
if (it.hasNext()) {
context.append(", ");
}
}
context.append("]");
}
}
/**
* 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.atlas.groovy;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Represents a literal value.
*/
public class LiteralExpression implements GroovyExpression {
public static final LiteralExpression TRUE = new LiteralExpression(true);
public static final LiteralExpression FALSE = new LiteralExpression(false);
public static final LiteralExpression NULL = new LiteralExpression(null);
private Object value;
private boolean translateToParameter = true;
private boolean addTypeSuffix = false;
public LiteralExpression(Object value, boolean addTypeSuffix) {
this.value = value;
this.translateToParameter = value instanceof String;
this.addTypeSuffix = addTypeSuffix;
}
public LiteralExpression(Object value) {
this.value = value;
this.translateToParameter = value instanceof String;
}
private String getTypeSuffix() {
if (!addTypeSuffix) {
return "";
}
if (value.getClass() == Long.class) {
return "L";
}
if (value.getClass() == Float.class) {
return "f";
}
if (value.getClass() == Double.class) {
return "d";
}
return "";
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
if (translateToParameter) {
GroovyExpression result = context.addParameter(value);
result.generateGroovy(context);
return;
}
if (value instanceof String) {
String escapedValue = getEscapedValue();
context.append("'");
context.append(escapedValue);
context.append("'");
} else {
context.append(String.valueOf(value));
context.append(getTypeSuffix());
}
}
private String getEscapedValue() {
String escapedValue = (String)value;
escapedValue = escapedValue.replaceAll(Pattern.quote("\\"), Matcher.quoteReplacement("\\\\"));
escapedValue = escapedValue.replaceAll(Pattern.quote("'"), Matcher.quoteReplacement("\\'"));
return escapedValue;
}
public void setTranslateToParameter(boolean translateToParameter) {
this.translateToParameter = translateToParameter;
}
}
/**
* 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.atlas.groovy;
/**
* Represents a logical (and/or) expression.
*
*/
public class LogicalExpression extends BinaryExpression {
/**
* Allowed logical operators.
*
*/
public enum LogicalOperator {
AND("&&"),
OR("||");
private String groovyValue;
LogicalOperator(String groovyValue) {
this.groovyValue = groovyValue;
}
public String getGroovyValue() {
return groovyValue;
}
}
public LogicalExpression(GroovyExpression left, LogicalOperator op, GroovyExpression right) {
super(left, op.getGroovyValue(), right);
}
}
/**
* 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.atlas.groovy;
/**
* Represents an "exclusive" range expression, e.g. [0..&lt;10].
*/
public class RangeExpression extends AbstractGroovyExpression {
private GroovyExpression parent;
private int offset;
private int count;
public RangeExpression(GroovyExpression parent, int offset, int count) {
this.parent = parent;
this.offset = offset;
this.count = count;
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
parent.generateGroovy(context);
context.append(" [");
new LiteralExpression(offset).generateGroovy(context);
context.append("..<");
new LiteralExpression(count).generateGroovy(context);
context.append("]");
}
}
/**
* 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.atlas.groovy;
/**
* Groovy expression that represents the ternary operator (expr ? trueValue :
* falseValue)
*/
public class TernaryOperatorExpression extends AbstractGroovyExpression {
private GroovyExpression booleanExpr;
private GroovyExpression trueValue;
private GroovyExpression falseValue;
public TernaryOperatorExpression(GroovyExpression booleanExpr, GroovyExpression trueValue,
GroovyExpression falseValue) {
this.booleanExpr = booleanExpr;
this.trueValue = trueValue;
this.falseValue = falseValue;
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
context.append("((");
booleanExpr.generateGroovy(context);
context.append(") ? (");
trueValue.generateGroovy(context);
context.append(") : (");
falseValue.generateGroovy(context);
context.append("))");
}
public String toString() {
GroovyGenerationContext context = new GroovyGenerationContext();
generateGroovy(context);
return context.getQuery();
}
}
/**
* 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.atlas.groovy;
/**
* Groovy expression that represents a type coersion (e.g obj as Set).
*/
public class TypeCoersionExpression extends AbstractGroovyExpression {
private GroovyExpression expr;
private String className;
public TypeCoersionExpression(GroovyExpression expr, String className) {
this.expr = expr;
this.className =className;
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
context.append("(");
expr.generateGroovy(context);
context.append(")");
context.append(" as ");
context.append(className);
}
}
/**
* 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.atlas.groovy;
/**
* Groovy statement that assigns a value to a variable.
*/
public class VariableAssignmentExpression extends AbstractGroovyExpression {
private String type = null;
private String name;
private GroovyExpression value;
/**
* @param string
* @param v
*/
public VariableAssignmentExpression(String type, String name, GroovyExpression v) {
this.type = type;
this.name = name;
this.value = v;
}
public VariableAssignmentExpression(String name, GroovyExpression v) {
this(null, name, v);
}
@Override
public void generateGroovy(GroovyGenerationContext context) {
if (type == null) {
context.append("def ");
} else {
context.append(type);
context.append(" ");
}
context.append(name);
context.append(" = ");
value.generateGroovy(context);
}
}
......@@ -23,6 +23,7 @@ import java.util.Set;
import javax.script.ScriptException;
import org.apache.atlas.groovy.GroovyExpression;
import org.apache.atlas.typesystem.types.IDataType;
/**
......@@ -204,7 +205,7 @@ public interface AtlasGraph<V, E> {
* @param type
* @return
*/
String generatePersisentToLogicalConversionExpression(String valueExpr, IDataType<?> type);
GroovyExpression generatePersisentToLogicalConversionExpression(GroovyExpression valueExpr, IDataType<?> type);
/**
* Indicates whether or not stored values with the specified type need to be converted
......@@ -240,7 +241,7 @@ public interface AtlasGraph<V, E> {
*
* @return
*/
String getInitialIndexedPredicate();
GroovyExpression getInitialIndexedPredicate(GroovyExpression parent);
/**
* As an optimization, a graph database implementation may want to retrieve additional
......@@ -249,7 +250,7 @@ public interface AtlasGraph<V, E> {
* avoid the need to make an extra REST API call to look up those edges. For implementations
* that do not require any kind of transform, an empty String should be returned.
*/
String getOutputTransformationPredicate(boolean isSelect, boolean isPath);
GroovyExpression addOutputTransformationPredicate(GroovyExpression expr, boolean isSelect, boolean isPath);
/**
* Executes a Gremlin script, returns an object with the result.
......
......@@ -34,6 +34,7 @@ import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.apache.atlas.groovy.GroovyExpression;
import org.apache.atlas.repository.graphdb.AtlasEdge;
import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.repository.graphdb.AtlasGraphManagement;
......@@ -286,7 +287,7 @@ public class Titan0Graph implements AtlasGraph<Titan0Vertex, Titan0Edge> {
}
@Override
public String generatePersisentToLogicalConversionExpression(String expr, IDataType<?> type) {
public GroovyExpression generatePersisentToLogicalConversionExpression(GroovyExpression expr, IDataType<?> type) {
//nothing special needed, value is stored in required type
return expr;
......@@ -304,13 +305,13 @@ public class Titan0Graph implements AtlasGraph<Titan0Vertex, Titan0Edge> {
}
@Override
public String getInitialIndexedPredicate() {
return "";
public GroovyExpression getInitialIndexedPredicate(GroovyExpression expr) {
return expr;
}
@Override
public String getOutputTransformationPredicate(boolean inSelect, boolean isPath) {
return "";
public GroovyExpression addOutputTransformationPredicate(GroovyExpression expr, boolean inSelect, boolean isPath) {
return expr;
}
public Iterable<AtlasEdge<Titan0Vertex, Titan0Edge>> wrapEdges(Iterator<Edge> it) {
......
......@@ -9,6 +9,7 @@ ATLAS-1060 Add composite indexes for exact match performance improvements for al
ATLAS-1127 Modify creation and modification timestamps to Date instead of Long(sumasai)
ALL CHANGES:
ATLAS-1195 Clean up DSL Translation (jnhagelb via dkantor)
ATLAS-1139 Parameter name of a HDFS DataSet entity should contain filesystem path (svimal2106 via sumasai)
ATLAS-1200 Error Catalog enhancement (apoorvnaik via sumasai)
ATLAS-1207 Dataset exists query in lineage APIs takes longer (shwethags)
......
......@@ -23,16 +23,16 @@ import java.util.List;
import javax.inject.Inject;
import org.apache.atlas.AtlasException;
import org.apache.atlas.query.Expressions;
import org.apache.atlas.groovy.GroovyExpression;
import org.apache.atlas.query.GraphPersistenceStrategies;
import org.apache.atlas.query.GraphPersistenceStrategies$class;
import org.apache.atlas.query.IntSequence;
import org.apache.atlas.query.TypeUtils;
import org.apache.atlas.repository.Constants;
import org.apache.atlas.repository.MetadataRepository;
import org.apache.atlas.repository.RepositoryException;
import org.apache.atlas.repository.graph.GraphBackedMetadataRepository;
import org.apache.atlas.repository.graph.GraphHelper;
import org.apache.atlas.repository.graphdb.AtlasEdgeDirection;
import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.repository.graphdb.AtlasVertex;
import org.apache.atlas.repository.graphdb.GremlinVersion;
......@@ -105,11 +105,6 @@ public class DefaultGraphPersistenceStrategy implements GraphPersistenceStrategi
}
@Override
public String fieldPrefixInSelect() {
return "it";
}
@Override
public Id getIdFromVertex(String dataTypeName, AtlasVertex vertex) {
return GraphHelper.getIdFromVertex(dataTypeName, vertex);
}
......@@ -212,28 +207,13 @@ public class DefaultGraphPersistenceStrategy implements GraphPersistenceStrategi
}
@Override
public String gremlinCompOp(Expressions.ComparisonExpression op) {
return GraphPersistenceStrategies$class.gremlinCompOp(this, op);
}
@Override
public String gremlinPrimitiveOp(Expressions.ComparisonExpression op) {
return GraphPersistenceStrategies$class.gremlinPrimitiveOp(this, op);
}
@Override
public String loopObjectExpression(IDataType<?> dataType) {
return GraphPersistenceStrategies$class.loopObjectExpression(this, dataType);
public AtlasEdgeDirection instanceToTraitEdgeDirection() {
return AtlasEdgeDirection.OUT;
}
@Override
public String instanceToTraitEdgeDirection() {
return "out";
}
@Override
public String traitToInstanceEdgeDirection() {
return "in";
public AtlasEdgeDirection traitToInstanceEdgeDirection() {
return AtlasEdgeDirection.IN;
}
@Override
......@@ -247,33 +227,28 @@ public class DefaultGraphPersistenceStrategy implements GraphPersistenceStrategi
}
@Override
public scala.collection.Seq<String> typeTestExpression(String typeName, IntSequence intSeq) {
return GraphPersistenceStrategies$class.typeTestExpression(this, typeName, intSeq);
}
@Override
public boolean collectTypeInstancesIntoVar() {
return GraphPersistenceStrategies$class.collectTypeInstancesIntoVar(this);
}
@Override
public boolean addGraphVertexPrefix(scala.collection.Traversable<String> preStatements) {
public boolean addGraphVertexPrefix(scala.collection.Traversable<GroovyExpression> preStatements) {
return GraphPersistenceStrategies$class.addGraphVertexPrefix(this, preStatements);
}
@Override
public GremlinVersion getSupportedGremlinVersion() {
return GraphPersistenceStrategies$class.getSupportedGremlinVersion(this);
}
@Override
public String generatePersisentToLogicalConversionExpression(String expr, IDataType<?> t) {
public GroovyExpression generatePersisentToLogicalConversionExpression(GroovyExpression expr, IDataType<?> t) {
return GraphPersistenceStrategies$class.generatePersisentToLogicalConversionExpression(this,expr, t);
}
@Override
public String initialQueryCondition() {
return GraphPersistenceStrategies$class.initialQueryCondition(this);
public GroovyExpression addInitialQueryCondition(GroovyExpression expr) {
return GraphPersistenceStrategies$class.addInitialQueryCondition(this, expr);
}
@Override
......
/**
* 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.atlas.gremlin;
import java.util.ArrayList;
import java.util.List;
import org.apache.atlas.AtlasException;
import org.apache.atlas.groovy.ClosureExpression;
import org.apache.atlas.groovy.ComparisonExpression;
import org.apache.atlas.groovy.ComparisonOperatorExpression;
import org.apache.atlas.groovy.FieldExpression;
import org.apache.atlas.groovy.FunctionCallExpression;
import org.apache.atlas.groovy.GroovyExpression;
import org.apache.atlas.groovy.IdentifierExpression;
import org.apache.atlas.groovy.ListExpression;
import org.apache.atlas.groovy.LiteralExpression;
import org.apache.atlas.groovy.LogicalExpression;
import org.apache.atlas.groovy.RangeExpression;
import org.apache.atlas.groovy.TernaryOperatorExpression;
import org.apache.atlas.groovy.ComparisonExpression.ComparisonOperator;
import org.apache.atlas.groovy.LogicalExpression.LogicalOperator;
import org.apache.atlas.query.GraphPersistenceStrategies;
import org.apache.atlas.query.TypeUtils.FieldInfo;
import org.apache.atlas.typesystem.types.IDataType;
/**
* Generates gremlin query expressions using Gremlin 2 syntax.
*
*/
public class Gremlin2ExpressionFactory extends GremlinExpressionFactory {
private static final String LOOP_METHOD = "loop";
private static final String CONTAINS = "contains";
private static final String LOOP_COUNT_FIELD = "loops";
private static final String PATH_FIELD = "path";
private static final String ENABLE_PATH_METHOD = "enablePath";
private static final String BACK_METHOD = "back";
@Override
public GroovyExpression generateLogicalExpression(GroovyExpression parent, String operator, List<GroovyExpression> operands) {
return new FunctionCallExpression(parent, operator, operands);
}
@Override
public GroovyExpression generateBackReferenceExpression(GroovyExpression parent, boolean inSelect, String alias) {
if (inSelect) {
return getFieldInSelect();
}
else {
return new FunctionCallExpression(parent, BACK_METHOD, new LiteralExpression(alias));
}
}
@Override
public GroovyExpression getLoopExpressionParent(GroovyExpression inputQry) {
return inputQry;
}
@Override
public GroovyExpression generateLoopExpression(GroovyExpression parent,GraphPersistenceStrategies s, IDataType dataType, GroovyExpression loopExpr, String alias, Integer times) {
GroovyExpression emitExpr = generateLoopEmitExpression(s, dataType);
//note that in Gremlin 2 (unlike Gremlin 3), the parent is not explicitly used. It is incorporated
//in the loopExpr.
GroovyExpression whileFunction = null;
if(times != null) {
GroovyExpression loopsExpr = new FieldExpression(getItVariable(), LOOP_COUNT_FIELD);
GroovyExpression timesExpr = new LiteralExpression(times);
whileFunction = new ClosureExpression(new ComparisonExpression(loopsExpr, ComparisonOperator.LESS_THAN, timesExpr));
}
else {
GroovyExpression pathExpr = new FieldExpression(getItVariable(),PATH_FIELD);
GroovyExpression itObjectExpr = getCurrentObjectExpression();
GroovyExpression pathContainsExpr = new FunctionCallExpression(pathExpr, CONTAINS, itObjectExpr);
whileFunction = new ClosureExpression(new TernaryOperatorExpression(pathContainsExpr, LiteralExpression.FALSE, LiteralExpression.TRUE));
}
GroovyExpression emitFunction = new ClosureExpression(emitExpr);
GroovyExpression loopCall = new FunctionCallExpression(loopExpr, LOOP_METHOD, new LiteralExpression(alias), whileFunction, emitFunction);
return new FunctionCallExpression(loopCall, ENABLE_PATH_METHOD);
}
@Override
public GroovyExpression typeTestExpression(GraphPersistenceStrategies s, String typeName, GroovyExpression itRef) {
GroovyExpression typeAttrExpr = new FieldExpression(itRef, s.typeAttributeName());
GroovyExpression superTypeAttrExpr = new FieldExpression(itRef, s.superTypeAttributeName());
GroovyExpression typeNameExpr = new LiteralExpression(typeName);
GroovyExpression typeMatchesExpr = new ComparisonExpression(typeAttrExpr, ComparisonOperator.EQUALS, typeNameExpr);
GroovyExpression isSuperTypeExpr = new FunctionCallExpression(superTypeAttrExpr, CONTAINS, typeNameExpr);
GroovyExpression hasSuperTypeAttrExpr = superTypeAttrExpr;
GroovyExpression superTypeMatchesExpr = new TernaryOperatorExpression(hasSuperTypeAttrExpr, isSuperTypeExpr, LiteralExpression.FALSE);
return new LogicalExpression(typeMatchesExpr, LogicalOperator.OR, superTypeMatchesExpr);
}
@Override
public GroovyExpression generateSelectExpression(GroovyExpression parent, List<LiteralExpression> sourceNames,
List<GroovyExpression> srcExprs) {
GroovyExpression srcNamesExpr = new ListExpression(sourceNames);
List<GroovyExpression> selectArgs = new ArrayList<>();
selectArgs.add(srcNamesExpr);
for(GroovyExpression expr : srcExprs) {
selectArgs.add(new ClosureExpression(expr));
}
return new FunctionCallExpression(parent, SELECT_METHOD, selectArgs);
}
@Override
public GroovyExpression generateFieldExpression(GroovyExpression parent, FieldInfo fInfo, String propertyName, boolean inSelect) {
return new FieldExpression(parent, propertyName);
}
@Override
public GroovyExpression generateHasExpression(GraphPersistenceStrategies s, GroovyExpression parent, String propertyName, String symbol,
GroovyExpression requiredValue, FieldInfo fInfo) throws AtlasException {
GroovyExpression op = gremlin2CompOp(symbol);
GroovyExpression propertyNameExpr = new LiteralExpression(propertyName);
return new FunctionCallExpression(parent, HAS_METHOD, propertyNameExpr, op, requiredValue);
}
private GroovyExpression gremlin2CompOp(String op) throws AtlasException {
GroovyExpression tExpr = new IdentifierExpression("T");
if(op.equals("=")) {
return new FieldExpression(tExpr, "eq");
}
if(op.equals("!=")) {
return new FieldExpression(tExpr, "neq");
}
if(op.equals(">")) {
return new FieldExpression(tExpr, "gt");
}
if(op.equals(">=")) {
return new FieldExpression(tExpr, "gte");
}
if(op.equals("<")) {
return new FieldExpression(tExpr, "lt");
}
if(op.equals("<=")) {
return new FieldExpression(tExpr, "lte");
}
throw new AtlasException("Comparison operator " + op + " not supported in Gremlin");
}
@Override
protected GroovyExpression initialExpression(GraphPersistenceStrategies s, GroovyExpression varExpr) {
return new FunctionCallExpression(varExpr, "_");
}
@Override
public GroovyExpression generateLimitExpression(GroovyExpression parent, int offset, int totalRows) {
return new RangeExpression(parent, offset, totalRows);
}
@Override
public List<GroovyExpression> getOrderFieldParents() {
GroovyExpression itExpr = getItVariable();
List<GroovyExpression> result = new ArrayList<>(2);
result.add(new FieldExpression(itExpr, "a"));
result.add(new FieldExpression(itExpr, "b"));
return result;
}
@Override
public GroovyExpression generateOrderByExpression(GroovyExpression parent, List<GroovyExpression> translatedOrderBy, boolean isAscending) {
GroovyExpression itExpr = getItVariable();
GroovyExpression aPropertyExpr = translatedOrderBy.get(0);
GroovyExpression bPropertyExpr = translatedOrderBy.get(1);
GroovyExpression aPropertyNotNull = new ComparisonExpression(aPropertyExpr, ComparisonOperator.NOT_EQUALS, LiteralExpression.NULL);
GroovyExpression bPropertyNotNull = new ComparisonExpression(bPropertyExpr, ComparisonOperator.NOT_EQUALS, LiteralExpression.NULL);
GroovyExpression aCondition = new TernaryOperatorExpression(aPropertyNotNull, new FunctionCallExpression(aPropertyExpr,TO_LOWER_CASE_METHOD), aPropertyExpr);
GroovyExpression bCondition = new TernaryOperatorExpression(bPropertyNotNull, new FunctionCallExpression(bPropertyExpr,TO_LOWER_CASE_METHOD), bPropertyExpr);
GroovyExpression comparisonFunction = null;
if(isAscending) {
comparisonFunction = new ComparisonOperatorExpression(aCondition, bCondition);
}
else {
comparisonFunction = new ComparisonOperatorExpression(bCondition, aCondition);
}
return new FunctionCallExpression(parent, ORDER_METHOD, new ClosureExpression(comparisonFunction));
}
@Override
public GroovyExpression getAnonymousTraversalExpression() {
return new FunctionCallExpression("_");
}
@Override
public GroovyExpression getFieldInSelect() {
return getItVariable();
}
}
/**
* 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.atlas.gremlin;
import java.util.ArrayList;
import java.util.List;
import org.apache.atlas.AtlasException;
import org.apache.atlas.groovy.CastExpression;
import org.apache.atlas.groovy.ClosureExpression;
import org.apache.atlas.groovy.ComparisonExpression;
import org.apache.atlas.groovy.ComparisonOperatorExpression;
import org.apache.atlas.groovy.FunctionCallExpression;
import org.apache.atlas.groovy.GroovyExpression;
import org.apache.atlas.groovy.IdentifierExpression;
import org.apache.atlas.groovy.LiteralExpression;
import org.apache.atlas.groovy.LogicalExpression;
import org.apache.atlas.groovy.TypeCoersionExpression;
import org.apache.atlas.groovy.ComparisonExpression.ComparisonOperator;
import org.apache.atlas.groovy.LogicalExpression.LogicalOperator;
import org.apache.atlas.query.GraphPersistenceStrategies;
import org.apache.atlas.query.TypeUtils.FieldInfo;
import org.apache.atlas.repository.graph.AtlasGraphProvider;
import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.typesystem.types.AttributeInfo;
import org.apache.atlas.typesystem.types.IDataType;
/**
* Generates gremlin query expressions using Gremlin 3 syntax.
*
*/
public class Gremlin3ExpressionFactory extends GremlinExpressionFactory {
private static final String VERTEX_LIST_CLASS = "List<Vertex>";
private static final String VERTEX_ARRAY_CLASS = "Vertex[]";
private static final String OBJECT_ARRAY_CLASS = "Object[]";
private static final String VERTEX_CLASS = "Vertex";
private static final String FUNCTION_CLASS = "Function";
private static final String VALUE_METHOD = "value";
private static final String IS_PRESENT_METHOD = "isPresent";
private static final String MAP_METHOD = "map";
private static final String VALUES_METHOD = "values";
private static final String GET_METHOD = "get";
private static final String OR_ELSE_METHOD = "orElse";
private static final String PROPERTY_METHOD = "property";
private static final String BY_METHOD = "by";
private static final String EQ_METHOD = "eq";
private static final String EMIT_METHOD = "emit";
private static final String TIMES_METHOD = "times";
private static final String REPEAT_METHOD = "repeat";
private static final String RANGE_METHOD = "range";
private static final String LAST_METHOD = "last";
private static final String WHERE_METHOD = "where";
private static final String TO_STRING_METHOD = "toString";
@Override
public GroovyExpression generateLogicalExpression(GroovyExpression parent, String operator,
List<GroovyExpression> operands) {
if (operands.size() == 1) {
// gremlin 3 treats one element expressions as 'false'. Avoid
// creating a boolean expression in this case. Inline the expression
// note: we can't simply omit it, since it will cause us to traverse
// the edge!
// use 'where' instead
GroovyExpression expr = operands.get(0);
// if child is a back expression, that expression becomes an
// argument to where
return new FunctionCallExpression(parent, WHERE_METHOD, expr);
} else {
// Gremlin 3 does not support _() syntax
//
return new FunctionCallExpression(parent, operator, operands);
}
}
@Override
public GroovyExpression generateBackReferenceExpression(GroovyExpression parent, boolean inSelect, String alias) {
if (inSelect) {
return getFieldInSelect();
} else {
return new FunctionCallExpression(parent, SELECT_METHOD, new LiteralExpression(alias));
}
}
@Override
public GroovyExpression typeTestExpression(GraphPersistenceStrategies s, String typeName, GroovyExpression itRef) {
LiteralExpression typeAttrExpr = new LiteralExpression(s.typeAttributeName());
LiteralExpression superTypeAttrExpr = new LiteralExpression(s.superTypeAttributeName());
LiteralExpression typeNameExpr = new LiteralExpression(typeName);
FunctionCallExpression result = new FunctionCallExpression(HAS_METHOD, typeAttrExpr, new FunctionCallExpression(EQ_METHOD, typeNameExpr));
result = new FunctionCallExpression(result, "or");
result = new FunctionCallExpression(HAS_METHOD, superTypeAttrExpr, new FunctionCallExpression(EQ_METHOD, typeNameExpr));
return result;
}
@Override
public GroovyExpression generateLoopExpression(GroovyExpression parent,GraphPersistenceStrategies s, IDataType dataType, GroovyExpression loopExpr, String alias, Integer times) {
GroovyExpression emitExpr = generateLoopEmitExpression(s, dataType);
GroovyExpression result = new FunctionCallExpression(parent, REPEAT_METHOD, loopExpr);
if (times != null) {
GroovyExpression timesExpr = new LiteralExpression(times);
result = new FunctionCallExpression(result, TIMES_METHOD, timesExpr);
}
result = new FunctionCallExpression(result, EMIT_METHOD, emitExpr);
return result;
}
@Override
public GroovyExpression getLoopExpressionParent(GroovyExpression inputQry) {
GroovyExpression curTraversal = new IdentifierExpression("__");
return curTraversal;
}
@Override
public GroovyExpression generateSelectExpression(GroovyExpression parent, List<LiteralExpression> sourceNames,
List<GroovyExpression> srcExprs) {
FunctionCallExpression result = new FunctionCallExpression(parent, SELECT_METHOD, sourceNames);
for (GroovyExpression expr : srcExprs) {
GroovyExpression closure = new ClosureExpression(expr);
GroovyExpression castClosure = new TypeCoersionExpression(closure, FUNCTION_CLASS);
result = new FunctionCallExpression(result, BY_METHOD, castClosure);
}
return result;
}
@Override
public GroovyExpression generateFieldExpression(GroovyExpression parent, FieldInfo fInfo,
String propertyName, boolean inSelect) {
AttributeInfo attrInfo = fInfo.attrInfo();
IDataType attrType = attrInfo.dataType();
GroovyExpression propertyNameExpr = new LiteralExpression(propertyName);
AtlasGraph graph = AtlasGraphProvider.getGraphInstance();
if (inSelect) {
GroovyExpression expr = new FunctionCallExpression(parent, PROPERTY_METHOD, propertyNameExpr);
expr = new FunctionCallExpression(expr, OR_ELSE_METHOD, LiteralExpression.NULL);
return graph.generatePersisentToLogicalConversionExpression(expr, attrType);
} else {
GroovyExpression unmapped = new FunctionCallExpression(parent, VALUES_METHOD, propertyNameExpr);
if (graph.isPropertyValueConversionNeeded(attrType)) {
GroovyExpression toConvert = new FunctionCallExpression(getItVariable(), GET_METHOD);
GroovyExpression conversionFunction = graph.generatePersisentToLogicalConversionExpression(toConvert,
attrType);
return new FunctionCallExpression(unmapped, MAP_METHOD, new ClosureExpression(conversionFunction));
} else {
return unmapped;
}
}
}
private ComparisonOperator getGroovyOperator(String symbol) throws AtlasException {
String toFind = symbol;
if (toFind.equals("=")) {
toFind = "==";
}
return ComparisonOperator.lookup(toFind);
}
private String getComparisonFunction(String op) throws AtlasException {
if (op.equals("=")) {
return "eq";
}
if (op.equals("!=")) {
return "neq";
}
if (op.equals(">")) {
return "gt";
}
if (op.equals(">=")) {
return "gte";
}
if (op.equals("<")) {
return "lt";
}
if (op.equals("<=")) {
return "lte";
}
throw new AtlasException("Comparison operator " + op + " not supported in Gremlin");
}
@Override
public GroovyExpression generateHasExpression(GraphPersistenceStrategies s, GroovyExpression parent,
String propertyName, String symbol, GroovyExpression requiredValue, FieldInfo fInfo) throws AtlasException {
AttributeInfo attrInfo = fInfo.attrInfo();
IDataType attrType = attrInfo.dataType();
GroovyExpression propertNameExpr = new LiteralExpression(propertyName);
if (s.isPropertyValueConversionNeeded(attrType)) {
// for some types, the logical value cannot be stored directly in
// the underlying graph,
// and conversion logic is needed to convert the persistent form of
// the value
// to the actual value. In cases like this, we generate a conversion
// expression to
// do this conversion and use the filter step to perform the
// comparsion in the gremlin query
GroovyExpression itExpr = getItVariable();
GroovyExpression vertexExpr = new CastExpression(new FunctionCallExpression(itExpr, GET_METHOD), VERTEX_CLASS);
GroovyExpression propertyValueExpr = new FunctionCallExpression(vertexExpr, VALUE_METHOD, propertNameExpr);
GroovyExpression conversionExpr = s.generatePersisentToLogicalConversionExpression(propertyValueExpr,
attrType);
GroovyExpression propertyIsPresentExpression = new FunctionCallExpression(
new FunctionCallExpression(vertexExpr, PROPERTY_METHOD, propertNameExpr), IS_PRESENT_METHOD);
GroovyExpression valueMatchesExpr = new ComparisonExpression(conversionExpr, getGroovyOperator(symbol),
requiredValue);
GroovyExpression filterCondition = new LogicalExpression(propertyIsPresentExpression, LogicalOperator.AND,
valueMatchesExpr);
GroovyExpression filterFunction = new ClosureExpression(filterCondition);
return new FunctionCallExpression(parent, FILTER_METHOD, filterFunction);
} else {
GroovyExpression valueMatches = new FunctionCallExpression(getComparisonFunction(symbol), requiredValue);
return new FunctionCallExpression(parent, HAS_METHOD, propertNameExpr, valueMatches);
}
}
@Override
protected GroovyExpression initialExpression(GraphPersistenceStrategies s, GroovyExpression varExpr) {
// this bit of groovy magic converts the set of vertices in varName into
// a String containing the ids of all the vertices. This becomes the
// argument
// to g.V(). This is needed because Gremlin 3 does not support
// _()
// s"g.V(${varName}.collect{it.id()} as String[])"
GroovyExpression gExpr = getGraph();
GroovyExpression varRefExpr = new TypeCoersionExpression(varExpr, OBJECT_ARRAY_CLASS);
FunctionCallExpression expr = new FunctionCallExpression(gExpr, V_METHOD, varRefExpr);
return s.addInitialQueryCondition(expr);
}
@Override
public GroovyExpression generateLimitExpression(GroovyExpression parent, int offset, int totalRows) {
return new FunctionCallExpression(parent, RANGE_METHOD, new LiteralExpression(offset), new LiteralExpression(totalRows));
}
@Override
public List<GroovyExpression> getOrderFieldParents() {
List<GroovyExpression> result = new ArrayList<>(1);
result.add(null);
return result;
}
@Override
public GroovyExpression generateOrderByExpression(GroovyExpression parent, List<GroovyExpression> translatedOrderBy,
boolean isAscending) {
GroovyExpression orderByClause = translatedOrderBy.get(0);
GroovyExpression aExpr = new IdentifierExpression("a");
GroovyExpression bExpr = new IdentifierExpression("b");
GroovyExpression aCompExpr = new FunctionCallExpression(new FunctionCallExpression(aExpr, TO_STRING_METHOD), TO_LOWER_CASE_METHOD);
GroovyExpression bCompExpr = new FunctionCallExpression(new FunctionCallExpression(bExpr, TO_STRING_METHOD), TO_LOWER_CASE_METHOD);
GroovyExpression comparisonExpr = null;
if (isAscending) {
comparisonExpr = new ComparisonOperatorExpression(aCompExpr, bCompExpr);
} else {
comparisonExpr = new ComparisonOperatorExpression(bCompExpr, aCompExpr);
}
ClosureExpression comparisonFunction = new ClosureExpression(comparisonExpr, "a", "b");
return new FunctionCallExpression(new FunctionCallExpression(parent, ORDER_METHOD), BY_METHOD, orderByClause, comparisonFunction);
}
@Override
public GroovyExpression getAnonymousTraversalExpression() {
return null;
}
@Override
public GroovyExpression getFieldInSelect() {
// this logic is needed to remove extra results from
// what is emitted by repeat loops. Technically
// for queries that don't have a loop in them we could just use "it"
// the reason for this is that in repeat loops with an alias,
// although the alias gets set to the right value, for some
// reason the select actually includes all vertices that were traversed
// through in the loop. In these cases, we only want the last vertex
// traversed in the loop to be selected. The logic here handles that
// case by converting the result to a list and just selecting the
// last item from it.
GroovyExpression itExpr = getItVariable();
GroovyExpression expr1 = new TypeCoersionExpression(itExpr, VERTEX_ARRAY_CLASS);
GroovyExpression expr2 = new TypeCoersionExpression(expr1, VERTEX_LIST_CLASS);
return new FunctionCallExpression(expr2, LAST_METHOD);
}
}
/**
* 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_METHOD 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.atlas.gremlin;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.atlas.AtlasException;
import org.apache.atlas.groovy.ArithmeticExpression;
import org.apache.atlas.groovy.ClosureExpression;
import org.apache.atlas.groovy.FieldExpression;
import org.apache.atlas.groovy.FunctionCallExpression;
import org.apache.atlas.groovy.GroovyExpression;
import org.apache.atlas.groovy.IdentifierExpression;
import org.apache.atlas.groovy.ListExpression;
import org.apache.atlas.groovy.LiteralExpression;
import org.apache.atlas.groovy.TypeCoersionExpression;
import org.apache.atlas.groovy.VariableAssignmentExpression;
import org.apache.atlas.groovy.ArithmeticExpression.ArithmeticOperator;
import org.apache.atlas.query.GraphPersistenceStrategies;
import org.apache.atlas.query.IntSequence;
import org.apache.atlas.query.TypeUtils.FieldInfo;
import org.apache.atlas.repository.graph.AtlasGraphProvider;
import org.apache.atlas.repository.graphdb.AtlasEdgeDirection;
import org.apache.atlas.repository.graphdb.GremlinVersion;
import org.apache.atlas.typesystem.types.IDataType;
/**
* Factory to generate Groovy expressions representing Gremlin syntax that that
* are independent of the specific version of Gremlin that is being used.
*
*/
public abstract class GremlinExpressionFactory {
private static final String G_VARIABLE = "g";
private static final String IT_VARIABLE = "it";
private static final String SET_CLASS = "Set";
private static final String OBJECT_FIELD = "object";
protected static final String V_METHOD = "V";
protected static final String FILTER_METHOD = "filter";
private static final String PATH_METHOD = "path";
private static final String AS_METHOD = "as";
private static final String FILL_METHOD = "fill";
protected static final String HAS_METHOD = "has";
protected static final String TO_LOWER_CASE_METHOD = "toLowerCase";
protected static final String SELECT_METHOD = "select";
protected static final String ORDER_METHOD = "order";
public static final GremlinExpressionFactory INSTANCE = AtlasGraphProvider.getGraphInstance()
.getSupportedGremlinVersion() == GremlinVersion.THREE ? new Gremlin3ExpressionFactory()
: new Gremlin2ExpressionFactory();
/**
* Gets the expression to use as the parent when translating the loop
* expression in a loop
*
* @param inputQry
* the
* @return
*/
public abstract GroovyExpression getLoopExpressionParent(GroovyExpression inputQry);
/**
* Generates a loop expression.
*
* @param parent
* the parent of the loop expression
* @param emitExpr
* Expression with the value that should be emitted by the loop
* expression.
* @param loopExpr
* the query expression that is being executed repeatedly
* executed in a loop
* @param alias
* The alias of the expression being looped over
* @param times
* the number of times to repeat, or null if a times condition
* should not be used.
* @return
*/
public abstract GroovyExpression generateLoopExpression(GroovyExpression parent, GraphPersistenceStrategies s, IDataType dataType,
GroovyExpression loopExpr, String alias, Integer times);
/**
* Generates a logical (and/or) expression with the given operands.
* @param parent
* @param operator
* @param operands
* @return
*/
public abstract GroovyExpression generateLogicalExpression(GroovyExpression parent, String operator,
List<GroovyExpression> operands);
/**
* Generates a back reference expression that refers to the given alias.
*
* @param parent
* @param inSelect
* @param alias
* @return
*/
public abstract GroovyExpression generateBackReferenceExpression(GroovyExpression parent, boolean inSelect,
String alias);
/**
* Generates a select expression
*
* @param parent
* @param sourceNames
* the names of the select fields
* @param srcExprs
* the corresponding values to return
* @return
*/
public abstract GroovyExpression generateSelectExpression(GroovyExpression parent,
List<LiteralExpression> sourceNames, List<GroovyExpression> srcExprs);
/**
* Generates a an expression that gets the value of the given property from the
* vertex presented by the parent.
*
* @param parent
* @param fInfo
* @param propertyName
* @param inSelect
* @return
*/
public abstract GroovyExpression generateFieldExpression(GroovyExpression parent, FieldInfo fInfo,
String propertyName, boolean inSelect);
/**
* Generates a has expression that checks whether the vertices match a specific condition
*
* @param s
* @param parent the object that we should call apply the "has" condition to.
* @param propertyName the name of the property whose value we are comparing
* @param symbol comparsion operator symbol ('=','<', etc.)
* @param requiredValue the value to compare against
* @param fInfo info about the field whose value we are checking
* @return
* @throws AtlasException
*/
public abstract GroovyExpression generateHasExpression(GraphPersistenceStrategies s, GroovyExpression parent,
String propertyName, String symbol, GroovyExpression requiredValue, FieldInfo fInfo) throws AtlasException;
/**
* Generates a limit expression
*
* @param parent
* @param offset
* @param totalRows
* @return
*/
public abstract GroovyExpression generateLimitExpression(GroovyExpression parent, int offset, int totalRows);
/**
* Generates an order by expression
*
* @param parent
* @param translatedOrderBy
* @param isAscending
* @return
*/
public abstract GroovyExpression generateOrderByExpression(GroovyExpression parent,
List<GroovyExpression> translatedOrderBy, boolean isAscending);
/**
* Returns the Groovy expressions that should be used as the parents when
* translating an order by expression. This is needed because Gremlin 2 and
* 3 handle order by expressions very differently.
*
*/
public abstract List<GroovyExpression> getOrderFieldParents();
/**
* Returns the expression that represents an anonymous graph traversal.
*
* @return
*/
public abstract GroovyExpression getAnonymousTraversalExpression();
/**
* Returns an expression representing
*
* @return
*/
public abstract GroovyExpression getFieldInSelect();
/**
* Generates the expression the serves as the root of the Gremlin query.
* @param s
* @param varExpr variable containing the vertices to traverse
* @return
*/
protected abstract GroovyExpression initialExpression(GraphPersistenceStrategies s, GroovyExpression varExpr);
/**
* Generates an expression that tests whether the vertex represented by the 'toTest'
* expression represents an instance of the specified type, checking both the type
* and super type names.
*
* @param s
* @param typeName
* @param itRef
* @return
*/
protected abstract GroovyExpression typeTestExpression(GraphPersistenceStrategies s, String typeName,
GroovyExpression vertexExpr);
/**
* Generates a sequence of groovy expressions that filter the vertices to only
* those that match the specified type. If GraphPersistenceStrategies.collectTypeInstancesIntoVar()
* is set, the vertices are put into a variable whose name is geneated from the specified IntSequence.
* The last item in the result will be a graph traversal restricted to only the matching vertices.
*/
public List<GroovyExpression> generateTypeTestExpression(GraphPersistenceStrategies s, GroovyExpression parent,
String typeName, IntSequence intSeq) {
if (s.collectTypeInstancesIntoVar()) {
return typeTestExpressionMultiStep(s, typeName, intSeq);
} else {
return typeTestExpressionUsingFilter(s, parent, typeName);
}
}
private List<GroovyExpression> typeTestExpressionMultiStep(GraphPersistenceStrategies s, String typeName,
IntSequence intSeq) {
String varName = "_var_" + intSeq.next();
GroovyExpression varExpr = new IdentifierExpression(varName);
List<GroovyExpression> result = new ArrayList<>();
result.add(newSetVar(varName));
result.add(fillVarWithTypeInstances(s, typeName, varName));
result.add(fillVarWithSubTypeInstances(s, typeName, varName));
result.add(initialExpression(s, varExpr));
return result;
}
private GroovyExpression newSetVar(String varName) {
GroovyExpression castExpr = new TypeCoersionExpression(new ListExpression(), SET_CLASS);
return new VariableAssignmentExpression(varName, castExpr);
}
private GroovyExpression fillVarWithTypeInstances(GraphPersistenceStrategies s, String typeName, String fillVar) {
GroovyExpression graphExpr = getAllVerticesExpr();
GroovyExpression typeAttributeNameExpr = new LiteralExpression(s.typeAttributeName());
GroovyExpression typeNameExpr = new LiteralExpression(typeName);
GroovyExpression hasExpr = new FunctionCallExpression(graphExpr, HAS_METHOD, typeAttributeNameExpr, typeNameExpr);
GroovyExpression fillExpr = new FunctionCallExpression(hasExpr, FILL_METHOD, new IdentifierExpression(fillVar));
return fillExpr;
}
private GroovyExpression fillVarWithSubTypeInstances(GraphPersistenceStrategies s, String typeName,
String fillVar) {
GroovyExpression graphExpr = getAllVerticesExpr();
GroovyExpression superTypeAttributeNameExpr = new LiteralExpression(s.superTypeAttributeName());
GroovyExpression typeNameExpr = new LiteralExpression(typeName);
GroovyExpression hasExpr = new FunctionCallExpression(graphExpr, HAS_METHOD, superTypeAttributeNameExpr,
typeNameExpr);
GroovyExpression fillExpr = new FunctionCallExpression(hasExpr, FILL_METHOD, new IdentifierExpression(fillVar));
return fillExpr;
}
private List<GroovyExpression> typeTestExpressionUsingFilter(GraphPersistenceStrategies s, GroovyExpression parent,
String typeName) {
GroovyExpression itExpr = getItVariable();
GroovyExpression typeTestExpr = typeTestExpression(s, typeName, itExpr);
GroovyExpression closureExpr = new ClosureExpression(typeTestExpr);
GroovyExpression filterExpr = new FunctionCallExpression(parent, FILTER_METHOD, closureExpr);
return Collections.singletonList(filterExpr);
}
/**
* Generates an expression which checks whether the vertices in the query have
* a field with the given name.
*
* @param parent
* @param fieldName
* @return
*/
public GroovyExpression generateUnaryHasExpression(GroovyExpression parent, String fieldName) {
return new FunctionCallExpression(parent, HAS_METHOD, new LiteralExpression(fieldName));
}
/**
* Generates a path expression
*
* @param parent
* @return
*/
public GroovyExpression generatePathExpression(GroovyExpression parent) {
return new FunctionCallExpression(parent, PATH_METHOD);
}
/**
* Generates the emit expression used in loop expressions.
* @param s
* @param dataType
* @return
*/
protected GroovyExpression generateLoopEmitExpression(GraphPersistenceStrategies s, IDataType dataType) {
return typeTestExpression(s, dataType.getName(), getCurrentObjectExpression());
}
/**
* Generates an alias expression
*
* @param parent
* @param alias
* @return
*/
public GroovyExpression generateAliasExpression(GroovyExpression parent, String alias) {
return new FunctionCallExpression(parent, AS_METHOD, new LiteralExpression(alias));
}
/**
* Generates an expression that gets the vertices adjacent to the vertex in 'parent'
* in the specified direction.
*
* @param parent
* @param dir
* @return
*/
public GroovyExpression generateAdjacentVerticesExpression(GroovyExpression parent, AtlasEdgeDirection dir) {
return new FunctionCallExpression(parent, getGremlinFunctionName(dir));
}
private String getGremlinFunctionName(AtlasEdgeDirection dir) {
switch(dir) {
case IN:
return "in";
case OUT:
return "out";
case BOTH:
return "both";
default:
throw new RuntimeException("Unknown Atlas Edge Direction: " + dir);
}
}
/**
* Generates an expression that gets the vertices adjacent to the vertex in 'parent'
* in the specified direction, following only edges with the given label.
*
* @param parent
* @param dir
* @return
*/
public GroovyExpression generateAdjacentVerticesExpression(GroovyExpression parent, AtlasEdgeDirection dir,
String label) {
return new FunctionCallExpression(parent, getGremlinFunctionName(dir), new LiteralExpression(label));
}
/**
* Generates an arithmetic expression, e.g. a + b
*
*/
public GroovyExpression generateArithmeticExpression(GroovyExpression left, String operator,
GroovyExpression right) throws AtlasException {
ArithmeticOperator op = ArithmeticOperator.lookup(operator);
return new ArithmeticExpression(left, op, right);
}
protected GroovyExpression getItVariable() {
return new IdentifierExpression(IT_VARIABLE);
}
protected GroovyExpression getAllVerticesExpr() {
GroovyExpression gExpr = getGraph();
return new FunctionCallExpression(gExpr, V_METHOD);
}
protected IdentifierExpression getGraph() {
return new IdentifierExpression(G_VARIABLE);
}
protected GroovyExpression getCurrentObjectExpression() {
return new FieldExpression(getItVariable(), OBJECT_FIELD);
}
}
\ No newline at end of file
......@@ -21,23 +21,20 @@ package org.apache.atlas.query
import java.util
import java.util.Date
import scala.collection.JavaConversions._
import scala.collection.JavaConversions.seqAsJavaList
import scala.language.existentials
import org.apache.atlas.query.Expressions.{ComparisonExpression, ExpressionException}
import org.apache.atlas.groovy.GroovyExpression
import org.apache.atlas.query.TypeUtils.FieldInfo
import org.apache.atlas.repository.graph.{GraphHelper, GraphBackedMetadataRepository}
import org.apache.atlas.repository.RepositoryException
import org.apache.atlas.repository.graph.GraphHelper
import org.apache.atlas.repository.graphdb._
import org.apache.atlas.typesystem.persistence.Id
import org.apache.atlas.typesystem.types.DataTypes._
import org.apache.atlas.typesystem.ITypedInstance
import org.apache.atlas.typesystem.ITypedReferenceableInstance
import org.apache.atlas.typesystem.persistence.Id
import org.apache.atlas.typesystem.types._
import org.apache.atlas.typesystem.{ITypedInstance, ITypedReferenceableInstance}
import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import org.apache.atlas.typesystem.types.DataTypes._
/**
* Represents the Bridge between the QueryProcessor and the Graph Persistence scheme used.
......@@ -47,19 +44,19 @@ import scala.collection.mutable.ArrayBuffer
* - how are attribute names mapped to Property Keys in Vertices.
*
* This is a work in progress.
*
*
*/
trait GraphPersistenceStrategies {
@throws(classOf[RepositoryException])
def getGraph() : AtlasGraph[_,_]
def getSupportedGremlinVersion() : GremlinVersion = getGraph().getSupportedGremlinVersion;
def generatePersisentToLogicalConversionExpression(expr: String, t: IDataType[_]) : String = getGraph().generatePersisentToLogicalConversionExpression(expr, t);
def generatePersisentToLogicalConversionExpression(expr: GroovyExpression, t: IDataType[_]) : GroovyExpression = getGraph().generatePersisentToLogicalConversionExpression(expr, t);
def isPropertyValueConversionNeeded(attrType: IDataType[_]) : Boolean = getGraph().isPropertyValueConversionNeeded(attrType);
def initialQueryCondition = if (getGraph().requiresInitialIndexedPredicate()) { s""".${getGraph().getInitialIndexedPredicate}""" } else {""};
def addInitialQueryCondition(parent: GroovyExpression) : GroovyExpression = if (getGraph().requiresInitialIndexedPredicate()) { getGraph().getInitialIndexedPredicate(parent) } else { parent };
/**
* Name of attribute used to store typeName in vertex
*/
......@@ -87,11 +84,12 @@ trait GraphPersistenceStrategies {
def traitLabel(cls: IDataType[_], traitName: String): String
def instanceToTraitEdgeDirection : String = "out"
def traitToInstanceEdgeDirection = instanceToTraitEdgeDirection match {
case "out" => "in"
case "in" => "out"
case x => x
def instanceToTraitEdgeDirection : AtlasEdgeDirection = AtlasEdgeDirection.OUT;
def traitToInstanceEdgeDirection : AtlasEdgeDirection = instanceToTraitEdgeDirection match {
case AtlasEdgeDirection.OUT => AtlasEdgeDirection.IN;
case AtlasEdgeDirection.IN => AtlasEdgeDirection.OUT;
case x => AtlasEdgeDirection.IN;
}
/**
......@@ -115,27 +113,6 @@ trait GraphPersistenceStrategies {
case FieldInfo(dataType, null, null, traitName) => traitLabel(dataType, traitName)
}
def fieldPrefixInSelect(): String = {
if(getSupportedGremlinVersion() == GremlinVersion.THREE) {
//this logic is needed to remove extra results from
//what is emitted by repeat loops. Technically
//for queries that don't have a loop in them we could just use "it"
//the reason for this is that in repeat loops with an alias,
//although the alias gets set to the right value, for some
//reason the select actually includes all vertices that were traversed
//through in the loop. In these cases, we only want the last vertex
//traversed in the loop to be selected. The logic here handles that
//case by converting the result to a list and just selecting the
//last item from it.
"((it as Vertex[]) as List<Vertex>).last()"
}
else {
"it"
}
}
/**
* extract the Id from a Vertex.
* @param dataTypeNm the dataType of the instance that the given vertex represents
......@@ -146,50 +123,7 @@ trait GraphPersistenceStrategies {
def constructInstance[U](dataType: IDataType[U], v: java.lang.Object): U
def gremlinCompOp(op: ComparisonExpression) = {
if( getSupportedGremlinVersion() == GremlinVersion.TWO) {
gremlin2CompOp(op);
}
else {
gremlin3CompOp(op);
}
}
def gremlinPrimitiveOp(op: ComparisonExpression) = op.symbol match {
case "=" => "=="
case "!=" => "!="
case ">" => ">"
case ">=" => ">="
case "<" => "<"
case "<=" => "<="
case _ => throw new ExpressionException(op, "Comparison operator not supported in Gremlin")
}
private def gremlin2CompOp(op: ComparisonExpression) = op.symbol match {
case "=" => "T.eq"
case "!=" => "T.neq"
case ">" => "T.gt"
case ">=" => "T.gte"
case "<" => "T.lt"
case "<=" => "T.lte"
case _ => throw new ExpressionException(op, "Comparison operator not supported in Gremlin")
}
private def gremlin3CompOp(op: ComparisonExpression) = op.symbol match {
case "=" => "eq"
case "!=" => "neq"
case ">" => "gt"
case ">=" => "gte"
case "<" => "lt"
case "<=" => "lte"
case _ => throw new ExpressionException(op, "Comparison operator not supported in Gremlin")
}
def loopObjectExpression(dataType: IDataType[_]) = {
_typeTestExpression(dataType.getName, "it.object")
}
def addGraphVertexPrefix(preStatements : Traversable[String]) = !collectTypeInstancesIntoVar
def addGraphVertexPrefix(preStatements : Traversable[GroovyExpression]) = !collectTypeInstancesIntoVar
/**
* Controls behavior of how instances of a Type are discovered.
......@@ -213,76 +147,14 @@ trait GraphPersistenceStrategies {
*/
def collectTypeInstancesIntoVar = true
def typeTestExpression(typeName : String, intSeq : IntSequence) : Seq[String] = {
if (collectTypeInstancesIntoVar)
typeTestExpressionMultiStep(typeName, intSeq)
else
typeTestExpressionUsingFilter(typeName)
}
private def typeTestExpressionUsingFilter(typeName : String) : Seq[String] = {
Seq(s"""filter${_typeTestExpression(typeName, "it")}""")
}
/**
* type test expression that ends up in the emit clause in
* loop/repeat steps and a few other places
*/
private def _typeTestExpression(typeName: String, itRef: String): String = {
if( getSupportedGremlinVersion() == GremlinVersion.TWO) {
s"""{(${itRef}.'${typeAttributeName}' == '${typeName}') |
| (${itRef}.'${superTypeAttributeName}' ?
| ${itRef}.'${superTypeAttributeName}'.contains('${typeName}') : false)}""".
stripMargin.replace(System.getProperty("line.separator"), "")
}
else {
//gremlin 3
s"""has('${typeAttributeName}',eq('${typeName}')).or().has('${superTypeAttributeName}',eq('${typeName}'))"""
}
}
private def propertyValueSet(vertexRef : String, attrName: String) : String = {
s"""org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils.set(${vertexRef}.values('${attrName})"""
}
private def typeTestExpressionMultiStep(typeName : String, intSeq : IntSequence) : Seq[String] = {
val varName = s"_var_${intSeq.next}"
Seq(
newSetVar(varName),
fillVarWithTypeInstances(typeName, varName),
fillVarWithSubTypeInstances(typeName, varName),
if(getSupportedGremlinVersion() == GremlinVersion.TWO) {
s"$varName._()"
}
else {
//this bit of groovy magic converts the set of vertices in varName into
//a String containing the ids of all the vertices. This becomes the argument
//to g.V(). This is needed because Gremlin 3 does not support
// _()
//s"g.V(${varName}.collect{it.id()} as String[])"
s"g.V(${varName} as Object[])${initialQueryCondition}"
}
)
}
private def newSetVar(varName : String) = s"def $varName = [] as Set"
private def fillVarWithTypeInstances(typeName : String, fillVar : String) = {
s"""g.V().has("${typeAttributeName}", "${typeName}").fill($fillVar)"""
}
private def fillVarWithSubTypeInstances(typeName : String, fillVar : String) = {
s"""g.V().has("${superTypeAttributeName}", "${typeName}").fill($fillVar)"""
}
}
import scala.language.existentials;
import org.apache.atlas.repository.RepositoryException
import org.apache.atlas.repository.RepositoryException
import org.apache.atlas.repository.RepositoryException
import org.apache.atlas.repository.RepositoryException
case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenceStrategies {
......@@ -293,8 +165,8 @@ case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenc
override def getGraph() : AtlasGraph[_,_] = {
return g;
}
}
def edgeLabel(dataType: IDataType[_], aInfo: AttributeInfo) = s"__${dataType.getName}.${aInfo.name}"
def edgeLabel(propertyName: String) = s"__${propertyName}"
......@@ -458,8 +330,6 @@ case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenc
}
}
private def mapVertexToCollectionEntry(instanceVertex: AtlasVertex[_,_], attributeInfo: AttributeInfo, elementType: IDataType[_], i: ITypedInstance, value: Any): Any = {
elementType.getTypeCategory match {
case DataTypes.TypeCategory.PRIMITIVE => value
......@@ -474,5 +344,6 @@ case class GraphPersistenceStrategy1(g: AtlasGraph[_,_]) extends GraphPersistenc
throw new UnsupportedOperationException(s"load for ${attributeInfo.dataType()} not supported")
}
}
}
......@@ -18,30 +18,61 @@
package org.apache.atlas.query
import java.lang.Boolean
import java.lang.Byte
import java.lang.Double
import java.lang.Float
import java.lang.Integer
import java.lang.Long
import java.lang.Short
import java.util.ArrayList
import scala.collection.JavaConversions.asScalaBuffer
import scala.collection.JavaConversions.bufferAsJavaList
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import org.apache.atlas.query.TypeUtils.FieldInfo;
import org.apache.atlas.query.Expressions._
import org.apache.atlas.repository.graphdb.GremlinVersion
import org.apache.atlas.gremlin.GremlinExpressionFactory
import org.apache.atlas.groovy.CastExpression
import org.apache.atlas.groovy.CodeBlockExpression
import org.apache.atlas.groovy.FunctionCallExpression
import org.apache.atlas.groovy.GroovyExpression
import org.apache.atlas.groovy.GroovyGenerationContext
import org.apache.atlas.groovy.IdentifierExpression
import org.apache.atlas.groovy.ListExpression
import org.apache.atlas.groovy.LiteralExpression
import org.apache.atlas.query.Expressions.AliasExpression
import org.apache.atlas.query.Expressions.ArithmeticExpression
import org.apache.atlas.query.Expressions.BackReference
import org.apache.atlas.query.Expressions.ClassExpression
import org.apache.atlas.query.Expressions.ComparisonExpression
import org.apache.atlas.query.Expressions.Expression
import org.apache.atlas.query.Expressions.ExpressionException
import org.apache.atlas.query.Expressions.FieldExpression
import org.apache.atlas.query.Expressions.FilterExpression
import org.apache.atlas.query.Expressions.InstanceExpression
import org.apache.atlas.query.Expressions.LimitExpression
import org.apache.atlas.query.Expressions.ListLiteral
import org.apache.atlas.query.Expressions.Literal
import org.apache.atlas.query.Expressions.LogicalExpression
import org.apache.atlas.query.Expressions.LoopExpression
import org.apache.atlas.query.Expressions.OrderExpression
import org.apache.atlas.query.Expressions.PathExpression
import org.apache.atlas.query.Expressions.SelectExpression
import org.apache.atlas.query.Expressions.TraitExpression
import org.apache.atlas.query.Expressions.TraitInstanceExpression
import org.apache.atlas.query.Expressions.hasFieldLeafExpression
import org.apache.atlas.query.Expressions.hasFieldUnaryExpression
import org.apache.atlas.query.Expressions.id
import org.apache.atlas.query.Expressions.isTraitLeafExpression
import org.apache.atlas.query.Expressions.isTraitUnaryExpression
import org.apache.atlas.repository.RepositoryException
import org.apache.atlas.repository.graphdb.AtlasEdgeDirection
import org.apache.atlas.typesystem.types.DataTypes
import org.apache.atlas.typesystem.types.DataTypes.TypeCategory
import org.apache.atlas.typesystem.types.IDataType
import org.apache.commons.lang.StringEscapeUtils
import org.apache.atlas.typesystem.types.TypeSystem
import org.apache.atlas.typesystem.types.AttributeInfo
import org.joda.time.format.ISODateTimeFormat
import org.apache.atlas.typesystem.types.DataTypes.BigDecimalType
import org.apache.atlas.typesystem.types.DataTypes.ByteType
import org.apache.atlas.typesystem.types.DataTypes.BooleanType
import org.apache.atlas.typesystem.types.DataTypes.DateType
import org.apache.atlas.typesystem.types.DataTypes.BigIntegerType
import org.apache.atlas.typesystem.types.DataTypes.IntType
import org.apache.atlas.typesystem.types.DataTypes.StringType
import org.apache.atlas.typesystem.types.DataTypes.LongType
import org.apache.atlas.typesystem.types.DataTypes.DoubleType
import org.apache.atlas.typesystem.types.DataTypes.FloatType
import org.apache.atlas.typesystem.types.DataTypes.ShortType
trait IntSequence {
def next: Int
......@@ -127,7 +158,7 @@ trait SelectExpressionHandling {
/**
* For each Output Column in the SelectExpression compute the ArrayList(Src) this maps to and the position within
* this list.
*
*
* @param sel
* @return
*/
......@@ -151,8 +182,8 @@ class GremlinTranslator(expr: Expression,
gPersistenceBehavior: GraphPersistenceStrategies)
extends SelectExpressionHandling {
val preStatements = ArrayBuffer[String]()
val postStatements = ArrayBuffer[String]()
val preStatements = ArrayBuffer[GroovyExpression]()
val postStatements = ArrayBuffer[GroovyExpression]()
val wrapAndRule: PartialFunction[Expression, Expression] = {
case f: FilterExpression if !f.condExpr.isInstanceOf[LogicalExpression] =>
......@@ -216,351 +247,287 @@ class GremlinTranslator(expr: Expression,
}
}
def typeTestExpression(typeName : String) : String = {
val stats = gPersistenceBehavior.typeTestExpression(escape(typeName), counter)
def typeTestExpression(parent: GroovyExpression, typeName : String) : GroovyExpression = {
val stats = GremlinExpressionFactory.INSTANCE.generateTypeTestExpression(gPersistenceBehavior, parent, typeName, counter)
preStatements ++= stats.init
stats.last
}
def escape(str: String): String = {
if (str != null) {
return str.replace("\"", "\\\"").replace("$", "\\$");
}
str
val QUOTE = "\"";
private def cleanStringLiteral(l : Literal[_]) : String = {
return l.toString.stripPrefix(QUOTE).stripSuffix(QUOTE);
}
private def genQuery(expr: Expression, inSelect: Boolean): String = expr match {
case ClassExpression(clsName) =>
typeTestExpression(clsName)
case TraitExpression(clsName) =>
typeTestExpression(clsName)
private def genQuery(parent: GroovyExpression, expr: Expression, inSelect: Boolean): GroovyExpression = expr match {
case ClassExpression(clsName) => typeTestExpression(parent, clsName)
case TraitExpression(clsName) => typeTestExpression(parent, clsName)
case fe@FieldExpression(fieldName, fInfo, child)
if fe.dataType.getTypeCategory == TypeCategory.PRIMITIVE || fe.dataType.getTypeCategory == TypeCategory.ARRAY => {
val fN = "\"" + gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo) + "\""
genPropertyAccessExpr(child, fInfo, fN, inSelect)
}
val fN = gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo)
val childExpr = translateOptChild(parent, child, inSelect);
return GremlinExpressionFactory.INSTANCE.generateFieldExpression(childExpr, fInfo, fN, inSelect);
}
case fe@FieldExpression(fieldName, fInfo, child)
if fe.dataType.getTypeCategory == TypeCategory.CLASS || fe.dataType.getTypeCategory == TypeCategory.STRUCT => {
val direction = if (fInfo.isReverse) "in" else "out"
val childExpr = translateOptChild(parent, child, inSelect);
val direction = if (fInfo.isReverse) AtlasEdgeDirection.IN else AtlasEdgeDirection.OUT
val edgeLbl = gPersistenceBehavior.edgeLabel(fInfo)
val step = s"""$direction("$edgeLbl")"""
generateAndPrependExpr(child, inSelect, s"""$step""")
}
return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(childExpr, direction, edgeLbl)
}
case fe@FieldExpression(fieldName, fInfo, child) if fInfo.traitName != null => {
val childExpr = translateOptChild(parent, child, inSelect);
val direction = gPersistenceBehavior.instanceToTraitEdgeDirection
val edgeLbl = gPersistenceBehavior.edgeLabel(fInfo)
val step = s"""$direction("$edgeLbl")"""
generateAndPrependExpr(child, inSelect, s"""$step""")
return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(childExpr, direction, edgeLbl)
}
case c@ComparisonExpression(symb, f@FieldExpression(fieldName, fInfo, ch), l) => {
return genHasPredicate(ch, fInfo, fieldName, inSelect, c, l)
val qualifiedPropertyName = s"${gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo)}"
val childExpr = translateOptChild(parent, ch, inSelect)
val persistentExprValue : GroovyExpression = if(l.isInstanceOf[Literal[_]]) {
translateLiteralValue(fInfo.attrInfo.dataType, l.asInstanceOf[Literal[_]]);
}
else {
genQuery(null, l, inSelect);
}
return GremlinExpressionFactory.INSTANCE.generateHasExpression(gPersistenceBehavior, childExpr, qualifiedPropertyName, c.symbol, persistentExprValue, fInfo);
}
case fil@FilterExpression(child, condExpr) => {
s"${genQuery(child, inSelect)}.${genQuery(condExpr, inSelect)}"
val newParent = genQuery(parent, child, inSelect);
return genQuery(newParent, condExpr, inSelect);
}
case l@LogicalExpression(symb, children) => {
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.THREE) {
if(children.length == 1) {
//gremlin 3 treats one element expressions as 'false'. Avoid
//creating a boolean expression in this case. Inline the expression
//note: we can't simply omit it, since it will cause us to traverse the edge!
//use 'where' instead
var child : Expression = children.head;
//if child is a back expression, that expression becomes an argument to where
return s"""where(${genQuery(child, inSelect)})""";
}
else {
// Gremlin 3 does not support _() syntax
//
return s"""$symb${children.map( genQuery(_, inSelect)).mkString("(", ",", ")")}"""
}
}
else {
s"""$symb${children.map("_()." + genQuery(_, inSelect)).mkString("(", ",", ")")}"""
}
val translatedChildren : java.util.List[GroovyExpression] = translateList(children, true, inSelect);
return GremlinExpressionFactory.INSTANCE.generateLogicalExpression(parent, symb, translatedChildren);
}
case sel@SelectExpression(child, selList) => {
val m = groupSelectExpressionsBySrc(sel)
var srcNamesList: List[String] = List()
var srcExprsList: List[List[String]] = List()
var srcNamesList: java.util.List[LiteralExpression] = new ArrayList()
var srcExprsList: List[java.util.List[GroovyExpression]] = List()
val it = m.iterator
while (it.hasNext) {
val (src, selExprs) = it.next
srcNamesList = srcNamesList :+ s""""$src""""
srcExprsList = srcExprsList :+ selExprs.map { selExpr =>
genQuery(selExpr, true)
}
srcNamesList.add(new LiteralExpression(src));
val translatedSelExprs : java.util.List[GroovyExpression] = translateList(selExprs, false, true);
srcExprsList = srcExprsList :+ translatedSelExprs
}
val srcExprsStringList = srcExprsList.map {
_.mkString("[", ",", "]")
val srcExprsStringList : java.util.List[GroovyExpression] = new ArrayList();
srcExprsList.foreach { it =>
srcExprsStringList.add(new ListExpression(it));
}
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
val srcNamesString = srcNamesList.mkString("[", ",", "]")
val srcExprsString = srcExprsStringList.foldLeft("")(_ + "{" + _ + "}")
s"${genQuery(child, inSelect)}.select($srcNamesString)$srcExprsString"
}
else {
//gremlin 3
val srcNamesString = srcNamesList.mkString("", ",", "")
val srcExprsString = srcExprsStringList.foldLeft("")(_ + ".by({" + _ + "} as Function)")
s"${genQuery(child, inSelect)}.select($srcNamesString)$srcExprsString"
}
val childExpr = genQuery(parent, child, inSelect)
return GremlinExpressionFactory.INSTANCE.generateSelectExpression(childExpr, srcNamesList, srcExprsStringList);
}
case loop@LoopExpression(input, loopExpr, t) => {
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
val inputQry = genQuery(input, inSelect)
val loopingPathGExpr = genQuery(loopExpr, inSelect)
val loopGExpr = s"""loop("${input.asInstanceOf[AliasExpression].alias}")"""
val untilCriteria = if (t.isDefined) s"{it.loops < ${t.get.value}}" else "{it.path.contains(it.object)?false:true}"
val loopObjectGExpr = gPersistenceBehavior.loopObjectExpression(input.dataType)
val enablePathExpr = s".enablePath()"
s"""${inputQry}.${loopingPathGExpr}.${loopGExpr}${untilCriteria}${loopObjectGExpr}${enablePathExpr}"""
val times : Integer = if(t.isDefined) {
t.get.rawValue.asInstanceOf[Integer]
}
else {
//gremlin 3 - TODO - add support for circular lineage
val inputQry = genQuery(input, inSelect)
val repeatExpr = s"""repeat(__.${genQuery(loopExpr, inSelect)})"""
val optTimesExpr = if (t.isDefined) s".times(${t.get.value})" else ""
val emitExpr = s""".emit(${gPersistenceBehavior.loopObjectExpression(input.dataType)})"""
s"""${inputQry}.${repeatExpr}${optTimesExpr}${emitExpr}"""
null.asInstanceOf[Integer]
}
val alias = input.asInstanceOf[AliasExpression].alias;
val inputQry = genQuery(parent, input, inSelect)
val translatedLoopExpr = genQuery(GremlinExpressionFactory.INSTANCE.getLoopExpressionParent(inputQry), loopExpr, inSelect);
return GremlinExpressionFactory.INSTANCE.generateLoopExpression(inputQry, gPersistenceBehavior, input.dataType, translatedLoopExpr, alias, times);
}
case BackReference(alias, _, _) => {
if (inSelect) {
gPersistenceBehavior.fieldPrefixInSelect()
}
else {
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
s"""back("$alias")"""
}
else {
s"""select("$alias")"""
}
}
return GremlinExpressionFactory.INSTANCE.generateBackReferenceExpression(parent, inSelect, alias);
}
case AliasExpression(child, alias) => {
var childExpr = genQuery(parent, child, inSelect);
return GremlinExpressionFactory.INSTANCE.generateAliasExpression(childExpr, alias);
}
case isTraitLeafExpression(traitName, Some(clsExp)) => {
val label = gPersistenceBehavior.traitLabel(clsExp.dataType, traitName);
return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(parent, AtlasEdgeDirection.OUT, label);
}
case isTraitUnaryExpression(traitName, child) => {
val label = gPersistenceBehavior.traitLabel(child.dataType, traitName);
return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(parent, AtlasEdgeDirection.OUT, label);
}
case AliasExpression(child, alias) => s"""${genQuery(child, inSelect)}.as("$alias")"""
case isTraitLeafExpression(traitName, Some(clsExp)) =>
s"""out("${gPersistenceBehavior.traitLabel(clsExp.dataType, traitName)}")"""
case isTraitUnaryExpression(traitName, child) =>
s"""out("${gPersistenceBehavior.traitLabel(child.dataType, traitName)}")"""
case hasFieldLeafExpression(fieldName, clsExp) => clsExp match {
case None => s"""has("$fieldName")"""
case None => GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(parent, fieldName)
case Some(x) => {
val fi = TypeUtils.resolveReference(clsExp.get.dataType, fieldName);
if(! fi.isDefined) {
s"""has("$fieldName")"""
return GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(parent, fieldName);
}
else {
s"""has("${gPersistenceBehavior.fieldNameInVertex(fi.get.dataType, fi.get.attrInfo)}")"""
val fName = gPersistenceBehavior.fieldNameInVertex(fi.get.dataType, fi.get.attrInfo)
return GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(parent, fName);
}
}
}
case hasFieldUnaryExpression(fieldName, child) =>
s"""${genQuery(child, inSelect)}.has("$fieldName")"""
case ArithmeticExpression(symb, left, right) => s"${genQuery(left, inSelect)} $symb ${genQuery(right, inSelect)}"
case l: Literal[_] => l.toString
case list: ListLiteral[_] => list.toString
val childExpr = genQuery(parent, child, inSelect);
return GremlinExpressionFactory.INSTANCE.generateUnaryHasExpression(childExpr, fieldName);
case ArithmeticExpression(symb, left, right) => {
val leftExpr = genQuery(parent, left, inSelect);
val rightExpr = genQuery(parent, right, inSelect);
return GremlinExpressionFactory.INSTANCE.generateArithmeticExpression(leftExpr, symb, rightExpr);
}
case l: Literal[_] => {
if(parent != null) {
return new org.apache.atlas.groovy.FieldExpression(parent, cleanStringLiteral(l));
}
return translateLiteralValue(l.dataType, l);
}
case list: ListLiteral[_] => {
val values : java.util.List[GroovyExpression] = translateList(list.rawValue, false, inSelect);
return new ListExpression(values);
}
case in@TraitInstanceExpression(child) => {
val direction = gPersistenceBehavior.traitToInstanceEdgeDirection
s"${genQuery(child, inSelect)}.$direction()"
val childExpr = genQuery(parent, child, inSelect);
val direction = gPersistenceBehavior.traitToInstanceEdgeDirection;
return GremlinExpressionFactory.INSTANCE.generateAdjacentVerticesExpression(childExpr, direction);
}
case in@InstanceExpression(child) => {
s"${genQuery(child, inSelect)}"
return genQuery(parent, child, inSelect);
}
case pe@PathExpression(child) => {
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
s"${genQuery(child, inSelect)}.path"
}
else {
s"${genQuery(child, inSelect)}.path()"
}
val childExpr = genQuery(parent, child, inSelect)
return GremlinExpressionFactory.INSTANCE.generatePathExpression(childExpr);
}
case order@OrderExpression(child, odr, asc) => {
var orderby = ""
var orderExpression = odr
if(odr.isInstanceOf[BackReference]) {
orderExpression = odr.asInstanceOf[BackReference].reference
if(odr.isInstanceOf[BackReference]) {
orderExpression = odr.asInstanceOf[BackReference].reference
}
else if (odr.isInstanceOf[AliasExpression]) {
else if (odr.isInstanceOf[AliasExpression]) {
orderExpression = odr.asInstanceOf[AliasExpression].child
}
val orderbyProperty = genQuery(orderExpression, false)
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
val bProperty = s"it.b.$orderbyProperty"
val aProperty = s"it.a.$orderbyProperty"
val aCondition = s"($aProperty != null ? $aProperty.toLowerCase(): $aProperty)"
val bCondition = s"($bProperty != null ? $bProperty.toLowerCase(): $bProperty)"
orderby = asc match {
//builds a closure comparison function based on provided order by clause in DSL. This will be used to sort the results by gremlin order pipe.
//Ordering is case insensitive.
case false=> s"order{$bCondition <=> $aCondition}"//descending
case _ => s"order{$aCondition <=> $bCondition}"
}
}
else {
val orderbyProperty = genQuery(orderExpression, true);
val aPropertyExpr = gremlin3ToLowerCase("a");
val bPropertyExpr = gremlin3ToLowerCase("b");
orderby = asc match {
//builds a closure comparison function based on provided order by clause in DSL. This will be used to sort the results by gremlin order pipe.
//Ordering is case insensitive.
case false=> s"""order().by({$orderbyProperty'}, { a,b -> $bPropertyExpr <=> $aPropertyExpr })"""
case _ => s"""order().by({$orderbyProperty},{ a,b -> $aPropertyExpr <=> $bPropertyExpr })"""
}
val childExpr = genQuery(parent, child, inSelect);
var orderByParents : java.util.List[GroovyExpression] = GremlinExpressionFactory.INSTANCE.getOrderFieldParents();
val translatedParents : java.util.List[GroovyExpression] = new ArrayList[GroovyExpression]();
var translatedOrderParents = orderByParents.foreach { it =>
translatedParents.add(genQuery(it, orderExpression, false));
}
s"""${genQuery(child, inSelect)}.$orderby"""
return GremlinExpressionFactory.INSTANCE.generateOrderByExpression(childExpr, translatedParents,asc);
}
case limitOffset@LimitExpression(child, limit, offset) => {
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
val totalResultRows = limit.value + offset.value
s"""${genQuery(child, inSelect)} [$offset..<$totalResultRows]"""
}
else {
val totalResultRows = limit.value + offset.value
s"""${genQuery(child, inSelect)}.range($offset,$totalResultRows)"""
}
val childExpr = genQuery(parent, child, inSelect);
val totalResultRows = limit.value + offset.value;
return GremlinExpressionFactory.INSTANCE.generateLimitExpression(childExpr, offset.value, totalResultRows);
}
case x => throw new GremlinTranslationException(x, "expression not yet supported")
}
def gremlin3ToLowerCase(varName : String) : String = {
s"""($varName != null ? $varName.toString().toLowerCase() : null)"""
}
def genPropertyAccessExpr(e: Option[Expression], fInfo : FieldInfo, quotedPropertyName: String, inSelect: Boolean) : String = {
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
generateAndPrependExpr(e, inSelect, s"""$quotedPropertyName""")
}
else {
val attrInfo : AttributeInfo = fInfo.attrInfo;
val attrType : IDataType[_] = attrInfo.dataType;
if(inSelect) {
val expr = generateAndPrependExpr(e, inSelect, s"""property($quotedPropertyName).orElse(null)""");
return gPersistenceBehavior.generatePersisentToLogicalConversionExpression(expr, attrType);
}
else {
val unmapped = s"""values($quotedPropertyName)"""
val expr = if(gPersistenceBehavior.isPropertyValueConversionNeeded(attrType)) {
val conversionFunction = gPersistenceBehavior.generatePersisentToLogicalConversionExpression(s"""it.get()""", attrType);
s"""$unmapped.map{ $conversionFunction }"""
}
else {
unmapped
}
generateAndPrependExpr(e, inSelect, expr)
}
def translateList(exprs : List[Expressions.Expression], isAnonymousTraveral: Boolean, inSelect : Boolean) : java.util.List[GroovyExpression] = {
var parent = if(isAnonymousTraveral) {GremlinExpressionFactory.INSTANCE.getAnonymousTraversalExpression() } else { null }
var result : java.util.List[GroovyExpression] = new java.util.ArrayList(exprs.size);
exprs.foreach { it =>
result.add(genQuery(parent, it, inSelect));
}
return result;
}
def generateAndPrependExpr(e1: Option[Expression], inSelect: Boolean, e2: String) : String = e1 match {
def translateOptChild(parent : GroovyExpression, child : Option[Expressions.Expression] , inSelect: Boolean) : GroovyExpression = child match {
case Some(x) => s"""${genQuery(x, inSelect)}.$e2"""
case None => e2
case Some(x) => genQuery(parent, x, inSelect)
case None => parent
}
def genHasPredicate(e: Option[Expression], fInfo : FieldInfo, fieldName: String, inSelect: Boolean, c: ComparisonExpression, expr: Expression) : String = {
def translateLiteralValue(dataType: IDataType[_], l: Literal[_]): GroovyExpression = {
val qualifiedPropertyName = s"${gPersistenceBehavior.fieldNameInVertex(fInfo.dataType, fInfo.attrInfo)}"
val persistentExprValue = translateValueToPersistentForm(fInfo, expr);
if(gPersistenceBehavior.getSupportedGremlinVersion() == GremlinVersion.TWO) {
return generateAndPrependExpr(e, inSelect, s"""has("${qualifiedPropertyName}", ${gPersistenceBehavior.gremlinCompOp(c)}, $persistentExprValue)""");
if (dataType == DataTypes.DATE_TYPE) {
try {
//Accepts both date, datetime formats
val dateStr = cleanStringLiteral(l)
val dateVal = ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(dateStr).getMillis
return new LiteralExpression(dateVal)
} catch {
case pe: java.text.ParseException =>
throw new GremlinTranslationException(l,
"Date format " + l + " not supported. Should be of the format " +
TypeSystem.getInstance().getDateFormat.toPattern);
}
}
else if(dataType == DataTypes.BYTE_TYPE) {
//cast needed, otherwise get class cast exception when trying to compare, since the
//persist value is assumed to be an Integer
return new CastExpression(new LiteralExpression(Byte.valueOf(s"""${l}"""), true),"byte");
}
else if(dataType == DataTypes.INT_TYPE) {
return new LiteralExpression(Integer.valueOf(s"""${l}"""));
}
else if(dataType == DataTypes.BOOLEAN_TYPE) {
return new LiteralExpression(Boolean.valueOf(s"""${l}"""));
}
else if(dataType == DataTypes.SHORT_TYPE) {
return new CastExpression(new LiteralExpression(Short.valueOf(s"""${l}"""), true),"short");
}
else if(dataType == DataTypes.LONG_TYPE) {
return new LiteralExpression(Long.valueOf(s"""${l}"""), true);
}
else if(dataType == DataTypes.FLOAT_TYPE) {
return new LiteralExpression(Float.valueOf(s"""${l}"""), true);
}
else if(dataType == DataTypes.DOUBLE_TYPE) {
return new LiteralExpression(Double.valueOf(s"""${l}"""), true);
}
else if(dataType == DataTypes.STRING_TYPE) {
return new LiteralExpression(cleanStringLiteral(l));
}
else {
val attrInfo : AttributeInfo = fInfo.attrInfo;
val attrType : IDataType[_] = attrInfo.dataType;
if(gPersistenceBehavior.isPropertyValueConversionNeeded(attrType)) {
//for some types, the logical value cannot be stored directly in the underlying graph,
//and conversion logic is needed to convert the persistent form of the value
//to the actual value. In cases like this, we generate a conversion expression to
//do this conversion and use the filter step to perform the comparsion in the gremlin query
val vertexExpr = "((Vertex)it.get())";
val conversionExpr = gPersistenceBehavior.generatePersisentToLogicalConversionExpression(s"""$vertexExpr.value("$qualifiedPropertyName")""", attrType);
return generateAndPrependExpr(e, inSelect, s"""filter{$vertexExpr.property("$qualifiedPropertyName").isPresent() && $conversionExpr ${gPersistenceBehavior.gremlinPrimitiveOp(c)} $persistentExprValue}""");
}
else {
return generateAndPrependExpr(e, inSelect, s"""has("${qualifiedPropertyName}", ${gPersistenceBehavior.gremlinCompOp(c)}($persistentExprValue))""");
}
}
return new LiteralExpression(l.rawValue);
}
}
def translateValueToPersistentForm(fInfo: FieldInfo, l: Expression): Any = {
val dataType = fInfo.attrInfo.dataType;
val QUOTE = "\"";
if (dataType == DataTypes.DATE_TYPE) {
try {
//Accepts both date, datetime formats
val dateStr = l.toString.stripPrefix(QUOTE).stripSuffix(QUOTE)
val dateVal = ISODateTimeFormat.dateOptionalTimeParser().parseDateTime(dateStr).getMillis
return dateVal
} catch {
case pe: java.text.ParseException =>
throw new GremlinTranslationException(l,
"Date format " + l + " not supported. Should be of the format " +
TypeSystem.getInstance().getDateFormat.toPattern);
}
}
else if(dataType == DataTypes.BYTE_TYPE) {
//cast needed, otherwise get class cast exception when trying to compare, since the
//persist value is assumed to be an Integer
return s"""(byte)$l"""
}
else if(dataType == DataTypes.SHORT_TYPE) {
return s"""(short)$l"""
}
else if(dataType == DataTypes.LONG_TYPE) {
return s"""${l}L"""
}
else if(dataType == DataTypes.FLOAT_TYPE) {
return s"""${l}f"""
}
else if(dataType == DataTypes.DOUBLE_TYPE) {
return s"""${l}d"""
}
else if(dataType == DataTypes.STRING_TYPE) {
return string(escape(l.toString.stripPrefix(QUOTE).stripSuffix(QUOTE)));
}
else {
return l
}
}
def genFullQuery(expr: Expression, hasSelect: Boolean): String = {
var q = genQuery(expr, false)
var q : GroovyExpression = new FunctionCallExpression(new IdentifierExpression("g"),"V");
val debug:Boolean = false
if(gPersistenceBehavior.addGraphVertexPrefix(preStatements)) {
q = s"g.V()${gPersistenceBehavior.initialQueryCondition}.$q"
q = gPersistenceBehavior.addInitialQueryCondition(q);
}
q = s"$q.toList()${gPersistenceBehavior.getGraph().getOutputTransformationPredicate(hasSelect, expr.isInstanceOf[PathExpression])}"
q = genQuery(q, expr, false)
q = new FunctionCallExpression(q, "toList");
q = gPersistenceBehavior.getGraph().addOutputTransformationPredicate(q, hasSelect, expr.isInstanceOf[PathExpression]);
var overallExpression = new CodeBlockExpression();
overallExpression.addStatements(preStatements);
overallExpression.addStatement(q)
overallExpression.addStatements(postStatements);
var qryStr = generateGremlin(overallExpression);
if(debug) {
println(" query " + q)
println(" query " + qryStr)
}
q = (preStatements ++ Seq(q) ++ postStatements).mkString("", ";", "")
/*
* the L:{} represents a groovy code block; the label is needed
* to distinguish it from a groovy closure.
*/
s"L:{$q}"
qryStr;
}
def generateGremlin(expr: GroovyExpression) : String = {
val ctx : GroovyGenerationContext = new GroovyGenerationContext();
ctx.setParametersAllowed(false);
expr.generateGroovy(ctx);
return ctx.getQuery;
}
def translate(): GremlinQuery = {
var e1 = expr.transformUp(wrapAndRule)
......@@ -570,9 +537,9 @@ class GremlinTranslator(expr: Expression,
e1 = e1.transformUp(addAliasToLoopInput())
e1 = e1.transformUp(instanceClauseToTop(e1))
e1 = e1.transformUp(traitClauseWithInstanceForTop(e1))
//Following code extracts the select expressions from expression tree.
val se = SelectExpressionHelper.extractSelectExpression(e1)
if (se.isDefined) {
val rMap = buildResultMapping(se.get)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment