+ SparqlToSparqlToSql.toView
authorEric Prud'hommeaux <eric@w3.org>
Sun, 28 Feb 2010 11:40:20 -0500
changeset 184 b64f1d9ee47e
parent 183 e733cd719f99
child 185 c95ae0c8d28b
+ SparqlToSparqlToSql.toView
src/main/scala/SparqlToSparqlToSql.scala
src/test/scala/SparqlToSparqlToSqlTest.scala
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/SparqlToSparqlToSql.scala	Sun Feb 28 11:40:20 2010 -0500
@@ -0,0 +1,56 @@
+/* SparqlToSparqlToSql: convert SPARQL queries to sound SQL queries.
+ * $Id$
+ */
+
+package w3c.sw.sparql2sparql2sql
+
+import w3c.sw.sql
+import w3c.sw.sparql
+import w3c.sw.sparql2sparql
+import w3c.sw.sparql2sql
+
+object SparqlToSparqlToSql {
+
+  def toView (rules:List[sparql.Construct], schema:sql.DatabaseDesc, stemUri:sparql2sql.StemURI) = {
+
+    /* For each rule... */
+    val disjoints = rules.foldLeft(List[sql.Select]())((allgps, rule) => {
+
+      /* ... turn each triple pattern in the rule heads into a SQL pattern. */
+      rule.head.triplepatterns.foldLeft(allgps)((gps, triple) => {
+
+	/* Substituted SPARQL ?S and ?O into the triple pattern. */
+	val s = sparql.Var("S");
+	val o = sparql.Var("O")
+	val select = sparql.Select(
+	  sparql.SparqlAttributeList(List(s, o)),
+	  sparql.TriplesBlock(
+	    List(sparql.TriplePattern(sparql.TermVar(s), triple.p, sparql.TermVar(o)))))
+
+	/* Convert to equivalent SQL SELECT. */
+	val asStem = sparql2sparql.SparqlToSparql(select, List(rule))
+	val sql.Select(sql.AttributeList(attributes), tablelist, expression) = sparql2sql.SparqlToSql(schema, asStem, stemUri, false, true)
+
+	/* Add constant for predicate into selects. */
+	val p = triple.p match {
+	  case sparql.TermUri(u) => sql.NamedAttribute(sql.PrimaryExpressionTyped(sql.Datatype.STRING,sql.Name("<" + u.s + ">")),
+						       sql.AttrAlias(sql.Name("P")))
+	  case sparql.TermVar(v) => error("triplification algorithm doesn't handly variable predicate in " + triple)
+	  case sparql.TermLit(_) => error("illegal literal in predicate position of " + triple)
+	}
+
+	/* Add resulting SELECT to list of graph patterns. */
+	gps :+ sql.Select(sql.AttributeList(attributes + p), tablelist, expression)
+      })
+    })
+
+    /* Create a view from the disjoints.*/
+    val body:sql.SelectORUnion = disjoints.size match {
+      case 0 => error("not sure why you want a view with no rows...")
+      case 1 => disjoints.toList(0)
+      case _ => sql.Union(disjoints.toSet)
+    }
+    sql.View(sql.Relation(sql.Name("triples")), body)
+  }
+}
+
--- a/src/test/scala/SparqlToSparqlToSqlTest.scala	Sun Feb 28 11:38:44 2010 -0500
+++ b/src/test/scala/SparqlToSparqlToSqlTest.scala	Sun Feb 28 11:40:20 2010 -0500
@@ -10,6 +10,7 @@
 import w3c.sw.sparql2sparql.{SparqlToSparql}
 import w3c.sw.sql.{Sql,DatabaseDesc,Relation,RelationDesc,Attribute,Value,Datatype,ForeignKey,Name}
 import w3c.sw.sparql2sql.{SparqlToSql,StemURI}
+import w3c.sw.sparql2sparql2sql.SparqlToSparqlToSql.toView
 
 /* The SparqlToSparqlTest class transforms SPARQL queries to a relational data
  * structure and compares them to a structure parsed from SQL.
@@ -19,7 +20,7 @@
   val DDLParser = Sql()
 
   val dbDdl = """
-CREATE TABLE Employee (empid INT, PRIMARY KEY (empid), lastName STRING, birthday DATE, manager INT, FOREIGN KEY (manager) REFERENCES Employee(empid));
+CREATE TABLE Employee (empid INT, PRIMARY KEY (empid), firstName STRING, lastName STRING, birthday DATE, manager INT, FOREIGN KEY (manager) REFERENCES Employee(empid));
 CREATE TABLE Tasks (taskid INT, PRIMARY KEY (taskid), name STRING, lead INT, FOREIGN KEY (lead) REFERENCES Employee(empid));
 CREATE TABLE TaskAssignments (id INT PRIMARY KEY, PRIMARY KEY (id), task INT, FOREIGN KEY (task) REFERENCES Tasks(taskid), employee INT, FOREIGN KEY (employee) REFERENCES Employee(empid));
 """
@@ -65,6 +66,33 @@
 """
   }
 
+  test("foaf2HR as view") {
+    val sparqlParser = Sparql()
+    val hr2foaf = sparqlParser.parseAll(sparqlParser.construct, """
+PREFIX foaf : <http://xmlns.com/foaf/0.1/>
+PREFIX empP : <http://hr.example/DB/Employee#>
+CONSTRUCT { ?who foaf:first_name ?fname .
+            ?who foaf:last_name  ?lname }
+    WHERE { ?who empP:firstName  ?fname .
+            ?who empP:lastName   ?lname }
+""").get
+    val view = toView(List(hr2foaf), HR, StemURI("http://hr.example/DB/"))
+    val sqlParser = Sql()
+    val expected = sqlParser.parseAll(sqlParser.createview, """
+CREATE VIEW triples AS
+  SELECT CONCAT("'", R_S.firstName, "'^^<http://www.w3.org/2001/XMLSchema#string>") AS O, "<http://xmlns.com/foaf/0.1/first_name>" AS P, CONCAT("http://hr.example/DB/", "Employee", "/", "empid", ".", R_S.empid, "#record") AS S
+    FROM Employee AS R_S
+   WHERE (R_S.empid IS NOT NULL)
+     AND (R_S.firstName IS NOT NULL)
+UNION
+  SELECT CONCAT("'", R_S.lastName, "'^^<http://www.w3.org/2001/XMLSchema#string>") AS O, "<http://xmlns.com/foaf/0.1/last_name>" AS P, CONCAT("http://hr.example/DB/", "Employee", "/", "empid", ".", R_S.empid, "#record") AS S
+    FROM Employee AS R_S
+   WHERE (R_S.empid IS NOT NULL)
+     AND (R_S.lastName IS NOT NULL)
+""").get
+    assert(expected === view)
+  }
+
   /* ========== DiabeticPatient database tests ==========
    *
    */
@@ -77,28 +105,8 @@
 CREATE TABLE NDCcodes (ID INT PRIMARY KEY, NDC INT, ingredient INT);
 """
   val hosp1:DatabaseDesc = DDLParser.parseAll(DDLParser.ddl, hosp1Ddl).get
-
-  test("~/swobjects/tests/healthCare/lists-notBound/hl7.rq short") {
-    val sparqlParser = Sparql()
-    val hl7Query = sparqlParser.parseAll(sparqlParser.select, """
-PREFIX hl7: <http://www.hl7.org/v3ballot/xml/infrastructure/vocabulary/vocabulary#> 
-SELECT ?patient ?dob ?sex
-WHERE
-{
-  ?patient a hl7:Person .
-  ?patient hl7:entityName ?middleName .
-  ?patient hl7:livingSubjectBirthTime ?dob .
-  ?patient hl7:administrativeGenderCodePrintName ?sex .
-  ?patient hl7:substanceAdministration ?subs_admin .
-  ?subs_admin a hl7:SubstanceAdministration .
-  ?subs_admin hl7:consumable ?consumable .
-  ?consumable hl7:displayName ?takes .
-  ?consumable hl7:activeIngredient ?ingredient .
-  ?ingredient hl7:classCode 6809 .
-  ?subs_admin hl7:effectiveTime ?indic_span .
-}""").get
-
-    val db2hl7 = sparqlParser.parseAll(sparqlParser.construct, """
+  val ConstructParser = Sparql()
+  val db2hl7 = ConstructParser.parseAll(ConstructParser.construct, """
 PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
 PREFIX Person: <http://hospital.example/DB/Person#>
 PREFIX Sex_DE: <http://hospital.example/DB/Sex_DE#>
@@ -140,6 +148,27 @@
   ?indicCode  NDCcodes:NDC         ?indicNDC .
   ?indicCode  NDCcodes:ingredient  ?ingredCode }
 """).get
+
+  test("~/swobjects/tests/healthCare/lists-notBound/hl7.rq short") {
+    val sparqlParser = Sparql()
+    val hl7Query = sparqlParser.parseAll(sparqlParser.select, """
+PREFIX hl7: <http://www.hl7.org/v3ballot/xml/infrastructure/vocabulary/vocabulary#> 
+SELECT ?patient ?dob ?sex
+WHERE
+{
+  ?patient a hl7:Person .
+  ?patient hl7:entityName ?middleName .
+  ?patient hl7:livingSubjectBirthTime ?dob .
+  ?patient hl7:administrativeGenderCodePrintName ?sex .
+  ?patient hl7:substanceAdministration ?subs_admin .
+  ?subs_admin a hl7:SubstanceAdministration .
+  ?subs_admin hl7:consumable ?consumable .
+  ?consumable hl7:displayName ?takes .
+  ?consumable hl7:activeIngredient ?ingredient .
+  ?ingredient hl7:classCode 6809 .
+  ?subs_admin hl7:effectiveTime ?indic_span .
+}""").get
+
     val stemQuery = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX Person: <http://hospital.example/DB/Person#>
 PREFIX Sex_DE: <http://hospital.example/DB/Sex_DE#>
@@ -219,48 +248,6 @@
   }
 }""").get
 
-    val db2hl7 = sparqlParser.parseAll(sparqlParser.construct, """
-PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
-PREFIX Person: <http://hospital.example/DB/Person#>
-PREFIX Sex_DE: <http://hospital.example/DB/Sex_DE#>
-PREFIX Item_Medication: <http://hospital.example/DB/Item_Medication#>
-PREFIX Medication: <http://hospital.example/DB/Medication#>
-PREFIX Medication_DE: <http://hospital.example/DB/Medication_DE#>
-PREFIX NDCcodes: <http://hospital.example/DB/NDCcodes#>
-
-PREFIX hl7: <http://www.hl7.org/v3ballot/xml/infrastructure/vocabulary/vocabulary#>
-PREFIX spl: <http://www.hl7.org/v3ballot/xml/infrastructure/vocabulary/vocabulary#>
-
-CONSTRUCT {
-?person     a					  hl7:Person .
-?person     hl7:entityName			  ?middleName .
-?person     hl7:livingSubjectBirthTime		  ?dob .
-?person     hl7:administrativeGenderCodePrintName ?sex .
-?person     hl7:substanceAdministration		  ?subst .
-?subst	a	       hl7:SubstanceAdministration .
-?subst 	hl7:consumable ?cons .
-?cons	    hl7:displayName	 ?takes .
-?cons	    spl:activeIngredient ?ingred .
-?ingred		spl:classCode ?ingredCode .
-?subst	hl7:effectiveTime ?interval .
-?interval	    hl7:start ?indicDate 
-} WHERE {
-  ?person     Person:MiddleName		     ?middleName .
-  ?person     Person:DateOfBirth	     ?dob .
-  ?person     Person:SexDE		     ?sexEntry   .
-
-              ?sexEntry   Sex_DE:EntryName   ?sex .
-
-  ?indicItem  Item_Medication:PatientID	     ?person .
-  ?indicItem  Item_Medication:PerformedDTTM  ?indicDate .
-  ?indicItem  Item_Medication:EntryName	     ?takes .
-  ?indicMed   Medication:ItemID		     ?indicItem .
-  ?indicMed   Medication:DaysToTake	     ?indicDuration .
-  ?indicMed   Medication:MedDictDE	     ?indicDE .
-  ?indicDE    Medication_DE:NDC		     ?indicNDC .
-  ?indicCode  NDCcodes:NDC         ?indicNDC .
-  ?indicCode  NDCcodes:ingredient  ?ingredCode }
-""").get
     val stemQuery = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX Person: <http://hospital.example/DB/Person#>
 PREFIX Sex_DE: <http://hospital.example/DB/Sex_DE#>
@@ -339,4 +326,13 @@
 """
   }
 
+//   test("hospital as view") {
+//     val sparqlParser = Sparql()
+//     val view = toView(List(db2hl7), HR, StemURI("http://hospital.example/DB/"))
+//     val sqlParser = Sql()
+//     val expected = sqlParser.parseAll(sqlParser.createview, """
+// """).get
+//     assert(expected === view)
+//   }
+
 }