Repository: atlas Updated Branches: refs/heads/master faeecf101 -> 5614bf0d6
ATLAS-2229: DSL implementation using ANTLR #3 - Select, GroupBy, OrderBy Signed-off-by: Madhan Neethiraj <mad...@apache.org> Project: http://git-wip-us.apache.org/repos/asf/atlas/repo Commit: http://git-wip-us.apache.org/repos/asf/atlas/commit/5614bf0d Tree: http://git-wip-us.apache.org/repos/asf/atlas/tree/5614bf0d Diff: http://git-wip-us.apache.org/repos/asf/atlas/diff/5614bf0d Branch: refs/heads/master Commit: 5614bf0d6da416455c366cf44bbaace72b0893b2 Parents: faeecf1 Author: Ashutosh Mestry <ames...@hortonworks.com> Authored: Wed Dec 20 13:37:23 2017 -0800 Committer: Madhan Neethiraj <mad...@apache.org> Committed: Wed Dec 20 23:48:55 2017 -0800 ---------------------------------------------------------------------- .../atlas/discovery/EntityDiscoveryService.java | 6 +- .../java/org/apache/atlas/query/DSLVisitor.java | 48 ++- .../apache/atlas/query/IdentifierHelper.java | 8 + .../org/apache/atlas/query/QueryProcessor.java | 338 +++++++++++++------ .../apache/atlas/query/QueryProcessorTest.java | 80 +++-- 5 files changed, 331 insertions(+), 149 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/atlas/blob/5614bf0d/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java index e742496..ece1516 100644 --- a/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java +++ b/repository/src/main/java/org/apache/atlas/discovery/EntityDiscoveryService.java @@ -720,8 +720,10 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { private AttributeSearchResult toAttributesResult(List results, GremlinQuery query) { AttributeSearchResult ret = new AttributeSearchResult(); - List<String> names = extractNames(results); - List<List<Object>> values = extractValues(results); +// List<String> names = extractNames(results); +// List<List<Object>> values = extractValues(results); + List<String> names = (List<String>) results.get(0); + List<List<Object>> values = extractValues(results.subList(1, results.size())); ret.setName(names); ret.setValues(values); http://git-wip-us.apache.org/repos/asf/atlas/blob/5614bf0d/repository/src/main/java/org/apache/atlas/query/DSLVisitor.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/query/DSLVisitor.java b/repository/src/main/java/org/apache/atlas/query/DSLVisitor.java index 064f6dd..b597a0d 100644 --- a/repository/src/main/java/org/apache/atlas/query/DSLVisitor.java +++ b/repository/src/main/java/org/apache/atlas/query/DSLVisitor.java @@ -21,13 +21,12 @@ 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; -import org.apache.commons.lang3.tuple.MutablePair; -import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class DSLVisitor extends AtlasDSLParserBaseVisitor<String> { private static final Logger LOG = LoggerFactory.getLogger(DSLVisitor.class); @@ -68,7 +67,7 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<String> { } queryProcessor.addLimit(ctx.limitClause().NUMBER().toString(), - (ctx.offsetClause() == null ? "0" : ctx.offsetClause().NUMBER().getText())); + (ctx.offsetClause() == null ? "0" : ctx.offsetClause().NUMBER().getText())); return super.visitLimitOffset(ctx); } @@ -78,17 +77,46 @@ public class DSLVisitor extends AtlasDSLParserBaseVisitor<String> { LOG.debug("=> DSLVisitor.visitSelectExpr({})", ctx); } + // Select can have only attributes, aliased attributes or aggregate functions + + // Groupby attr also represent select expr, no processing is needed in that case + // visit groupBy would handle the select expr appropriately if (!(ctx.getParent() instanceof GroupByExpressionContext)) { - List<Pair<String, String>> items = new ArrayList<>(); - for (int i = 0; i < ctx.selectExpression().size(); i++) { - String idf = ctx.selectExpression(i).expr().getText(); - String alias = (ctx.selectExpression(i).K_AS() != null) ? - ctx.selectExpression(i).identifier().getText() : ""; + String[] items = new String[ctx.selectExpression().size()]; + String[] labels = new String[ctx.selectExpression().size()]; + + QueryProcessor.SelectExprMetadata selectExprMetadata = new QueryProcessor.SelectExprMetadata(); - items.add(new MutablePair<String, String>(idf, alias)); + for (int i = 0; i < ctx.selectExpression().size(); i++) { + SelectExpressionContext selectExpression = ctx.selectExpression(i); + CountClauseContext countClause = selectExpression.expr().compE().countClause(); + SumClauseContext sumClause = selectExpression.expr().compE().sumClause(); + MinClauseContext minClause = selectExpression.expr().compE().minClause(); + MaxClauseContext maxClause = selectExpression.expr().compE().maxClause(); + IdentifierContext identifier = selectExpression.identifier(); + + labels[i] = identifier != null ? identifier.getText() : selectExpression.getText(); + + if (Objects.nonNull(countClause)) { + items[i] = "count"; + selectExprMetadata.setCountIdx(i); + } else if (Objects.nonNull(sumClause)) { + items[i] = sumClause.expr().getText(); + selectExprMetadata.setSumIdx(i); + } else if (Objects.nonNull(minClause)) { + items[i] = minClause.expr().getText(); + selectExprMetadata.setMinIdx(i); + } else if (Objects.nonNull(maxClause)) { + items[i] = maxClause.expr().getText(); + selectExprMetadata.setMaxIdx(i); + } else { + items[i] = selectExpression.expr().getText(); + } } - queryProcessor.addSelect(items); + selectExprMetadata.setItems(items); + selectExprMetadata.setLabels(labels); + queryProcessor.addSelect(selectExprMetadata); } return super.visitSelectExpr(ctx); } http://git-wip-us.apache.org/repos/asf/atlas/blob/5614bf0d/repository/src/main/java/org/apache/atlas/query/IdentifierHelper.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/query/IdentifierHelper.java b/repository/src/main/java/org/apache/atlas/query/IdentifierHelper.java index 6bbadc4..0c7e2ed 100644 --- a/repository/src/main/java/org/apache/atlas/query/IdentifierHelper.java +++ b/repository/src/main/java/org/apache/atlas/query/IdentifierHelper.java @@ -189,6 +189,14 @@ public class IdentifierHelper { return isPrimitive; } + public boolean isAttribute() { + return isAttribute; + } + + public String getAttributeName() { + return attributeName; + } + public String getEdgeLabel() { return edgeLabel; } http://git-wip-us.apache.org/repos/asf/atlas/blob/5614bf0d/repository/src/main/java/org/apache/atlas/query/QueryProcessor.java ---------------------------------------------------------------------- diff --git a/repository/src/main/java/org/apache/atlas/query/QueryProcessor.java b/repository/src/main/java/org/apache/atlas/query/QueryProcessor.java index 599f370..76a10e3 100644 --- a/repository/src/main/java/org/apache/atlas/query/QueryProcessor.java +++ b/repository/src/main/java/org/apache/atlas/query/QueryProcessor.java @@ -21,20 +21,29 @@ import com.google.common.annotations.VisibleForTesting; import org.apache.atlas.exception.AtlasBaseException; import org.apache.atlas.model.TypeCategory; import org.apache.atlas.model.discovery.SearchParameters; +import org.apache.atlas.model.typedef.AtlasBaseTypeDef; import org.apache.atlas.query.Expressions.Expression; -import org.apache.atlas.type.*; +import org.apache.atlas.type.AtlasArrayType; +import org.apache.atlas.type.AtlasBuiltInTypes; +import org.apache.atlas.type.AtlasEntityType; +import org.apache.atlas.type.AtlasStructType; +import org.apache.atlas.type.AtlasType; +import org.apache.atlas.type.AtlasTypeRegistry; import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang3.tuple.Pair; import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; +import java.util.stream.Stream; public class QueryProcessor { private static final Logger LOG = LoggerFactory.getLogger(QueryProcessor.class); @@ -42,13 +51,18 @@ public class QueryProcessor { private final int DEFAULT_QUERY_RESULT_LIMIT = 25; private final int DEFAULT_QUERY_RESULT_OFFSET = 0; + private final List<String> errorList = new ArrayList<>(); + private final GremlinClauseList queryClauses = new GremlinClauseList(); + + private int providedLimit = DEFAULT_QUERY_RESULT_LIMIT; + private int providedOffset = DEFAULT_QUERY_RESULT_OFFSET; + private boolean hasSelect = false; + private boolean isSelectNoop = false; + private boolean hasGrpBy = false; + + private final org.apache.atlas.query.Lookup lookup; private final boolean isNestedQuery; - private final List<String> errorList = new ArrayList<>(); - private final GremlinClauseList queryClauses = new GremlinClauseList(); - private int providedLimit = DEFAULT_QUERY_RESULT_LIMIT; - private int providedOffset = DEFAULT_QUERY_RESULT_OFFSET; private int currentStep; - private final org.apache.atlas.query.Lookup lookup; private Context context; @Inject @@ -83,15 +97,6 @@ public class QueryProcessor { return expression.isReady(); } - private void init() { - if (!isNestedQuery) { - add(GremlinClause.G); - add(GremlinClause.V); - } else { - add(GremlinClause.NESTED_START); - } - } - public void addFrom(String typeName) { if (LOG.isDebugEnabled()) { LOG.debug("addFrom(typeName={})", typeName); @@ -117,17 +122,6 @@ public class QueryProcessor { } } - private void introduceType(IdentifierHelper.Advice ia) { - if (!ia.isPrimitive() && ia.getIntroduceType()) { - add(GremlinClause.OUT, ia.getEdgeLabel()); - context.registerActive(ia.getTypeName()); - } - } - - private IdentifierHelper.Advice getAdvice(String actualTypeName) { - return IdentifierHelper.create(context, lookup, actualTypeName); - } - public void addFromProperty(String typeName, String attribute) { if (LOG.isDebugEnabled()) { LOG.debug("addFromProperty(typeName={}, attribute={})", typeName, attribute); @@ -202,51 +196,60 @@ public class QueryProcessor { queryClauses.add(GremlinClause.OR, StringUtils.join(clauses, ',')); } - public void addSelect(List<Pair<String, String>> items) { + public void addSelect(SelectExprMetadata selectExprMetadata) { + String[] items = selectExprMetadata.getItems(); + String[] labels = selectExprMetadata.getLabels(); if (LOG.isDebugEnabled()) { - LOG.debug("addSelect(items.length={})", items != null ? items.size() : -1); + LOG.debug("addSelect(items.length={})", items != null ? items.length : 0); } - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < items.size(); i++) { - IdentifierHelper.Advice ia = getAdvice(items.get(i).getLeft()); - if(StringUtils.isNotEmpty(items.get(i).getRight())) { - context.aliasMap.put(items.get(i).getRight(), ia.getQualifiedName()); - } + if (items != null) { + for (int i = 0; i < items.length; i++) { + IdentifierHelper.Advice ia = getAdvice(items[i]); - if(!ia.isPrimitive() && ia.getIntroduceType()) { - add(GremlinClause.OUT, ia.getEdgeLabel()); - add(GremlinClause.AS, getCurrentStep()); - addSelectClause(getCurrentStep()); - incrementCurrentStep(); - } else { - sb.append(quoted(ia.getQualifiedName())); - } + if(!labels[i].equals(items[i])) { + context.aliasMap.put(labels[i], ia.getQualifiedName()); + } - if (i != items.size() - 1) { - sb.append(","); + if (i == selectExprMetadata.getCountIdx()) { + items[i] = GremlinClause.INLINE_COUNT.get(); + } else if (i == selectExprMetadata.getMinIdx()) { + items[i] = GremlinClause.INLINE_MIN.get(ia.getQualifiedName(), ia.getQualifiedName()); + } else if (i == selectExprMetadata.getMaxIdx()) { + items[i] = GremlinClause.INLINE_MAX.get(ia.getQualifiedName(), ia.getQualifiedName()); + } else if (i == selectExprMetadata.getSumIdx()) { + items[i] = GremlinClause.INLINE_SUM.get(ia.getQualifiedName(), ia.getQualifiedName()); + } else { + if (!ia.isPrimitive() && ia.getIntroduceType()) { + add(GremlinClause.OUT, ia.getEdgeLabel()); + context.registerActive(ia.getTypeName()); + + int dotIdx = ia.get().indexOf("."); + if (dotIdx != -1) { + IdentifierHelper.Advice iax = getAdvice(ia.get()); + items[i] = GremlinClause.INLINE_GET_PROPERTY.get(iax.getQualifiedName()); + } else { + isSelectNoop = true; + } + } else { + items[i] = GremlinClause.INLINE_GET_PROPERTY.get(ia.getQualifiedName()); + } + } } - } - if (!StringUtils.isEmpty(sb.toString())) { - addValueMapClause(sb.toString()); - } - } + // If GroupBy clause exists then the query spits out a List<Map<String, List<AtlasVertex>>> otherwise the query returns List<AtlasVertex> + // Different transformations are needed for DSLs with groupby and w/o groupby + GremlinClause transformationFn; + if (isSelectNoop) { + transformationFn = GremlinClause.SELECT_EXPR_NOOP_FN; + } else { + transformationFn = hasGrpBy ? GremlinClause.SELECT_WITH_GRPBY_HELPER_FN : GremlinClause.SELECT_EXPR_HELPER_FN; + } + queryClauses.add(0, transformationFn, getJoinedQuotedStr(labels), String.join(",", items)); + queryClauses.add(GremlinClause.INLINE_TRANSFORM_CALL); - private void addSelectClause(String s) { - if (LOG.isDebugEnabled()) { - LOG.debug("addSelectClause(s={})", s); + hasSelect = true; } - - add(GremlinClause.SELECT, s); - } - - private String getCurrentStep() { - return String.format("s%d", currentStep); - } - - private void incrementCurrentStep() { - currentStep++; } public QueryProcessor createNestedProcessor() { @@ -255,14 +258,6 @@ public class QueryProcessor { return qp; } - private void addValueMapClause(String s) { - if (LOG.isDebugEnabled()) { - LOG.debug("addValueMapClause(s={})", s); - } - - add(GremlinClause.VALUE_MAP, s); - } - public void addFromAlias(String typeName, String alias) { if (LOG.isDebugEnabled()) { LOG.debug("addFromAlias(typeName={}, alias={})", typeName, alias); @@ -281,19 +276,6 @@ public class QueryProcessor { add(GremlinClause.AS, stepName); } - private void add(GremlinClause clause, String... args) { - queryClauses.add(new GremlinClauseValue(clause, clause.get(args))); - } - - private void addRangeClause(String startIndex, String endIndex) { - if (LOG.isDebugEnabled()) { - LOG.debug("addRangeClause(startIndex={}, endIndex={})", startIndex, endIndex); - } - - add(GremlinClause.RANGE, startIndex, startIndex, endIndex); - } - - public void addGroupBy(String item) { if (LOG.isDebugEnabled()) { LOG.debug("addGroupBy(item={})", item); @@ -301,16 +283,7 @@ public class QueryProcessor { add(GremlinClause.GROUP); addByClause(item, false); - } - - private void addByClause(String name, boolean descr) { - if (LOG.isDebugEnabled()) { - LOG.debug("addByClause(name={})", name, descr); - } - - IdentifierHelper.Advice ia = getAdvice(name); - add((!descr) ? GremlinClause.BY : GremlinClause.BY_DESC, - ia.getQualifiedName()); + hasGrpBy = true; } public void addLimit(String limit, String offset) { @@ -335,27 +308,38 @@ public class QueryProcessor { addLimit(Integer.toString(providedLimit), Integer.toString(providedOffset)); } + updatePosition(GremlinClause.LIMIT); add(GremlinClause.TO_LIST); + updatePosition(GremlinClause.INLINE_TRANSFORM_CALL); } public String getText() { + String ret; String[] items = new String[queryClauses.size()]; - for (int i = 0; i < queryClauses.size(); i++) { + int startIdx = hasSelect ? 1 : 0; + int endIdx = hasSelect ? queryClauses.size() - 1 : queryClauses.size(); + for (int i = startIdx; i < endIdx; i++) { items[i] = queryClauses.getValue(i); } - String ret = StringUtils.join(items, "."); + if (hasSelect) { + String body = StringUtils.join(Stream.of(items).filter(Objects::nonNull).toArray(), "."); + String inlineFn = queryClauses.getValue(queryClauses.size() - 1); + String funCall = String.format(inlineFn, body); + ret = queryClauses.getValue(0) + funCall; + } else { + ret = String.join(".", items); + } if (LOG.isDebugEnabled()) { LOG.debug("getText() => {}", ret); } - return ret; } public boolean hasSelect() { - return (queryClauses.hasClause(GremlinClause.VALUE_MAP) != -1); + return hasSelect; } public void addOrderBy(String name, boolean isDesc) { @@ -365,19 +349,73 @@ public class QueryProcessor { add(GremlinClause.ORDER); addByClause(name, isDesc); - updateSelectClausePosition(); } - private void updateSelectClausePosition() { - int selectClauseIndex = queryClauses.hasClause(GremlinClause.VALUE_MAP); - if(-1 == selectClauseIndex) { + private void updatePosition(GremlinClause clause) { + int index = queryClauses.hasClause(clause); + if(-1 == index) { return; } - GremlinClauseValue gcv = queryClauses.remove(selectClauseIndex); + GremlinClauseValue gcv = queryClauses.remove(index); queryClauses.add(gcv); } + private void init() { + if (!isNestedQuery) { + add(GremlinClause.G); + add(GremlinClause.V); + } else { + add(GremlinClause.NESTED_START); + } + } + + private void introduceType(IdentifierHelper.Advice ia) { + if (!ia.isPrimitive() && ia.getIntroduceType()) { + add(GremlinClause.OUT, ia.getEdgeLabel()); + context.registerActive(ia.getTypeName()); + } + } + + private IdentifierHelper.Advice getAdvice(String actualTypeName) { + return IdentifierHelper.create(context, lookup, actualTypeName); + } + + private String getJoinedQuotedStr(String[] elements) { + StringJoiner joiner = new StringJoiner(","); + Arrays.stream(elements).map(x -> "'" + x + "'").forEach(joiner::add); + return joiner.toString(); + } + + private void add(GremlinClause clause, String... args) { + queryClauses.add(new GremlinClauseValue(clause, clause.get(args))); + } + + private void add(int idx, GremlinClause clause, String... args) { + queryClauses.add(idx, new GremlinClauseValue(clause, clause.get(args))); + } + + private void addRangeClause(String startIndex, String endIndex) { + if (LOG.isDebugEnabled()) { + LOG.debug("addRangeClause(startIndex={}, endIndex={})", startIndex, endIndex); + } + + if (hasSelect) { + add(queryClauses.size() - 1, GremlinClause.RANGE, startIndex, startIndex, endIndex); + } else { + add(GremlinClause.RANGE, startIndex, startIndex, endIndex); + } + } + + private void addByClause(String name, boolean descr) { + if (LOG.isDebugEnabled()) { + LOG.debug("addByClause(name={})", name, descr); + } + + IdentifierHelper.Advice ia = getAdvice(name); + add((!descr) ? GremlinClause.BY : GremlinClause.BY_DESC, ia.getQualifiedName()); + } + private enum GremlinClause { AS("as('%s')"), BY("by('%s')"), @@ -407,6 +445,17 @@ public class QueryProcessor { TEXT_PREFIX("has('%s', org.janusgraph.core.attribute.Text.textPrefix(%s))"), TEXT_SUFFIX("has('%s', org.janusgraph.core.attribute.Text.textRegex(\".*\" + %s))"), TRAIT("has('__traitNames', within('%s'))"), + SELECT_EXPR_NOOP_FN("def f(r){ r }; "), + SELECT_EXPR_HELPER_FN("def f(r){ return [[%s]].plus(r.collect({[%s]})).unique(); }; "), + SELECT_WITH_GRPBY_HELPER_FN("def f(r){ return [[%s]].plus(r.collect({it.values()}).flatten().collect({[%s]})).unique(); }; "), + INLINE_COUNT("r.size()"), + INLINE_SUM("r.sum({it.value('%s')}).value('%s')"), + INLINE_MAX("r.max({it.value('%s')}).value('%s')"), + INLINE_MIN("r.min({it.value('%s')}).value('%s')"), + INLINE_GET_PROPERTY("it.value('%s')"), + INLINE_OUT_VERTEX("it.out('%s')"), + INLINE_OUT_VERTEX_VALUE("it.out('%s').value('%s')"), // This might require more closure introduction :( + INLINE_TRANSFORM_CALL("f(%s)"), V("V()"), VALUE_MAP("valueMap(%s)"); @@ -452,14 +501,30 @@ public class QueryProcessor { list.add(g); } + public void add(int idx, GremlinClauseValue g) { + list.add(idx, g); + } + public void add(GremlinClauseValue g, AtlasEntityType t) { add(g); } + public void add(int idx, GremlinClauseValue g, AtlasEntityType t) { + add(idx, g); + } + public void add(GremlinClause clause, String... args) { list.add(new GremlinClauseValue(clause, clause.get(args))); } + public void add(int i, GremlinClause clause, String... args) { + list.add(i, new GremlinClauseValue(clause, clause.get(args))); + } + + public GremlinClauseValue getAt(int i) { + return list.get(i); + } + public String getValue(int i) { return list.get(i).value; } @@ -500,8 +565,8 @@ public class QueryProcessor { static class Context { private final List<String> errorList; org.apache.atlas.query.Lookup lookup; - private AtlasType activeType; Map<String, String> aliasMap = new HashMap<>(); + private AtlasType activeType; public Context(List<String> errorList, org.apache.atlas.query.Lookup lookup) { this.lookup = lookup; @@ -682,16 +747,71 @@ public class QueryProcessor { @Override public boolean isDate(Context context, String attributeName) { AtlasEntityType et = context.getActiveEntityType(); - if(et == null) { + if (et == null) { return false; } AtlasType attr = et.getAttributeType(attributeName); - if(attr == null) { - return false; - } + return attr != null && attr.getTypeName().equals(AtlasBaseTypeDef.ATLAS_TYPE_DATE); + + } + } + + static class SelectExprMetadata { + private String[] items; + private String[] labels; + + private int countIdx = -1; + private int sumIdx = -1; + private int maxIdx = -1; + private int minIdx = -1; + + public String[] getItems() { + return items; + } + + public int getCountIdx() { + return countIdx; + } + + public void setCountIdx(final int countIdx) { + this.countIdx = countIdx; + } + + public int getSumIdx() { + return sumIdx; + } + + public void setSumIdx(final int sumIdx) { + this.sumIdx = sumIdx; + } + + public int getMaxIdx() { + return maxIdx; + } + + public void setMaxIdx(final int maxIdx) { + this.maxIdx = maxIdx; + } + + public int getMinIdx() { + return minIdx; + } + + public void setMinIdx(final int minIdx) { + this.minIdx = minIdx; + } + + public String[] getLabels() { + return labels; + } + + public void setItems(final String[] items) { + this.items = items; + } - return attr.getTypeName().equals("date"); + public void setLabels(final String[] labels) { + this.labels = labels; } } } http://git-wip-us.apache.org/repos/asf/atlas/blob/5614bf0d/repository/src/test/java/org/apache/atlas/query/QueryProcessorTest.java ---------------------------------------------------------------------- diff --git a/repository/src/test/java/org/apache/atlas/query/QueryProcessorTest.java b/repository/src/test/java/org/apache/atlas/query/QueryProcessorTest.java index 4c6d314..b873141 100644 --- a/repository/src/test/java/org/apache/atlas/query/QueryProcessorTest.java +++ b/repository/src/test/java/org/apache/atlas/query/QueryProcessorTest.java @@ -32,9 +32,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -72,8 +70,8 @@ public class QueryProcessorTest { @Test public void DBHasName() { String expected = "g.V().has('__typeName', 'DB').has('DB.name').limit(25).toList()"; - // verify("DB has name", expected); - verify("DB where DB has name", expected); + verify("DB has name", expected); + verify("DB where DB has name", expected); } @Test @@ -83,15 +81,19 @@ public class QueryProcessorTest { @Test public void DBasDSelect() { - String expected = "g.V().has('__typeName', 'DB').as('d').valueMap('DB.name','DB.owner')"; - verify("DB as d select d.name, d.owner", expected + ".limit(25).toList()"); - verify("DB as d select d.name, d.owner limit 10", expected + ".limit(10).toList()"); + String expected = "def f(r){ return [['d.name','d.owner']].plus(r.collect({[it.value('DB.name'),it.value('DB.owner')]})).unique(); }; f(g.V().has('__typeName', 'DB').as('d')"; + verify("DB as d select d.name, d.owner", expected + ".limit(25).toList())"); + verify("DB as d select d.name, d.owner limit 10", expected + ".limit(10).toList())"); } @Test public void tableSelectColumns() { - verify("Table select columns limit 10", "g.V().has('__typeName', 'Table').out('__Table.columns').as('s0').select('s0').limit(10).toList()"); - verify("Table select db.name", "g.V().has('__typeName', 'Table').out('__DB.Table').as('s0').select('s0').limit(25).toList()"); + String exMain = "g.V().has('__typeName', 'Table').out('__Table.columns').limit(10).toList()"; + String exSel = "def f(r){ r }"; + verify("Table select columns limit 10", getExpected(exSel, exMain)); + + String exMain2 = "g.V().has('__typeName', 'Table').out('__Table.db').limit(25).toList()"; + verify("Table select db.name", getExpected(exSel, exMain2)); } @Test(enabled = false) @@ -112,9 +114,16 @@ public class QueryProcessorTest { verify("from DB orderby name", expected); verify("from DB as d orderby d.owner limit 3", "g.V().has('__typeName', 'DB').as('d').order().by('DB.owner').limit(3).toList()"); verify("DB as d orderby d.owner limit 3", "g.V().has('__typeName', 'DB').as('d').order().by('DB.owner').limit(3).toList()"); - verify("DB as d select name, owner orderby d.owner limit 3", "g.V().has('__typeName', 'DB').as('d').order().by('DB.owner').valueMap('DB.name','DB.owner').limit(3).toList()"); + + + String exSel = "def f(r){ return [['d.name','d.owner']].plus(r.collect({[it.value('DB.name'),it.value('DB.owner')]})).unique(); }"; + String exMain = "g.V().has('__typeName', 'DB').as('d').order().by('DB.owner)').limit(25).toList()"; + verify("DB as d select d.name, d.owner orderby (d.owner) limit 25", getExpected(exSel, exMain)); + + String exMain2 = "g.V().has('__typeName', 'Table').and(__.has('Table.name', eq(\"sales_fact\")),__.has('Table.createTime', gt('1388563200000'))).order().by('Table.createTime').limit(25).toList()"; + String exSel2 = "def f(r){ return [['_col_0','_col_1']].plus(r.collect({[it.value('Table.name'),it.value('Table.createTime')]})).unique(); }"; verify("Table where (name = \"sales_fact\" and createTime > \"2014-01-01\" ) select name as _col_0, createTime as _col_1 orderby _col_1", - "g.V().has('__typeName', 'Table').and(__.has('Table.name', eq(\"sales_fact\")),__.has('Table.createTime', gt('1388563200000'))).order().by('Table.createTime').valueMap('Table.name','Table.createTime').limit(25).toList()"); + getExpected(exSel2, exMain2)); } @Test @@ -124,47 +133,55 @@ public class QueryProcessorTest { @Test public void fromDBSelect() { - verify("from DB select DB.name, DB.owner", "g.V().has('__typeName', 'DB').valueMap('DB.name','DB.owner').limit(25).toList()"); + String expected = "def f(r){ return [['DB.name','DB.owner']].plus(r.collect({[it.value('DB.name'),it.value('DB.owner')]})).unique(); }; f(g.V().has('__typeName', 'DB').limit(25).toList())"; + verify("from DB select DB.name, DB.owner", expected); } @Test - public void fromDBSelectGroupBy() { + public void fromDBGroupBy() { verify("from DB groupby (DB.owner)", "g.V().has('__typeName', 'DB').group().by('DB.owner').limit(25).toList()"); } @Test public void whereClauseTextContains() { - String expected = "g.V().has('__typeName', 'DB').has('DB.name', eq(\"Reporting\")).valueMap('DB.name','DB.owner').limit(25).toList()"; - verify("from DB where name = \"Reporting\" select name, owner", expected); + String exMain = "g.V().has('__typeName', 'DB').has('DB.name', eq(\"Reporting\")).limit(25).toList()"; + String exSel = "def f(r){ return [['name','owner']].plus(r.collect({[it.value('DB.name'),it.value('DB.owner')]})).unique(); }"; + verify("from DB where name = \"Reporting\" select name, owner", getExpected(exSel, exMain)); + verify("from DB where (name = \"Reporting\") select name, owner", getExpected(exSel, exMain)); verify("Table where Asset.name like \"Tab*\"", "g.V().has('__typeName', 'Table').has('Table.name', org.janusgraph.core.attribute.Text.textRegex(\"Tab.*\")).limit(25).toList()"); - verify("from DB where (name = \"Reporting\") select name, owner", expected); verify("from Table where (db.name = \"Reporting\")", - "g.V().has('__typeName', 'Table').out('__DB.Table').has('DB.name', eq(\"Reporting\")).in('__DB.Table').limit(25).toList()"); + "g.V().has('__typeName', 'Table').out('__Table.db').has('DB.name', eq(\"Reporting\")).in('__Table.db').limit(25).toList()"); } @Test public void whereClauseWithAsTextContains() { - verify("Table as t where t.name = \"testtable_1\" select t.name, t.owner)", - "g.V().has('__typeName', 'Table').as('t').has('Table.name', eq(\"testtable_1\")).valueMap('Table.name','Table.owner').limit(25).toList()"); + String exSel = "def f(r){ return [['t.name','t.owner']].plus(r.collect({[it.value('Table.name'),it.value('Table.owner')]})).unique(); }"; + String exMain = "g.V().has('__typeName', 'Table').as('t').has('Table.name', eq(\"testtable_1\")).limit(25).toList()"; + verify("Table as t where t.name = \"testtable_1\" select t.name, t.owner)", getExpected(exSel, exMain)); } @Test public void whereClauseWithDateCompare() { - verify("Table as t where t.createdTime = \"2017-12-12T02:35:58.440Z\" select t.name, t.owner)", - "g.V().has('__typeName', 'Table').as('t').has('Table.createdTime', eq('1513046158440')).valueMap('Table.name','Table.owner').limit(25).toList()"); + String exSel = "def f(r){ return [['t.name','t.owner']].plus(r.collect({[it.value('Table.name'),it.value('Table.owner')]})).unique(); }"; + String exMain = "g.V().has('__typeName', 'Table').as('t').has('Table.createdTime', eq('1513046158440')).limit(25).toList()"; + verify("Table as t where t.createdTime = \"2017-12-12T02:35:58.440Z\" select t.name, t.owner)", getExpected(exSel, exMain)); } @Test public void multipleWhereClauses() { - verify("Table where name=\"sales_fact\", columns as c select c.owner, c.name, c.dataType", - "g.V().has('__typeName', 'Table').has('Table.name', eq(\"sales_fact\")).out('__Table.columns').as('c').valueMap('Column.owner','Column.name','Column.dataType').limit(25).toList()"); + String exSel = "def f(r){ return [['c.owner','c.name','c.dataType']].plus(r.collect({[it.value('Column.owner'),it.value('Column.name'),it.value('Column.dataType')]})).unique(); }"; + String exMain = "g.V().has('__typeName', 'Table').has('Table.name', eq(\"sales_fact\")).out('__Table.columns').as('c').limit(25).toList()"; + verify("Table where name=\"sales_fact\", columns as c select c.owner, c.name, c.dataType", getExpected(exSel, exMain)); + ; } @Test public void subType() { - verify("Asset select name, owner", - "g.V().has('__typeName', within('Asset','Table')).valueMap('Asset.name','Asset.owner').limit(25).toList()"); + String exMain = "g.V().has('__typeName', within('Asset','Table')).limit(25).toList()"; + String exSel = "def f(r){ return [['name','owner']].plus(r.collect({[it.value('Asset.name'),it.value('Asset.owner')]})).unique(); }"; + + verify("Asset select name, owner", getExpected(exSel, exMain)); } @Test @@ -236,7 +253,7 @@ public class QueryProcessorTest { verify("hive_db where hive_db.name='Reporting' and hive_db.createTime < '2017-12-12T02:35:58.440Z'", "g.V().has('__typeName', 'hive_db').and(__.has('hive_db.name', eq('Reporting')),__.has('hive_db.createTime', lt('1513046158440'))).limit(25).toList()"); verify("Table where db.name='Sales' and db.clusterName='cl1'", - "g.V().has('__typeName', 'Table').and(__.out('__DB.Table').has('DB.name', eq('Sales')).in('__DB.Table'),__.out('__DB.Table').has('DB.clusterName', eq('cl1')).in('__DB.Table')).limit(25).toList()"); + "g.V().has('__typeName', 'Table').and(__.out('__Table.db').has('DB.name', eq('Sales')).in('__Table.db'),__.out('__Table.db').has('DB.clusterName', eq('cl1')).in('__Table.db')).limit(25).toList()"); } private void verify(String dsl, String expectedGremlin) { @@ -245,6 +262,10 @@ public class QueryProcessorTest { assertEquals(actualGremlin, expectedGremlin); } + private String getExpected(String select, String main) { + return String.format("%s; f(%s)", select, main); + } + private AtlasDSLParser.QueryContext getParsedQuery(String query) { AtlasDSLParser.QueryContext queryContext = null; InputStream stream = new ByteArrayInputStream(query.getBytes()); @@ -276,8 +297,9 @@ public class QueryProcessorTest { qv.visit(queryContext); queryProcessor.close(); - assertTrue(StringUtils.isNotEmpty(queryProcessor.getText())); - return queryProcessor.getText(); + String s = queryProcessor.getText(); + assertTrue(StringUtils.isNotEmpty(s)); + return s; } private static class TestLookup implements org.apache.atlas.query.Lookup { @@ -324,6 +346,8 @@ public class QueryProcessorTest { public String getRelationshipEdgeLabel(QueryProcessor.Context context, String attributeName) { if (attributeName.equalsIgnoreCase("columns")) return "__Table.columns"; + if (attributeName.equalsIgnoreCase("db")) + return "__Table.db"; else return "__DB.Table"; }