Commit f2255da1 by Sarath Subramanian

ATLAS-1807 : Enhance DSL query to support like operator for wildcard search

parent cf64a03e
...@@ -40,6 +40,7 @@ import org.apache.atlas.groovy.LogicalExpression.LogicalOperator; ...@@ -40,6 +40,7 @@ import org.apache.atlas.groovy.LogicalExpression.LogicalOperator;
import org.apache.atlas.groovy.RangeExpression; import org.apache.atlas.groovy.RangeExpression;
import org.apache.atlas.groovy.TernaryOperatorExpression; import org.apache.atlas.groovy.TernaryOperatorExpression;
import org.apache.atlas.groovy.TraversalStepType; import org.apache.atlas.groovy.TraversalStepType;
import org.apache.atlas.query.Expressions;
import org.apache.atlas.query.GraphPersistenceStrategies; import org.apache.atlas.query.GraphPersistenceStrategies;
import org.apache.atlas.query.TypeUtils.FieldInfo; import org.apache.atlas.query.TypeUtils.FieldInfo;
import org.apache.atlas.typesystem.types.IDataType; import org.apache.atlas.typesystem.types.IDataType;
...@@ -147,6 +148,37 @@ public class Gremlin2ExpressionFactory extends GremlinExpressionFactory { ...@@ -147,6 +148,37 @@ public class Gremlin2ExpressionFactory extends GremlinExpressionFactory {
return new FunctionCallExpression(TraversalStepType.FILTER, parent, HAS_METHOD, propertyNameExpr, op, requiredValue); return new FunctionCallExpression(TraversalStepType.FILTER, parent, HAS_METHOD, propertyNameExpr, op, requiredValue);
} }
@Override
public GroovyExpression generateLikeExpressionUsingFilter(GroovyExpression parent, String propertyName, GroovyExpression propertyValue) throws AtlasException {
GroovyExpression itExpr = getItVariable();
GroovyExpression nameExpr = new FieldExpression(itExpr, propertyName);
GroovyExpression matchesExpr = new FunctionCallExpression(nameExpr, MATCHES, escapePropertyValue(propertyValue));
GroovyExpression closureExpr = new ClosureExpression(matchesExpr);
GroovyExpression filterExpr = new FunctionCallExpression(parent, FILTER_METHOD, closureExpr);
return filterExpr;
}
private GroovyExpression escapePropertyValue(GroovyExpression propertyValue) {
GroovyExpression ret = propertyValue;
if (propertyValue instanceof LiteralExpression) {
LiteralExpression exp = (LiteralExpression) propertyValue;
if (exp != null && exp.getValue() instanceof String) {
String stringValue = (String) exp.getValue();
// replace '*' with ".*", replace '?' with '.'
stringValue = stringValue.replaceAll("\\*", ".*")
.replaceAll("\\?", ".");
ret = new LiteralExpression(stringValue);
}
}
return ret;
}
private GroovyExpression gremlin2CompOp(String op) throws AtlasException { private GroovyExpression gremlin2CompOp(String op) throws AtlasException {
GroovyExpression tExpr = new IdentifierExpression("T"); GroovyExpression tExpr = new IdentifierExpression("T");
......
...@@ -29,6 +29,7 @@ import org.apache.atlas.groovy.ClosureExpression; ...@@ -29,6 +29,7 @@ import org.apache.atlas.groovy.ClosureExpression;
import org.apache.atlas.groovy.ComparisonExpression; import org.apache.atlas.groovy.ComparisonExpression;
import org.apache.atlas.groovy.ComparisonExpression.ComparisonOperator; import org.apache.atlas.groovy.ComparisonExpression.ComparisonOperator;
import org.apache.atlas.groovy.ComparisonOperatorExpression; import org.apache.atlas.groovy.ComparisonOperatorExpression;
import org.apache.atlas.groovy.FieldExpression;
import org.apache.atlas.groovy.FunctionCallExpression; import org.apache.atlas.groovy.FunctionCallExpression;
import org.apache.atlas.groovy.GroovyExpression; import org.apache.atlas.groovy.GroovyExpression;
import org.apache.atlas.groovy.IdentifierExpression; import org.apache.atlas.groovy.IdentifierExpression;
...@@ -244,6 +245,37 @@ public class Gremlin3ExpressionFactory extends GremlinExpressionFactory { ...@@ -244,6 +245,37 @@ public class Gremlin3ExpressionFactory extends GremlinExpressionFactory {
} }
@Override @Override
public GroovyExpression generateLikeExpressionUsingFilter(GroovyExpression parent, String propertyName, GroovyExpression propertyValue) throws AtlasException {
GroovyExpression itExpr = getItVariable();
GroovyExpression nameExpr = new FieldExpression(itExpr, propertyName);
GroovyExpression matchesExpr = new FunctionCallExpression(nameExpr, MATCHES, escapePropertyValue(propertyValue));
GroovyExpression closureExpr = new ClosureExpression(matchesExpr);
GroovyExpression filterExpr = new FunctionCallExpression(parent, FILTER_METHOD, closureExpr);
return filterExpr;
}
private GroovyExpression escapePropertyValue(GroovyExpression propertyValue) {
GroovyExpression ret = propertyValue;
if (propertyValue instanceof LiteralExpression) {
LiteralExpression exp = (LiteralExpression) propertyValue;
if (exp != null && exp.getValue() instanceof String) {
String stringValue = (String) exp.getValue();
// replace '*' with ".*", replace '?' with '.'
stringValue = stringValue.replaceAll("\\*", ".*")
.replaceAll("\\?", ".");
ret = new LiteralExpression(stringValue);
}
}
return ret;
}
@Override
protected GroovyExpression initialExpression(GroovyExpression varExpr, GraphPersistenceStrategies s) { protected GroovyExpression initialExpression(GroovyExpression varExpr, GraphPersistenceStrategies s) {
// this bit of groovy magic converts the set of vertices in varName into // this bit of groovy magic converts the set of vertices in varName into
......
...@@ -76,6 +76,7 @@ public abstract class GremlinExpressionFactory { ...@@ -76,6 +76,7 @@ public abstract class GremlinExpressionFactory {
protected static final String SELECT_METHOD = "select"; protected static final String SELECT_METHOD = "select";
protected static final String ORDER_METHOD = "order"; protected static final String ORDER_METHOD = "order";
protected static final String FILL_METHOD = "fill"; protected static final String FILL_METHOD = "fill";
protected static final String MATCHES = "matches";
public static final GremlinExpressionFactory INSTANCE = AtlasGraphProvider.getGraphInstance() public static final GremlinExpressionFactory INSTANCE = AtlasGraphProvider.getGraphInstance()
.getSupportedGremlinVersion() == GremlinVersion.THREE ? new Gremlin3ExpressionFactory() .getSupportedGremlinVersion() == GremlinVersion.THREE ? new Gremlin3ExpressionFactory()
...@@ -182,6 +183,9 @@ public abstract class GremlinExpressionFactory { ...@@ -182,6 +183,9 @@ public abstract class GremlinExpressionFactory {
public abstract GroovyExpression generateHasExpression(GraphPersistenceStrategies s, GroovyExpression parent, public abstract GroovyExpression generateHasExpression(GraphPersistenceStrategies s, GroovyExpression parent,
String propertyName, String symbol, GroovyExpression requiredValue, FieldInfo fInfo) throws AtlasException; String propertyName, String symbol, GroovyExpression requiredValue, FieldInfo fInfo) throws AtlasException;
public abstract GroovyExpression generateLikeExpressionUsingFilter(GroovyExpression parent, String propertyName,
GroovyExpression propertyValue) throws AtlasException;
/** /**
* Generates a range expression * Generates a range expression
* *
......
...@@ -437,6 +437,10 @@ class GremlinTranslator(expr: Expression, ...@@ -437,6 +437,10 @@ class GremlinTranslator(expr: Expression,
genQuery(null, l, inClosure); genQuery(null, l, inClosure);
} }
if (symb == "like") {
return GremlinExpressionFactory.INSTANCE.generateLikeExpressionUsingFilter(childExpr, qualifiedPropertyName, persistentExprValue);
}
return GremlinExpressionFactory.INSTANCE.generateHasExpression(gPersistenceBehavior, childExpr, qualifiedPropertyName, c.symbol, persistentExprValue, fInfo); return GremlinExpressionFactory.INSTANCE.generateHasExpression(gPersistenceBehavior, childExpr, qualifiedPropertyName, c.symbol, persistentExprValue, fInfo);
} }
case fil@FilterExpression(child, condExpr) => { case fil@FilterExpression(child, condExpr) => {
......
...@@ -75,6 +75,7 @@ trait QueryKeywords { ...@@ -75,6 +75,7 @@ trait QueryKeywords {
protected val SUM = Keyword("sum") protected val SUM = Keyword("sum")
protected val BY = Keyword("by") protected val BY = Keyword("by")
protected val ORDER = Keyword("order") protected val ORDER = Keyword("order")
protected val LIKE = Keyword("like")
} }
trait ExpressionUtils { trait ExpressionUtils {
...@@ -312,7 +313,7 @@ object QueryParser extends StandardTokenParsers with QueryKeywords with Expressi ...@@ -312,7 +313,7 @@ object QueryParser extends StandardTokenParsers with QueryKeywords with Expressi
def exprRight = (AND | OR) ~ compE ^^ { case op ~ c => (op, c)} def exprRight = (AND | OR) ~ compE ^^ { case op ~ c => (op, c)}
def compE = def compE =
arithE ~ (LT | LTE | EQ | NEQ | GT | GTE) ~ arithE ^^ { case l ~ op ~ r => l.compareOp(op)(r)} | arithE ~ (LT | LTE | EQ | NEQ | GT | GTE | LIKE) ~ arithE ^^ { case l ~ op ~ r => l.compareOp(op)(r)} |
arithE ~ (ISA | IS) ~ ident ^^ { case l ~ i ~ t => l.isTrait(t)} | arithE ~ (ISA | IS) ~ ident ^^ { case l ~ i ~ t => l.isTrait(t)} |
arithE ~ HAS ~ ident ^^ { case l ~ i ~ f => l.hasField(f)} | arithE ~ HAS ~ ident ^^ { case l ~ i ~ f => l.hasField(f)} |
arithE | countClause | maxClause | minClause | sumClause arithE | countClause | maxClause | minClause | sumClause
......
...@@ -256,6 +256,22 @@ public class GraphBackedDiscoveryServiceTest extends BaseRepositoryTest { ...@@ -256,6 +256,22 @@ public class GraphBackedDiscoveryServiceTest extends BaseRepositoryTest {
assertEquals(entityState, Id.EntityState.ACTIVE.name()); assertEquals(entityState, Id.EntityState.ACTIVE.name());
} }
@DataProvider(name = "dslLikeQueriesProvider")
private Object[][] createDslLikeQueries() {
return new Object[][]{
{"hive_table where name like \"sa?es*\"", 3},
{"hive_db where name like \"R*\"", 1},
{"hive_db where hive_db.name like \"R???rt?*\" or hive_db.name like \"S?l?s\" or hive_db.name like\"Log*\"", 3},
{"hive_db where hive_db.name like \"R???rt?*\" and hive_db.name like \"S?l?s\" and hive_db.name like\"Log*\"", 0},
{"hive_table where name like 'sales*', db where name like 'Sa?es'", 1},
};
}
@Test(dataProvider = "dslLikeQueriesProvider")
public void testDslSearchUsingLikeOperator(String dslQuery, Integer expectedNumRows) throws Exception {
runQuery(dslQuery, expectedNumRows, 50, 0);
}
@Test(expectedExceptions = Throwable.class) @Test(expectedExceptions = Throwable.class)
public void testSearchByDSLBadQuery() throws Exception { public void testSearchByDSLBadQuery() throws Exception {
String dslQuery = "from blah"; String dslQuery = "from blah";
......
...@@ -54,7 +54,7 @@ public class EntityDiscoveryJerseyResourceIT extends BaseResourceIT { ...@@ -54,7 +54,7 @@ public class EntityDiscoveryJerseyResourceIT extends BaseResourceIT {
@BeforeClass @BeforeClass
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
dbName = "db" + randomString(); dbName = "database" + randomString();
createTypes(); createTypes();
createInstance(createHiveDBInstanceBuiltIn(dbName)); createInstance(createHiveDBInstanceBuiltIn(dbName));
} }
...@@ -145,6 +145,22 @@ public class EntityDiscoveryJerseyResourceIT extends BaseResourceIT { ...@@ -145,6 +145,22 @@ public class EntityDiscoveryJerseyResourceIT extends BaseResourceIT {
} }
@Test @Test
public void testLikeSearchUsingDSL() throws Exception {
String dslQuery = DATABASE_TYPE_BUILTIN + " where " + QUALIFIED_NAME + " like \"da?a*\"";
AtlasSearchResult searchResult = atlasClientV2.dslSearch(dslQuery);
assertNotNull(searchResult);
List<AtlasEntityHeader> entities = searchResult.getEntities();
assertNotNull(entities);
assertEquals(entities.size(), 1);
AtlasEntityHeader dbEntity = entities.get(0);
assertEquals(dbEntity.getTypeName(), DATABASE_TYPE_BUILTIN);
assertEquals(dbEntity.getDisplayText(), dbName);
}
@Test
public void testSearchFullTextOnDSLFailure() throws Exception { public void testSearchFullTextOnDSLFailure() throws Exception {
String query = "*"; String query = "*";
AtlasSearchResult searchResult = atlasClientV2.fullTextSearch(query); AtlasSearchResult searchResult = atlasClientV2.fullTextSearch(query);
......
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