From 485428d5419577dcc465dbfa1e4600ecaa1eeaed Mon Sep 17 00:00:00 2001 From: Lakshya A Agrawal Date: Wed, 14 Apr 2021 13:42:35 +0530 Subject: [PATCH] Add conversion for opProject and opBGP. Tests working --- src/main/java/com/KRacR/s2c/CreateCypher.java | 25 +++- src/main/java/com/KRacR/s2c/RDFtoCypher.java | 4 +- .../s2c/SparqlAlgebraToCypherVisitor.java | 129 +++++++++++++++++- .../com/KRacR/s2c/SparqlToCypherTest.java | 7 + 4 files changed, 152 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/KRacR/s2c/CreateCypher.java b/src/main/java/com/KRacR/s2c/CreateCypher.java index 99ed83b..f830c25 100644 --- a/src/main/java/com/KRacR/s2c/CreateCypher.java +++ b/src/main/java/com/KRacR/s2c/CreateCypher.java @@ -9,16 +9,25 @@ // TODO: Check if there exists a unique id for every node in Neo4j class CreateCypher implements RDFVisitor { + String operation; String label; public CreateCypher() { this.label = ""; + this.operation = ""; + } + + public CreateCypher(String operation) { + this.label = ""; + this.operation = operation + " "; } @Override public String visitBlank(Resource r, AnonId id) { return String.format( - "MERGE (%s {uri:\"\", id:\"%s\"})", + "%s(%s {uri:\"\", id:\"%s\", stringrep:\"%s\"})", + this.operation, this.label, + id.getBlankNodeId().toString(), id.getBlankNodeId().toString() ); } @@ -26,8 +35,10 @@ public String visitBlank(Resource r, AnonId id) { @Override public String visitURI(Resource r, String uri) { return String.format( - "MERGE (%s {uri:\"%s\"})", + "%s(%s {uri:\"%s\", stringrep:\"%s\"})", + this.operation, this.label, + uri, uri ); } @@ -38,17 +49,21 @@ public String visitLiteral(Literal l) { (l.getDatatype() instanceof org.apache.jena.datatypes.xsd.impl.RDFLangString)? ( String.format( - "MERGE (%s {uri:\"\", typeiri:\"%s\", lexform:\"%s\", langtag:\"%s\"})", + "%s(%s {uri:\"\", typeiri:\"%s\", lexform:\"%s\", langtag:\"%s\", stringrep:\"%s\"})", + this.operation, this.label, l.getDatatypeURI(), l.getLexicalForm(), - l.getLanguage() + l.getLanguage(), + l.getLexicalForm() + "@" + l.getLanguage() ) ):( String.format( - "MERGE (%s {uri:\"\", typeiri:\"%s\", lexform:\"%s\"})", + "%s(%s {uri:\"\", typeiri:\"%s\", lexform:\"%s\", stringrep:\"%s\"})", + this.operation, this.label, l.getDatatypeURI(), + l.getLexicalForm(), l.getLexicalForm() ) ); diff --git a/src/main/java/com/KRacR/s2c/RDFtoCypher.java b/src/main/java/com/KRacR/s2c/RDFtoCypher.java index 3c25dd7..1ada517 100644 --- a/src/main/java/com/KRacR/s2c/RDFtoCypher.java +++ b/src/main/java/com/KRacR/s2c/RDFtoCypher.java @@ -17,7 +17,7 @@ public static List RDFtoCypherDirect(Model model) { while (it.hasNext()) { Statement stmt = it.next(); String cypher_q = ""; - CreateCypher visitor = new CreateCypher(); + CreateCypher visitor = new CreateCypher("MERGE"); visitor.set_label("s"); cypher_q = cypher_q.concat((String) stmt.getSubject().visitWith(visitor)); cypher_q = cypher_q.concat("\n"); @@ -25,7 +25,7 @@ public static List RDFtoCypherDirect(Model model) { cypher_q = cypher_q.concat((String) stmt.getObject().visitWith(visitor)); cypher_q = cypher_q.concat("\n"); cypher_q = cypher_q.concat( - String.format("MERGE (s)-[:Edge {uri:\"%s\"}]->(o);", stmt.getPredicate().getURI()) + String.format("MERGE (s)-[:Edge {uri:\"%s\", stringrep:\"%s\"}]->(o);", stmt.getPredicate().getURI(), stmt.getPredicate().getURI()) ); cypher_qs.add(cypher_q); } diff --git a/src/main/java/com/KRacR/s2c/SparqlAlgebraToCypherVisitor.java b/src/main/java/com/KRacR/s2c/SparqlAlgebraToCypherVisitor.java index f1c2587..a25fb34 100644 --- a/src/main/java/com/KRacR/s2c/SparqlAlgebraToCypherVisitor.java +++ b/src/main/java/com/KRacR/s2c/SparqlAlgebraToCypherVisitor.java @@ -1,6 +1,20 @@ package com.KRacR.s2c; +import java.util.HashMap; +import java.util.Map; + +import org.apache.jena.graph.BlankNodeId; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeVisitor; +import org.apache.jena.graph.Node_ANY; +import org.apache.jena.graph.Node_Blank; +import org.apache.jena.graph.Node_Literal; +import org.apache.jena.graph.Node_URI; +import org.apache.jena.graph.Node_Variable; +import org.apache.jena.graph.Triple; +import org.apache.jena.graph.impl.LiteralLabel; import org.apache.jena.sparql.algebra.OpVisitor; +import org.apache.jena.sparql.algebra.Transform; import org.apache.jena.sparql.algebra.op.OpAssign; import org.apache.jena.sparql.algebra.op.OpBGP; import org.apache.jena.sparql.algebra.op.OpConditional; @@ -35,18 +49,114 @@ import org.apache.jena.sparql.algebra.op.OpTopN; import org.apache.jena.sparql.algebra.op.OpTriple; import org.apache.jena.sparql.algebra.op.OpUnion; +import org.apache.jena.sparql.core.Var; +import org.apache.lucene.util.packed.PackedLongValues.Iterator; public class SparqlAlgebraToCypherVisitor implements OpVisitor { - String cypher; + private String cypher; + private Map Sparql_to_cypher_variable_map; + private Map Cypher_to_sparql_variable_map; + private int blank_node_num = 0; + private Map Sparql_blank_node_to_var_map; + + public SparqlAlgebraToCypherVisitor() { + cypher = new String(); + Sparql_blank_node_to_var_map = new HashMap(); + Sparql_to_cypher_variable_map = new HashMap(); + Cypher_to_sparql_variable_map = new HashMap(); + } @Override public void visit(OpBGP opBGP) { // TODO Auto-generated method stub System.out.println("In opBGP\n" + opBGP.toString()); - } - - @Override - public void visit(OpQuadPattern quadPattern) { + java.util.Iterator it = opBGP.getPattern().iterator(); + while(it.hasNext()) { + Triple t = it.next(); + CreateCypher visitor = new CreateCypher(); + NodeVisitor cypherNodeMatcher = new NodeVisitor() { + + @Override + public String visitAny(Node_ANY it) { + // TODO Auto-generated method stub + return null; + } + + @Override + public String visitBlank(Node_Blank it, BlankNodeId id) { + // TODO Auto-generated method stub + return create_or_get_variable(it); + } + + @Override + public String visitLiteral(Node_Literal it, LiteralLabel lit) { + // TODO Auto-generated method stub + return + (lit.language().equals(""))? + ( + String.format( + "{uri:\"\", typeiri:\"%s\", lexform:\"%s\"}", + lit.getDatatypeURI(), + lit.getLexicalForm() + ) + ):( + String.format( + "{uri:\"\", typeiri:\"%s\", lexform:\"%s\", langtag:\"%s\"}", + lit.getDatatypeURI(), + lit.getLexicalForm(), + lit.language() + ) + ); + } + + @Override + public String visitURI(Node_URI it, String uri) { + // TODO Auto-generated method stub + return String.format("{uri:\"%s\"}", uri); + } + + @Override + public String visitVariable(Node_Variable it, String name) { + // TODO Auto-generated method stub + return create_or_get_variable(Var.alloc(it)); + } + + }; + + cypher = cypher + "MATCH (" + + t.getMatchSubject().visitWith(cypherNodeMatcher) + + ")-[" + + t.getMatchPredicate().visitWith(cypherNodeMatcher) + + "]->(" + + t.getMatchObject().visitWith(cypherNodeMatcher) + + ")\n"; + } + } + + protected String create_or_get_variable(Node_Blank it) { + // TODO Auto-generated method stub + String var_name = "blankvar" + (blank_node_num++); + Var var = Var.alloc(var_name + it.getBlankNodeId().toString()); + Sparql_blank_node_to_var_map.put(it, var); + String created_var = create_or_get_variable(var); + return created_var; + } + + protected String create_or_get_variable(Var allocated_var) { + // TODO Auto-generated method stub + // TODO Account for variable names to ensure that there is no collision, and the created variable is valid in Cypher conventions + // https://neo4j.com/docs/cypher-manual/current/syntax/naming/ + // https://www.w3.org/TR/sparql11-query/#rVARNAME + if(Sparql_to_cypher_variable_map.containsKey(allocated_var)) return Sparql_to_cypher_variable_map.get(allocated_var); + else { + Sparql_to_cypher_variable_map.put(allocated_var, allocated_var.getName()); + Cypher_to_sparql_variable_map.put(allocated_var.getName(), allocated_var); + return Sparql_to_cypher_variable_map.get(allocated_var); + } + } + + @Override + public void visit(OpQuadPattern quadPattern){ // TODO Auto-generated method stub System.out.println("In quadPattern\n" + quadPattern.toString()); } @@ -211,6 +321,13 @@ public void visit(OpOrder opOrder) { public void visit(OpProject opProject) { // TODO Auto-generated method stub System.out.println("In opProject\n" + opProject.toString()); + opProject.getSubOp().visit(this); + cypher = cypher.concat("RETURN "); + for(Var var: opProject.getVars()) { + cypher = cypher.concat(Sparql_to_cypher_variable_map.get(var) + ".stringrep AS " + Sparql_to_cypher_variable_map.get(var) + ", "); + } + cypher = cypher.substring(0, cypher.length() - 2); + cypher = cypher.concat("\n"); } @Override @@ -245,7 +362,7 @@ public void visit(OpTopN opTop) { public String getCypher() { // TODO Auto-generated method stub - return "match (n)-[e:Edge]->(r {uri:\"http://localhost/persons/Paul_Erdoes\"}) return n.uri as subject, e.uri as predicate"; + return cypher; //"match (n)-[e:Edge]->(r {uri:\"http://localhost/persons/Paul_Erdoes\"}) return n.uri as subject, e.uri as predicate"; } } diff --git a/src/test/java/com/KRacR/s2c/SparqlToCypherTest.java b/src/test/java/com/KRacR/s2c/SparqlToCypherTest.java index 091f23f..8d6a07b 100644 --- a/src/test/java/com/KRacR/s2c/SparqlToCypherTest.java +++ b/src/test/java/com/KRacR/s2c/SparqlToCypherTest.java @@ -92,6 +92,9 @@ public boolean accept(File file) { } cypher_result.add(res); } + }catch(Exception e) { + System.out.println("Running the returned Cypher query FAILED with exception:"); + System.out.println(e.getMessage()); } // Execute the sparql query on the database @@ -114,6 +117,10 @@ public boolean accept(File file) { System.out.println("\nTEST PASSED\n"); }else { System.out.println("\nTEST FAILED\n"); + System.out.println("-------Sparql-------"); + System.out.println(sparql_result); + System.out.println("-------Cypher-------"); + System.out.println(cypher_result); } // TODO: Account for ORDER BY queries