opencypher / cypher-for-gremlin

Cypher for Gremlin adds Cypher support to any Gremlin graph database.
Apache License 2.0
359 stars 48 forks source link

Error in Translation #294

Closed argium closed 5 years ago

argium commented 5 years ago

First of all - thank you for such a useful library. My team is more familiar with Cypher but I've built my project using a CosmosDB backend and this allows them to use their existing knowledge.

So, I have a simple application for converting a CYPHER query to Gremlin.

object Main extends App {
    val input = "MATCH (m:Machine)-[e:Has {storeName: \"CA\"}]->(c:Certificate) \nWHERE m.name =~ \"*.domain\"\nRETURN m.name,e.storeName,c.name,c.thumbprint"
    val facade = new TranslationFacade()
    println(facade.toGremlinGroovy(input))
}

this outputs:

g.V().as('m').hasLabel('Machine').has('name', cypherRegex('*.domain')).outE('Has').as('e').has('storeName', eq('CA')).inV().as('c').hasLabel('Certificate').select('m', 'e', 'c').project('m.name', 'e.storeName', 'c.name', 'c.thumbprint').by(__.select('m').choose(__.values('name'), __.values('name'), __.constant('  cypher.null'))).by(__.select('e').choose(neq('  cypher.null'), __.choose(__.values('storeName'), __.values('storeName'), __.constant('  cypher.null')))).by(__.select('c').choose(neq('  cypher.null'), __.choose(__.values('name'), __.values('name'), __.constant('  cypher.null')))).by(__.select('c').choose(neq('  cypher.null'), __.choose(__.values('thumbprint'), __.values('thumbprint'), __.constant('  cypher.null'))))

When I run that query on CosmosDB I get this error: Gremlin Query Compilation Error: Unable to resolve symbol 'cypherRegex' in the current context

Is this a bug or unsupported?

dwitry commented 5 years ago

Hello @rifuller,

thank you, we are happy to know that Cypher for Gremlin is useful for your team.

Because native Gremlin has no predicate for Regex match, cypherRegex is custom predicate that extends Gremlin functionality. Unfortunately, custom functions and predicates are not supported in Cosmos DB.

This is actually a shortcoming that error about cypherRegex is not shown on the translation stage, as it happens with other custom predicates. I will improve query validation.

Maybe for your requirement could be addressed with text predicates (startingWith, containing...)? According to Cosmos DB Team statement it was recently implemented, however, I was not able to get it working:

gremlin> g.V().has('name', TextP.startingWith('ma'))
Gremlin Query Compilation Error: Unable to resolve symbol 'TextP' in the current context. @ line 1, column 19.

Please notify me if it works in your case, I might be missing something.

By the way, to adapt query to other Cosmos DB specifics, it is recommended to use cosmosDb flavor:

  val input =
    "MATCH (m:Machine)-[e:Has {storeName: \"CA\"}]->(c:Certificate) \nWHERE m.name =~ \"*.domain\"\nRETURN m.name,e.storeName,c.name,c.thumbprint"
  val ast = CypherAst.parse(input)
  val translator = Translator.builder.gremlinGroovy.build(TranslatorFlavor.cosmosDb)
  println(ast.buildTranslation(translator))

Please let me know if you have any other questions, or close this issue.

argium commented 5 years ago

There are only a couple of pieces of that translated query at least that are custom predicates: cypherRegex and cypher.null. I replaced the first with TextP.endingWith (and removed the '*') and replaced the latter with an empty string and then it does run! Is replacing cypher.null with that the right option here?

The final query:

g.V().as('m').hasLabel('Machine').has('name', TextP.endingWith('.domain')).outE('Has').as('e').has('storeName', eq('CA')).inV().as('c').hasLabel('Certificate').select('m', 'e', 'c').project('m.name', 'e.storeName', 'c.name', 'c.thumbprint').by(__.select('m').choose(__.values('name'), __.values('name'), __.constant(''))).by(__.select('e').choose(neq(''), __.choose(__.values('storeName'), __.values('storeName'), __.constant('')))).by(__.select('c').choose(neq(''), __.choose(__.values('name'), __.values('name'), __.constant('')))).by(__.select('c').choose(neq(''), __.choose(__.values('thumbprint'), __.values('thumbprint'), __.constant(''))))

c.name is an optional field so I just need to confirm that records are returned where that is empty/null still.

I'll update my tool with those tweaks, and set it to use the CosmosDB flavor. I really appreciate your prompt response.

dwitry commented 5 years ago

" cypher.null" is just a String representing null in Gremlin. It does not require custom plugin, so my recommendation would be to leave it as it is.

Thank you for mentioning that TextP.* is working in your case. I've created a new Cosmos DB instance (TextP.* was not working for previously created instances for some reason). Can confirm that Cypher STARTS WITH / ENDS WITH / CONTAINS are now working in Cosmos DB, so it should be possible to use your tool without additional tweaks.

Edit: by additional tweaks I've meant workarounds. It is still recommended to use the CosmosDB flavor.

dwitry commented 5 years ago

@rifuller, closing issue after a week of inactivity. Don't hesitate to open a new issue if you have any more questions.