Commit 5bd5327c by Ashutosh Mestry Committed by Madhan Neethiraj

ATLAS-2229: DSL implementation using Antlr

parent 71a30876
http://www.antlr.org/license.html
[The BSD License]
Copyright (c) 2012 Terence Parr and Sam Harwell
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Developer's Certificate of Origin
All contributors to ANTLR v4 must formally agree to abide by the certificate of origin by signing on the bottom of that document. To contribute:
fork the ANTLR v4 github repository
make your changes
[first time contributors]: sign contributors.txt by adding your github userid, full name, email address (you can obscure your e-mail, but it must be computable by human), and date.
commit your changes
send a pull request
After you have signed once, you don't have to sign future pull requests. We can merge by simply checking to see your name is in the contributors file.
\ No newline at end of file
......@@ -552,6 +552,7 @@
<javax.servlet.version>3.1.0</javax.servlet.version>
<guava.version>19.0</guava.version>
<scala.version>2.11.8</scala.version>
<antlr4.version>4.7</antlr4.version>
<!-- Needed for hooks -->
<aopalliance.version>1.0</aopalliance.version>
......@@ -565,6 +566,7 @@
<paranamer.version>2.7</paranamer.version>
<zkclient.version>0.8</zkclient.version>
<enunciate-maven-plugin.version>2.10.1</enunciate-maven-plugin.version>
<antlr4.plugin.version>4.5</antlr4.plugin.version>
<PermGen>64m</PermGen>
<MaxPermGen>512m</MaxPermGen>
......@@ -791,6 +793,12 @@
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>${antlr4.version}</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>${guava.version}</version>
......@@ -1609,6 +1617,26 @@
<pluginManagement>
<plugins>
<plugin>
<groupId>org.antlr</groupId>
<artifactId>antlr4-maven-plugin</artifactId>
<version>${antlr4.plugin.version}</version>
<configuration>
<listener>false</listener>
<visitor>true</visitor>
</configuration>
<executions>
<execution>
<goals>
<goal>antlr4</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<outputDirectory>src/main/java</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.4</version>
......@@ -1871,6 +1899,7 @@
<useEclipseDefaultExcludes>true</useEclipseDefaultExcludes>
<excludeSubProjects>true</excludeSubProjects>
<excludes>
<exclude>**/antlr4/**</exclude>
<exclude>**/dependency-reduced-pom.xml</exclude>
<exclude>**/javax.script.ScriptEngineFactory</exclude>
<exclude>.reviewboardrc</exclude>
......
......@@ -59,6 +59,11 @@
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
......
......@@ -33,15 +33,12 @@ import org.apache.atlas.model.discovery.SearchParameters;
import org.apache.atlas.model.instance.AtlasEntityHeader;
import org.apache.atlas.model.instance.AtlasObjectId;
import org.apache.atlas.model.profile.AtlasUserSavedSearch;
import org.apache.atlas.query.Expressions.AliasExpression;
import org.apache.atlas.query.Expressions.Expression;
import org.apache.atlas.query.Expressions.SelectExpression;
import org.apache.atlas.query.GremlinQuery;
import org.apache.atlas.query.GremlinTranslator;
import org.apache.atlas.query.QueryParams;
import org.apache.atlas.query.QueryParser;
import org.apache.atlas.query.QueryProcessor;
import org.apache.atlas.query.SelectExpressionHelper;
import org.apache.atlas.repository.Constants;
import org.apache.atlas.repository.graph.GraphBackedSearchIndexer;
import org.apache.atlas.repository.graph.GraphHelper;
......@@ -149,29 +146,29 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
LOG.warn("searchUsingDslQuery({}): expected an AtlasVertex; found unexpected entry in result {}", dslQuery, element);
}
}
} else if (firstElement instanceof Map &&
(((Map)firstElement).containsKey("theInstance") || ((Map)firstElement).containsKey("theTrait"))) {
} else if (gremlinQuery.hasSelectList()) {
ret.setAttributes(toAttributesResult(queryResult, gremlinQuery));
} else if (firstElement instanceof Map) {
for (Object element : queryResult) {
if (element instanceof Map) {
Map map = (Map)element;
if (map.containsKey("theInstance")) {
Object value = map.get("theInstance");
for (Object key : map.keySet()) {
Object value = map.get(key);
if (value instanceof List && CollectionUtils.isNotEmpty((List)value)) {
Object entry = ((List)value).get(0);
if (entry instanceof AtlasVertex) {
ret.addEntity(entityRetriever.toAtlasEntityHeader((AtlasVertex)entry));
for (Object o : (List) value) {
Object entry = o;
if (entry instanceof AtlasVertex) {
ret.addEntity(entityRetriever.toAtlasEntityHeader((AtlasVertex) entry));
}
}
}
}
} else {
LOG.warn("searchUsingDslQuery({}): expected a trait result; found unexpected entry in result {}", dslQuery, element);
}
}
} else if (gremlinQuery.hasSelectList()) {
ret.setAttributes(toAttributesResult(queryResult, gremlinQuery));
} else {
LOG.warn("searchUsingDslQuery({}/{}): found unexpected entry in result {}", dslQuery, dslQuery, gremlinQuery.queryStr());
}
}
......@@ -681,15 +678,16 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
}
private GremlinQuery toGremlinQuery(String query, int limit, int offset) throws AtlasBaseException {
QueryParams params = validateSearchParams(limit, offset);
Expression expression = QueryParser.apply(query, params);
QueryParams params = validateSearchParams(limit, offset);
Expression expression = QueryParser.apply(query, params);
if (expression == null) {
throw new AtlasBaseException(DISCOVERY_QUERY_FAILED, query);
}
Expression validExpression = QueryProcessor.validate(expression);
GremlinQuery gremlinQuery = new GremlinTranslator(validExpression).translate();
QueryProcessor queryProcessor = new QueryProcessor(typeRegistry);
Expression validExpression = queryProcessor.validate(expression);
GremlinQuery gremlinQuery = new GremlinTranslator(queryProcessor, validExpression).translate();
if (LOG.isDebugEnabled()) {
LOG.debug("Translated Gremlin Query: {}", gremlinQuery.queryStr());
......@@ -722,41 +720,68 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
private AttributeSearchResult toAttributesResult(List results, GremlinQuery query) {
AttributeSearchResult ret = new AttributeSearchResult();
List<String> names = new ArrayList<>();
List<List<Object>> values = new ArrayList<>();
List<String> names = extractNames(results);
List<List<Object>> values = extractValues(results);
// extract select attributes from gremlin query
SelectExpression selectExpr = SelectExpressionHelper.extractSelectExpression(query.expr());
if (selectExpr != null) {
List<AliasExpression> aliases = selectExpr.toJavaList();
ret.setName(names);
ret.setValues(values);
return ret;
}
if (CollectionUtils.isNotEmpty(aliases)) {
for (AliasExpression alias : aliases) {
names.add(alias.alias());
private List<String> extractNames(List results) {
List<String> names = new ArrayList<>();
for (Object obj : results) {
if (obj instanceof Map) {
Map map = (Map) obj;
if (MapUtils.isNotEmpty(map)) {
for (Object key : map.keySet()) {
names.add((String) key);
}
return names;
}
} else if (obj instanceof List) {
List list = (List) obj;
if (CollectionUtils.isNotEmpty(list)) {
for(Object o : list) {
names.add((String) o);
}
}
ret.setName(names);
}
}
return names;
}
private List<List<Object>> extractValues(List results) {
List<List<Object>> values = new ArrayList<>();
for (Object obj : results) {
if (obj instanceof Map) {
Map map = (Map) obj;
List<Object> list = new ArrayList<>();
if (MapUtils.isNotEmpty(map)) {
for (Object key : map.keySet()) {
Object vals = map.get(key);
values.add((List<Object>) vals);
if(vals instanceof List) {
List l = (List) vals;
for(Object o : l) {
list.add(o);
}
}
}
ret.setValues(values);
values.add(list);
}
} else if (obj instanceof List) {
List list = (List) obj;
if (CollectionUtils.isNotEmpty(list)) {
values.add(list);
}
ret.setValues(values);
}
}
return ret;
return values;
}
private boolean skipDeletedEntities(boolean excludeDeletedEntities, AtlasVertex<?, ?> vertex) {
......
/**
* 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
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.query;
import org.apache.atlas.query.antlr4.AtlasDSLParser.*;
import org.apache.atlas.query.antlr4.AtlasDSLParserBaseVisitor;
import org.apache.commons.collections.CollectionUtils;
public class DSLVisitor extends AtlasDSLParserBaseVisitor<String> {
private final QueryProcessor queryProcessor;
public DSLVisitor(QueryProcessor queryProcessor) {
this.queryProcessor = queryProcessor;
}
@Override
public String visitFromExpression(final FromExpressionContext ctx) {
FromSrcContext fromSrc = ctx.fromSrc();
AliasExprContext aliasExpr = fromSrc.aliasExpr();
if (aliasExpr != null) {
queryProcessor.addFromAlias(aliasExpr.identifier(0).getText(), aliasExpr.identifier(1).getText());
} else {
if (fromSrc.identifier() != null) {
queryProcessor.addFrom(fromSrc.identifier().getText());
} else {
queryProcessor.addFrom(fromSrc.literal().getText());
}
}
return super.visitFromExpression(ctx);
}
@Override
public String visitWhereClause(WhereClauseContext ctx) {
ExprContext expr = ctx.expr();
processExpr(expr.compE());
if (CollectionUtils.isNotEmpty(expr.exprRight())) {
for (ExprRightContext exprRight : expr.exprRight()) {
if (exprRight.K_AND() != null) {
// AND expression
processExpr(exprRight.compE());
}
// OR is tricky
}
}
return super.visitWhereClause(ctx);
}
private void processExpr(final CompEContext compE) {
if (compE != null && compE.isClause() == null && compE.hasClause() == null && compE.isClause() == null) {
ComparisonClauseContext comparisonClause = compE.comparisonClause();
if(comparisonClause == null) {
comparisonClause = compE.arithE().multiE().atomE().expr().compE().comparisonClause();
}
if (comparisonClause != null) {
String lhs = comparisonClause.arithE(0).getText();
String op = comparisonClause.operator().getText().toUpperCase();
String rhs = comparisonClause.arithE(1).getText();
queryProcessor.addWhere(lhs, op, rhs);
}
}
}
@Override
public String visitSelectExpr(SelectExprContext ctx) {
if (!(ctx.getParent() instanceof GroupByExpressionContext)) {
String[] items = new String[ctx.selectExpression().size()];
for (int i = 0; i < ctx.selectExpression().size(); i++) {
items[i] = ctx.selectExpression(i).expr().getText();
}
queryProcessor.addSelect(items);
}
return super.visitSelectExpr(ctx);
}
@Override
public String visitLimitOffset(LimitOffsetContext ctx) {
queryProcessor.addLimit(ctx.limitClause().NUMBER().toString(),
(ctx.offsetClause() == null ? "0" : ctx.offsetClause().NUMBER().getText()));
return super.visitLimitOffset(ctx);
}
@Override
public String visitOrderByExpr(OrderByExprContext ctx) {
queryProcessor.addOrderBy(ctx.expr().getText(), (ctx.sortOrder() != null && ctx.sortOrder().getText().equalsIgnoreCase("desc")));
return super.visitOrderByExpr(ctx);
}
@Override
public String visitIsClause(IsClauseContext ctx) {
queryProcessor.addFromIsA(ctx.arithE().getText(), ctx.identifier().getText());
return super.visitIsClause(ctx);
}
@Override
public String visitHasClause(HasClauseContext ctx) {
queryProcessor.addFromProperty(ctx.arithE().getText(), ctx.identifier().getText());
return super.visitHasClause(ctx);
}
@Override
public String visitGroupByExpression(GroupByExpressionContext ctx) {
String s = ctx.selectExpr().getText();
queryProcessor.addGroupBy(s);
return super.visitGroupByExpression(ctx);
}
}
......@@ -18,28 +18,24 @@
package org.apache.atlas.query;
import java.util.List;
import org.apache.atlas.query.antlr4.AtlasDSLParser.QueryContext;
public class Expressions {
public static class Expression {
private final QueryContext parsedQuery;
}
public static class AliasExpression {
public String alias() {
String ret = null;
return ret;
public Expression(QueryContext q) {
parsedQuery = q;
}
}
public static class SelectExpression {
public List<AliasExpression> toJavaList() {
List<AliasExpression> ret = null;
return ret;
public Expression isReady() {
return (parsedQuery != null ? this : null);
}
public void accept(DSLVisitor qv) {
qv.visit(parsedQuery);
}
}
}
......@@ -6,9 +6,9 @@
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
......@@ -17,26 +17,22 @@
*/
package org.apache.atlas.query;
import org.apache.atlas.query.Expressions.Expression;
public class GremlinQuery {
public boolean hasSelectList() {
boolean ret = false;
private final String queryStr;
private final boolean hasSelect;
return ret;
public GremlinQuery(String text, boolean hasSelect) {
this.queryStr = text;
this.hasSelect = hasSelect;
}
public String queryStr() {
String ret = null;
return ret;
public boolean hasSelectList() {
return this.hasSelect;
}
public Expression expr() {
Expression ret = null;
return ret;
public String queryStr() {
return queryStr;
}
}
......@@ -19,16 +19,23 @@ package org.apache.atlas.query;
import org.apache.atlas.query.Expressions.Expression;
public class GremlinTranslator {
private Expression expression;
private final QueryProcessor queryProcessor;
private Expression expression;
public GremlinTranslator(Expression expression) {
this.expression = expression;
public GremlinTranslator(QueryProcessor queryProcessor, Expression expression) {
this.expression = expression;
this.queryProcessor = queryProcessor;
}
public GremlinQuery translate() {
GremlinQuery ret = null;
DSLVisitor qv = new DSLVisitor(queryProcessor);
expression.accept(qv);
queryProcessor.close();
GremlinQuery ret = new GremlinQuery(queryProcessor.getText(), queryProcessor.hasSelect());
return ret;
}
}
......@@ -6,9 +6,9 @@
* 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
*
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.
......@@ -17,19 +17,30 @@
*/
package org.apache.atlas.query;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.TokenStream;
import org.apache.atlas.query.Expressions.Expression;
import org.apache.atlas.query.antlr4.AtlasDSLLexer;
import org.apache.atlas.query.antlr4.AtlasDSLParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class QueryParser {
private static final Logger LOG = LoggerFactory.getLogger(QueryParser.class);
private static final Set<String> RESERVED_KEYWORDS =
new HashSet<>(Arrays.asList("[", "]", "(", ")", "=", "<", ">", "!=", "<=", ">=", ",", "and", "or", "+", "-",
"*", "/", ".", "select", "from", "where", "groupby", "loop", "isa", "is", "has",
"as", "times", "withPath", "limit", "offset", "orderby", "count", "max", "min",
"sum", "by", "order", "like"));
"*", "/", ".", "select", "from", "where", "groupby", "loop", "isa", "is", "has",
"as", "times", "withPath", "limit", "offset", "orderby", "count", "max", "min",
"sum", "by", "order", "like"));
public static boolean isKeyword(String word) {
return RESERVED_KEYWORDS.contains(word);
......@@ -38,6 +49,18 @@ public class QueryParser {
public static Expression apply(String queryStr, QueryParams params) {
Expression ret = null;
try {
InputStream stream = new ByteArrayInputStream(queryStr.getBytes());
AtlasDSLLexer lexer = new AtlasDSLLexer(CharStreams.fromStream(stream));
TokenStream inputTokenStream = new CommonTokenStream(lexer);
AtlasDSLParser parser = new AtlasDSLParser(inputTokenStream);
ret = new Expression(parser.query());
} catch (IOException e) {
ret = null;
LOG.error(e.getMessage(), e);
}
return ret;
}
}
/**
* 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.
*/
lexer grammar AtlasDSLLexer;
fragment A: ('A'|'a');
fragment B: ('B'|'b');
fragment C: ('C'|'c');
fragment D: ('D'|'d');
fragment E: ('E'|'e');
fragment F: ('F'|'f');
fragment G: ('G'|'g');
fragment H: ('H'|'h');
fragment I: ('I'|'i');
fragment J: ('J'|'j');
fragment K: ('K'|'k');
fragment L: ('L'|'l');
fragment M: ('M'|'m');
fragment N: ('N'|'n');
fragment O: ('O'|'o');
fragment P: ('P'|'p');
fragment Q: ('Q'|'q');
fragment R: ('R'|'r');
fragment S: ('S'|'s');
fragment T: ('T'|'t');
fragment U: ('U'|'u');
fragment V: ('V'|'v');
fragment W: ('W'|'w');
fragment X: ('X'|'x');
fragment Y: ('Y'|'y');
fragment Z: ('Z'|'z');
fragment DIGIT: [0-9];
fragment LETTER: 'a'..'z'| 'A'..'Z' | '_';
// Comment skipping
SINGLE_LINE_COMMENT: '--' ~[\r\n]* -> channel(HIDDEN) ;
MULTILINE_COMMENT : '/*' .*? ( '*/' | EOF ) -> channel(HIDDEN) ;
WS: (' ' ' '* | [ \n\t\r]+) -> channel(HIDDEN) ;
// Lexer rules
NUMBER: (K_PLUS | K_MINUS)? DIGIT DIGIT* (E (K_PLUS | K_MINUS)? DIGIT DIGIT*)? ;
FLOATING_NUMBER: (K_PLUS | K_MINUS)? DIGIT+ K_DOT DIGIT+ (E (K_PLUS | K_MINUS)? DIGIT DIGIT*)? ;
BOOL: K_TRUE | K_FALSE ;
K_COMMA: ',' ;
K_PLUS: '+' ;
K_MINUS: '-' ;
K_STAR: '*' ;
K_DIV: '/' ;
K_DOT: '.' ;
K_LIKE: L I K E ;
K_AND: A N D ;
K_OR: O R ;
K_LPAREN: '(' ;
K_LBRACKET: '[' ;
K_RPAREN: ')' ;
K_RBRACKET: ']' ;
K_LT: '<' | L T ;
K_LTE: '<=' | L T E ;
K_EQ: '=' | E Q ;
K_NEQ: '!=' | N E Q ;
K_GT: '>' | G T ;
K_GTE: '>=' | G T E ;
K_FROM: F R O M ;
K_WHERE: W H E R E ;
K_ORDERBY: O R D E R B Y ;
K_GROUPBY: G R O U P B Y ;
K_LIMIT: L I M I T ;
K_SELECT: S E L E C T ;
K_MAX: M A X ;
K_MIN: M I N ;
K_SUM: S U M ;
K_COUNT: C O U N T ;
K_LOOP: L O O P ;
K_OFFSET: O F F S E T ;
K_AS: A S ;
K_ISA: I S A ;
K_IS: I S ;
K_HAS: H A S ;
K_ASC: A S C ;
K_DESC: D E S C ;
K_WITHPATH: W I T H P A T H ;
K_TRUE: T R U E ;
K_FALSE: F A L S E ;
KEYWORD: K_LIKE
| K_DOT
| K_SELECT
| K_AS
| K_HAS
| K_IS
| K_ISA
| K_WHERE
| K_LIMIT
| K_TRUE
| K_FALSE
| K_AND
| K_OR
| K_GROUPBY
| K_ORDERBY
| K_WITHPATH
| K_SUM
| K_MIN
| K_MAX
| K_OFFSET
| K_LOOP
| K_FROM
| K_DESC
| K_ASC
| K_COUNT
;
ID: STRING
|LETTER (LETTER|DIGIT)*
| LETTER (LETTER|DIGIT)* KEYWORD KEYWORD*
| KEYWORD KEYWORD* LETTER (LETTER|DIGIT)*
| LETTER (LETTER|DIGIT)* KEYWORD KEYWORD* LETTER (LETTER|DIGIT)*
;
STRING: '"' ~('"')* '"' | '\'' ~('\'')* '\'' | '`' ~('`')* '`';
\ No newline at end of file
SINGLE_LINE_COMMENT=1
MULTILINE_COMMENT=2
WS=3
NUMBER=4
FLOATING_NUMBER=5
BOOL=6
K_COMMA=7
K_PLUS=8
K_MINUS=9
K_STAR=10
K_DIV=11
K_DOT=12
K_LIKE=13
K_AND=14
K_OR=15
K_LPAREN=16
K_LBRACKET=17
K_RPAREN=18
K_RBRACKET=19
K_LT=20
K_LTE=21
K_EQ=22
K_NEQ=23
K_GT=24
K_GTE=25
K_FROM=26
K_WHERE=27
K_ORDERBY=28
K_GROUPBY=29
K_LIMIT=30
K_SELECT=31
K_MAX=32
K_MIN=33
K_SUM=34
K_COUNT=35
K_LOOP=36
K_OFFSET=37
K_AS=38
K_ISA=39
K_IS=40
K_HAS=41
K_ASC=42
K_DESC=43
K_WITHPATH=44
K_TRUE=45
K_FALSE=46
KEYWORD=47
ID=48
STRING=49
','=7
'+'=8
'-'=9
'*'=10
'/'=11
'.'=12
'('=16
'['=17
')'=18
']'=19
/**
* 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.
*/
parser grammar AtlasDSLParser;
options { tokenVocab=AtlasDSLLexer; }
// Start of rules, bottom-up (rules at the end are built using the core rules)
// Core rules
identifier: ID ;
operator: (K_LT | K_LTE | K_EQ | K_NEQ | K_GT | K_GTE | K_LIKE) ;
sortOrder: K_ASC | K_DESC ;
valueArray: K_LBRACKET STRING (K_COMMA STRING)* K_RBRACKET ;
literal: BOOL | NUMBER | FLOATING_NUMBER | (STRING | valueArray) ;
// Composite rules
limitClause: K_LIMIT NUMBER ;
offsetClause: K_OFFSET NUMBER ;
atomE: (identifier | literal) | K_LPAREN expr K_RPAREN ;
multiERight: (K_STAR | K_DIV) atomE ;
multiE: atomE multiERight* ;
arithERight: (K_PLUS | K_MINUS) multiE ;
arithE: multiE arithERight* ;
comparisonClause: arithE operator arithE ;
isClause: arithE (K_ISA | K_IS) identifier ;
hasClause: arithE K_HAS identifier ;
countClause: K_COUNT K_LPAREN K_RPAREN ;
maxClause: K_MAX K_LPAREN expr K_RPAREN ;
minClause: K_MIN K_LPAREN expr K_RPAREN ;
sumClause: K_SUM K_LPAREN expr K_RPAREN ;
exprRight: (K_AND | K_OR) compE ;
compE: comparisonClause
| isClause
| hasClause
| arithE
| countClause
| maxClause
| minClause
| sumClause
;
expr: compE exprRight* ;
limitOffset: limitClause offsetClause? ;
selectExpression: expr (K_AS identifier)? ;
selectExpr: selectExpression (K_COMMA selectExpression)* ;
aliasExpr: (identifier | literal) K_AS identifier ;
orderByExpr: K_ORDERBY expr sortOrder? ;
fromSrc: aliasExpr | (identifier | literal) ;
whereClause: K_WHERE expr ;
fromExpression: fromSrc whereClause? ;
fromClause: K_FROM fromExpression ;
selectClause: K_SELECT selectExpr ;
singleQrySrc: fromClause | whereClause | fromExpression | expr ;
loopExpression: K_LOOP K_LPAREN query K_RPAREN NUMBER? (K_AS identifier)? ;
groupByExpression: K_GROUPBY K_LPAREN selectExpr K_RPAREN ;
commaDelimitedQueries: singleQrySrc (K_COMMA singleQrySrc)* ;
spaceDelimitedQueries: singleQrySrc singleQrySrc* ;
querySrc: commaDelimitedQueries | spaceDelimitedQueries ;
query: querySrc loopExpression?
groupByExpression?
selectClause?
orderByExpr?
limitOffset? ;
queryWithPath: query (K_WITHPATH)? ;
SINGLE_LINE_COMMENT=1
MULTILINE_COMMENT=2
WS=3
NUMBER=4
FLOATING_NUMBER=5
BOOL=6
K_COMMA=7
K_PLUS=8
K_MINUS=9
K_STAR=10
K_DIV=11
K_DOT=12
K_LIKE=13
K_AND=14
K_OR=15
K_LPAREN=16
K_LBRACKET=17
K_RPAREN=18
K_RBRACKET=19
K_LT=20
K_LTE=21
K_EQ=22
K_NEQ=23
K_GT=24
K_GTE=25
K_FROM=26
K_WHERE=27
K_ORDERBY=28
K_GROUPBY=29
K_LIMIT=30
K_SELECT=31
K_MAX=32
K_MIN=33
K_SUM=34
K_COUNT=35
K_LOOP=36
K_OFFSET=37
K_AS=38
K_ISA=39
K_IS=40
K_HAS=41
K_ASC=42
K_DESC=43
K_WITHPATH=44
K_TRUE=45
K_FALSE=46
KEYWORD=47
ID=48
STRING=49
','=7
'+'=8
'-'=9
'*'=10
'/'=11
'.'=12
'('=16
'['=17
')'=18
']'=19
// Generated from AtlasDSLParser.g4 by ANTLR 4.7
package org.apache.atlas.query.antlr4;
import org.antlr.v4.runtime.tree.ParseTreeVisitor;
/**
* This interface defines a complete generic visitor for a parse tree produced
* by {@link AtlasDSLParser}.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
public interface AtlasDSLParserVisitor<T> extends ParseTreeVisitor<T> {
/**
* Visit a parse tree produced by {@link AtlasDSLParser#identifier}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitIdentifier(AtlasDSLParser.IdentifierContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#operator}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitOperator(AtlasDSLParser.OperatorContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#sortOrder}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSortOrder(AtlasDSLParser.SortOrderContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#valueArray}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitValueArray(AtlasDSLParser.ValueArrayContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#literal}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLiteral(AtlasDSLParser.LiteralContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#limitClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLimitClause(AtlasDSLParser.LimitClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#offsetClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitOffsetClause(AtlasDSLParser.OffsetClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#atomE}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAtomE(AtlasDSLParser.AtomEContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#multiERight}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMultiERight(AtlasDSLParser.MultiERightContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#multiE}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMultiE(AtlasDSLParser.MultiEContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#arithERight}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitArithERight(AtlasDSLParser.ArithERightContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#arithE}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitArithE(AtlasDSLParser.ArithEContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#comparisonClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitComparisonClause(AtlasDSLParser.ComparisonClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#isClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitIsClause(AtlasDSLParser.IsClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#hasClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitHasClause(AtlasDSLParser.HasClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#countClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCountClause(AtlasDSLParser.CountClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#maxClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMaxClause(AtlasDSLParser.MaxClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#minClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMinClause(AtlasDSLParser.MinClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#sumClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSumClause(AtlasDSLParser.SumClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#exprRight}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitExprRight(AtlasDSLParser.ExprRightContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#compE}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCompE(AtlasDSLParser.CompEContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#expr}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitExpr(AtlasDSLParser.ExprContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#limitOffset}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLimitOffset(AtlasDSLParser.LimitOffsetContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#selectExpression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSelectExpression(AtlasDSLParser.SelectExpressionContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#selectExpr}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSelectExpr(AtlasDSLParser.SelectExprContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#aliasExpr}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAliasExpr(AtlasDSLParser.AliasExprContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#orderByExpr}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitOrderByExpr(AtlasDSLParser.OrderByExprContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#fromSrc}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFromSrc(AtlasDSLParser.FromSrcContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#whereClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitWhereClause(AtlasDSLParser.WhereClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#fromExpression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFromExpression(AtlasDSLParser.FromExpressionContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#fromClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitFromClause(AtlasDSLParser.FromClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#selectClause}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSelectClause(AtlasDSLParser.SelectClauseContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#singleQrySrc}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSingleQrySrc(AtlasDSLParser.SingleQrySrcContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#loopExpression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitLoopExpression(AtlasDSLParser.LoopExpressionContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#groupByExpression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitGroupByExpression(AtlasDSLParser.GroupByExpressionContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#commaDelimitedQueries}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCommaDelimitedQueries(AtlasDSLParser.CommaDelimitedQueriesContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#spaceDelimitedQueries}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSpaceDelimitedQueries(AtlasDSLParser.SpaceDelimitedQueriesContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#querySrc}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitQuerySrc(AtlasDSLParser.QuerySrcContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#query}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitQuery(AtlasDSLParser.QueryContext ctx);
/**
* Visit a parse tree produced by {@link AtlasDSLParser#queryWithPath}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitQueryWithPath(AtlasDSLParser.QueryWithPathContext ctx);
}
\ No newline at end of file
......@@ -15,16 +15,42 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.atlas.query;
import org.apache.atlas.query.Expressions.Expression;
import org.apache.atlas.query.Expressions.SelectExpression;
package org.apache.atlas.services;
import com.google.inject.Inject;
import org.apache.atlas.TestModules;
import org.apache.atlas.discovery.EntityDiscoveryService;
import org.apache.atlas.exception.AtlasBaseException;
import org.apache.atlas.model.discovery.AtlasSearchResult;
import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.store.AtlasTypeDefStore;
import org.apache.atlas.type.AtlasTypeRegistry;
import org.junit.Assert;
import org.testng.annotations.Guice;
import org.testng.annotations.Test;
public class SelectExpressionHelper {
public static SelectExpression extractSelectExpression(Expression expr) {
SelectExpression ret = null;
@Guice(modules = TestModules.TestOnlyModule.class)
public class EntityDiscoveryServiceTest {
@Inject
AtlasTypeRegistry typeRegistry;
return ret;
@Inject
private AtlasTypeDefStore typeDefStore;
@Inject
private AtlasGraph atlasGraph;
@Inject
EntityDiscoveryService entityDiscoveryService;
@Test
public void dslTest() throws AtlasBaseException {
//String dslQuery = "DB where name = \"Reporting\"";
String dslQuery = "hive_table where Asset.name = \"testtable_x_0\"";
AtlasSearchResult result = entityDiscoveryService.searchUsingDslQuery(dslQuery, 20 , 0);
Assert.assertNotNull(result);
}
}
......@@ -471,7 +471,7 @@ public class QuickStartV2 {
"DB where DB.name=\"Reporting\" select name, owner",
"DB has name",
"DB where DB has name",
"DB, Table",
//--TODO: Fix "DB, Table", // Table, db; Table db works
"DB is JdbcAccess",
"from Table",
"Table",
......@@ -483,12 +483,12 @@ public class QuickStartV2 {
"Column where Column.name=\"customer_id\"",
"from Table select Table.name",
"DB where (name = \"Reporting\")",
"DB where (name = \"Reporting\") select name as _col_0, owner as _col_1",
//--TODO: Fix "DB where (name = \"Reporting\") select name as _col_0, owner as _col_1",
"DB where DB is JdbcAccess",
"DB where DB has name",
"DB Table",
//--TODO: Fix "DB Table",
"DB as db1 Table where (db1.name = \"Reporting\")",
"DB where (name = \"Reporting\") select name as _col_0, (createTime + 1) as _col_1 ",
//--TODO: Fix "DB where (name = \"Reporting\") select name as _col_0, (createTime + 1) as _col_1 ", // N
DIMENSION_CLASSIFICATION,
JDBC_CLASSIFICATION,
ETL_CLASSIFICATION,
......@@ -505,22 +505,26 @@ public class QuickStartV2 {
System.out.println("\nSample DSL Queries: ");
for (String dslQuery : getDSLQueries()) {
AtlasSearchResult results = atlasClientV2.dslSearchWithParams(dslQuery, 10, 0);
if (results != null) {
List<AtlasEntityHeader> entitiesResult = results.getEntities();
List<AtlasFullTextResult> fullTextResults = results.getFullTextResult();
AttributeSearchResult attribResult = results.getAttributes();
if (CollectionUtils.isNotEmpty(entitiesResult)) {
System.out.println("query [" + dslQuery + "] returned [" + entitiesResult.size() + "] rows.");
} else if (CollectionUtils.isNotEmpty(fullTextResults)) {
System.out.println("query [" + dslQuery + "] returned [" + fullTextResults.size() + "] rows.");
} else if (attribResult != null) {
System.out.println("query [" + dslQuery + "] returned [" + attribResult.getValues().size() + "] rows.");
try {
AtlasSearchResult results = atlasClientV2.dslSearchWithParams(dslQuery, 10, 0);
if (results != null) {
List<AtlasEntityHeader> entitiesResult = results.getEntities();
List<AtlasFullTextResult> fullTextResults = results.getFullTextResult();
AttributeSearchResult attribResult = results.getAttributes();
if (CollectionUtils.isNotEmpty(entitiesResult)) {
System.out.println("query [" + dslQuery + "] returned [" + entitiesResult.size() + "] rows.");
} else if (CollectionUtils.isNotEmpty(fullTextResults)) {
System.out.println("query [" + dslQuery + "] returned [" + fullTextResults.size() + "] rows.");
} else if (attribResult != null) {
System.out.println("query [" + dslQuery + "] returned [" + attribResult.getValues().size() + "] rows.");
}
} else {
System.out.println("query [" + dslQuery + "] failed, results:" + results);
}
} else {
System.out.println("query [" + dslQuery + "] failed, results:" + results);
} catch (Exception e) {
System.out.println("query [" + dslQuery + "] execution failed!");
}
}
}
......
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