+ SPARQL < expressions
authorEric Prud'hommeaux <bertails@w3.org>
Tue, 15 Dec 2009 21:08:44 -0500
changeset 48 9191563eff96
parent 47 46a58e2d69a3
child 49 6663b4a69f56
+ SPARQL < expressions
src/main/scala/SPARQL.scala
src/test/scala/RDB2RDFTest.scala
src/test/scala/SQLTest.scala
src/test/scala/SparqlTest.scala
--- a/src/main/scala/SPARQL.scala	Tue Dec 15 16:46:38 2009 -0500
+++ b/src/main/scala/SPARQL.scala	Tue Dec 15 21:08:44 2009 -0500
@@ -14,7 +14,7 @@
 case class SparqlSelect(attrs:SparqlAttributeList, triples:TriplePatterns)
 case class SparqlAttributeList(attributelist:List[Var])
 
-case class TriplePatterns(triplepatterns:List[TriplePattern])
+case class TriplePatterns(triplepatterns:List[TriplePattern], filter:Option[SparqlExpression])
 case class TriplePattern(s:S, p:P, o:O)
 
 case class ObjUri(stem:Stem, rel:Rel, attr:Attr, v:CellValue)
@@ -42,16 +42,48 @@
 
 case class Var(s:String)
 
+case class SparqlExpression(conjuncts:List[SparqlPrimaryExpression])
+sealed abstract class SparqlPrimaryExpression
+case class SparqlPrimaryExpressionEq(left:SparqlTermExpression, right:SparqlTermExpression) extends SparqlPrimaryExpression
+case class SparqlPrimaryExpressionLt(left:SparqlTermExpression, right:SparqlTermExpression) extends SparqlPrimaryExpression
+case class SparqlTermExpression(term:Term)
+
+sealed abstract class Term
+case class TermUri(obj:ObjUri) extends Term
+case class TermVar(v:Var) extends Term
+case class TermLit(lit:SparqlLiteral) extends Term
+
+
 case class Sparql() extends JavaTokenParsers {
 
   def select:Parser[SparqlSelect] =
     "SELECT" ~ attributelist ~ "{" ~ triplepatterns ~ "}" ^^ { case "SELECT"~a~"{"~t~"}" => SparqlSelect(a, t) }
 
+  def filter:Parser[SparqlExpression] =
+    "FILTER" ~ "(" ~ expression ~ ")" ^^ { case "FILTER"~"("~expression~")" => expression }
+
+  def expression:Parser[SparqlExpression] = 
+    repsep(primaryexpression, "&&") ^^ 
+    { SparqlExpression(_) }
+
+  def primaryexpression:Parser[SparqlPrimaryExpression] = (
+      value ~ "=" ~ value ^^
+      { case left ~ "=" ~ right => SparqlPrimaryExpressionEq(left, right) }
+    | value ~ "<" ~ value ^^
+      { case left ~ "<" ~ right => SparqlPrimaryExpressionLt(left, right) }
+  )
+
+  def value:Parser[SparqlTermExpression] = (
+      "<"~uri~">" ^^ { case "<"~x~">" => SparqlTermExpression(TermUri(Sparql.parseObjectURI(x))) }
+    | varr ^^ { x => SparqlTermExpression(TermVar(x)) }
+    | literal ^^ { x => SparqlTermExpression(TermLit(x)) }
+  )
+
   def attributelist:Parser[SparqlAttributeList] =
     rep(varr) ^^ { SparqlAttributeList(_) }
 
   def triplepatterns:Parser[TriplePatterns] =
-    repsep(triplepattern, ".") ^^ { TriplePatterns(_) }
+    repsep(triplepattern, ".") ~ opt(filter) ^^ { case pats~filter => TriplePatterns(pats, filter) }
 
   def triplepattern:Parser[TriplePattern] =
     subject ~ predicate ~ objectt ^^ { case s~p~o => TriplePattern(s, p, o) }
--- a/src/test/scala/RDB2RDFTest.scala	Tue Dec 15 16:46:38 2009 -0500
+++ b/src/test/scala/RDB2RDFTest.scala	Tue Dec 15 21:08:44 2009 -0500
@@ -10,9 +10,25 @@
 	RelationDesc(Attribute("id"), 
 		     Map(Attribute("id") -> Value(SQLDatatype.INTEGER),
 			 Attribute("lastName") -> Value(SQLDatatype.STRING),
-			 Attribute("width") -> Value(SQLDatatype.INTEGER), 
+			 Attribute("birthday") -> Value(SQLDatatype.INTEGER), // !!!
 			 Attribute("manager") -> ForeignKey(Relation("Employee"), Attribute("id")), 
-			 Attribute("address") -> ForeignKey(Relation("Address"),  Attribute("id"))))))
+			 Attribute("address") -> ForeignKey(Relation("Address"),  Attribute("id"))))
+      ))
+
+  val db2:DatabaseDesc = DatabaseDesc(
+    Map(Relation("Employee") -> 
+	RelationDesc(Attribute("id"), 
+		     Map(Attribute("id") -> Value(SQLDatatype.INTEGER),
+			 Attribute("lastName") -> Value(SQLDatatype.STRING),
+			 Attribute("birthday") -> Value(SQLDatatype.INTEGER), // !!!
+			 Attribute("manager") -> Value(SQLDatatype.INTEGER),
+			 Attribute("address") -> Value(SQLDatatype.INTEGER))),
+	Relation("Manage") -> 
+	RelationDesc(Attribute("id"), // !!doesnotexist!!
+		     Map(Attribute("id") -> Value(SQLDatatype.INTEGER), // !!doesnotexist!!
+			 Attribute("manager") -> ForeignKey(Relation("Employee"), Attribute("id")), 
+			 Attribute("manages") -> ForeignKey(Relation("Employee"),  Attribute("id"))))
+      ))
 
 
   test("?s <p> <x>") {
@@ -108,4 +124,35 @@
     assert(RDB2RDF(db, sparqlSelect, StemURI("http://hr.example/DB/"), PrimaryKey(Attribute(Name("id")))) === sqlSelect)
   }
 
+  test("transform filter1") {
+    val sparqlParser = Sparql()
+    val sparqlSelect = sparqlParser.parseAll(sparqlParser.select, """
+SELECT ?empName ?grandManagName {
+         ?emp          <http://hr.example/DB/Employee#lastName>   ?empName .
+         ?emp          <http://hr.example/DB/Employee#birthday>   ?empBday .
+         ?lower        <http://hr.example/DB/Manage#manages>   ?emp .
+         ?lower        <http://hr.example/DB/Manage#manager>   ?manager .
+         ?manager      <http://hr.example/DB/Employee#birthday>   ?manBday .
+         ?upper        <http://hr.example/DB/Manage#manages>   ?manager .
+         ?upper        <http://hr.example/DB/Manage#manager>   ?grandManager .
+         ?grandManager <http://hr.example/DB/Employee#birthday>   ?grandManBday .
+         ?grandManager <http://hr.example/DB/Employee#lastName>   ?grandManagName
+         FILTER (?manBday < ?empBday && ?grandManBday < ?manBday)
 }
+""").get
+    val sqlParser = Sql()
+    val sqlSelect = sqlParser.parseAll(sqlParser.select, """
+SELECT R_emp.lastName AS A_empName, R_grandManager.lastName AS A_grandManagName
+  FROM Employee AS R_emp
+       INNER JOIN Manage AS R_lower ON R_lower.manages=R_emp.id
+       INNER JOIN Employee AS R_manager ON R_manager.id=R_lower.manager
+                                         AND R_manager.birthday < R_emp.birthday
+       INNER JOIN Manage AS R_upper ON R_upper.manages=R_manager.id
+       INNER JOIN Employee AS R_grandManager ON R_grandManager.id=R_upper.manager
+                                         AND R_grandManager.birthday < R_manager.birthday
+ WHERE R_emp.lastName IS NOT NULL AND R_grandManager.lastName IS NOT NULL
+""").get
+    assert(RDB2RDF(db2, sparqlSelect, StemURI("http://hr.example/DB/"), PrimaryKey(Attribute(Name("id")))) === sqlSelect)
+  }
+
+}
--- a/src/test/scala/SQLTest.scala	Tue Dec 15 16:46:38 2009 -0500
+++ b/src/test/scala/SQLTest.scala	Tue Dec 15 21:08:44 2009 -0500
@@ -82,10 +82,10 @@
 SELECT R_emp.lastName AS A_empName, R_grandManager.lastName AS A_grandManagName
   FROM Employee AS R_emp
        INNER JOIN Manage AS R_lower ON R_lower.manages=R_emp.id
-       INNER JOIN Employee AS R_manager ON R_manager.id=R_lower.R_manager
+       INNER JOIN Employee AS R_manager ON R_manager.id=R_lower.manager
                                          AND R_manager.birthday < R_emp.birthday
        INNER JOIN Manage AS R_upper ON R_upper.manages=R_manager.id
-       INNER JOIN Employee AS R_grandManager ON R_grandManager.id=R_upper.R_manager
+       INNER JOIN Employee AS R_grandManager ON R_grandManager.id=R_upper.manager
                                          AND R_grandManager.birthday < R_manager.birthday
  WHERE R_emp.lastName IS NOT NULL AND R_grandManager.lastName IS NOT NULL
 """
@@ -95,13 +95,13 @@
 							    AttrAlias(Name("A_grandManagName"))))),
 			  TableList(List(Join(RelAsRelAlias(Relation(Name("Employee")),RelAlias(Name("R_emp"))),None),
 					 Join(RelAsRelAlias(Relation(Name("Manage")),RelAlias(Name("R_lower"))),Some(Expression(List(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manages"))),RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("id"))))))))),
-					 Join(RelAsRelAlias(Relation(Name("Employee")),RelAlias(Name("R_manager"))),Some(Expression(List(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),RValueAttr(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("R_manager"))))), PrimaryExpressionLt(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))),RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("birthday"))))))))),
+					 Join(RelAsRelAlias(Relation(Name("Employee")),RelAlias(Name("R_manager"))),Some(Expression(List(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))),RValueAttr(RelAliasAttribute(RelAlias(Name("R_lower")),Attribute(Name("manager"))))), PrimaryExpressionLt(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("birthday"))),RValueAttr(RelAliasAttribute(RelAlias(Name("R_emp")),Attribute(Name("birthday"))))))))),
 					 Join(RelAsRelAlias(Relation(Name("Manage")),RelAlias(Name("R_upper"))),Some(Expression(List(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_upper")),Attribute(Name("manages"))),RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),Attribute(Name("id"))))))))),
 					 Join(RelAsRelAlias(Relation(Name("Employee")),RelAlias(Name("R_grandManager"))),
 					      Some(Expression(List(PrimaryExpressionEq(RelAliasAttribute(RelAlias(Name("R_grandManager")),
 													 Attribute(Name("id"))),
 										       RValueAttr(RelAliasAttribute(RelAlias(Name("R_upper")),
-														    Attribute(Name("R_manager"))))),
+														    Attribute(Name("manager"))))),
 								   PrimaryExpressionLt(RelAliasAttribute(RelAlias(Name("R_grandManager")),
 													 Attribute(Name("birthday"))),
 										       RValueAttr(RelAliasAttribute(RelAlias(Name("R_manager")),
--- a/src/test/scala/SparqlTest.scala	Tue Dec 15 16:46:38 2009 -0500
+++ b/src/test/scala/SparqlTest.scala	Tue Dec 15 21:08:44 2009 -0500
@@ -10,7 +10,7 @@
     val e = """
 ?emp      <http://hr.example/DB/Employee#lastName>   "bob"^^<http://www.w3.org/2001/XMLSchema#string>
 """
-    val expected = TriplePatterns(List(TriplePattern(SVar(Var("emp")),PUri(Stem("http://hr.example/DB"),Rel("Employee"),Attr("lastName")),OLit(SparqlLiteral(RDFLiteral("bob",Datatype(new URI("http://www.w3.org/2001/XMLSchema#string"))))))))
+    val expected = TriplePatterns(List(TriplePattern(SVar(Var("emp")),PUri(Stem("http://hr.example/DB"),Rel("Employee"),Attr("lastName")),OLit(SparqlLiteral(RDFLiteral("bob",Datatype(new URI("http://www.w3.org/2001/XMLSchema#string"))))))), None)
     assert(expected === (a.parseAll(a.triplepatterns, e).get))
   }
 
@@ -19,7 +19,7 @@
     val e = """
 ?emp      <http://hr.example/DB/Employee#age>   "21"^^<http://www.w3.org/2001/XMLSchema#integer>
 """
-    val expected = TriplePatterns(List(TriplePattern(SVar(Var("emp")),PUri(Stem("http://hr.example/DB"),Rel("Employee"),Attr("age")),OLit(SparqlLiteral(RDFLiteral("21",Datatype(new URI("http://www.w3.org/2001/XMLSchema#integer"))))))))
+    val expected = TriplePatterns(List(TriplePattern(SVar(Var("emp")),PUri(Stem("http://hr.example/DB"),Rel("Employee"),Attr("age")),OLit(SparqlLiteral(RDFLiteral("21",Datatype(new URI("http://www.w3.org/2001/XMLSchema#integer"))))))), None)
     assert(expected === (a.parseAll(a.triplepatterns, e).get))
   }
 
@@ -44,10 +44,76 @@
 	  TriplePattern(
 	    SVar(Var("manager")),
 	    PUri(Stem("http://hr.example/DB"),Rel("Employee"),Attr("lastName")),
-	    OVar(Var("managName")))))
+	    OVar(Var("managName")))), None)
     assert(tps === a.parseAll(a.triplepatterns, e).get)
   }
 
+  // ?manBday < ?empBday && ?grandManBday < ?manBday
+  test("SparqlTermExpression") {
+    val a = Sparql()
+    val e = """
+?emp
+"""
+    val expected = SparqlTermExpression(TermVar(Var("emp")))
+    assert(expected === (a.parseAll(a.value, e).get))
+  }
+
+  test("SparqlPrimaryExpressionEq") {
+    val a = Sparql()
+    val e = """
+?emp<?emp
+"""
+    val expected = SparqlPrimaryExpressionLt(SparqlTermExpression(TermVar(Var("emp"))), SparqlTermExpression(TermVar(Var("emp"))))
+    assert(expected === (a.parseAll(a.primaryexpression, e).get))
+  }
+
+  test("SparqlExpression") {
+    val a = Sparql()
+    val e = """
+?manBday < ?empBday && ?grandManBday < ?manBday
+"""
+    val expected = SparqlExpression(List(
+      SparqlPrimaryExpressionLt(SparqlTermExpression(TermVar(Var("manBday"))), SparqlTermExpression(TermVar(Var("empBday")))), 
+      SparqlPrimaryExpressionLt(SparqlTermExpression(TermVar(Var("grandManBday"))), SparqlTermExpression(TermVar(Var("manBday"))))))
+    assert(expected === (a.parseAll(a.expression, e).get))
+  }
+
+  test("FILTER") {
+    val a = Sparql()
+    val e = """
+FILTER(?manBday < ?empBday && ?grandManBday < ?manBday)
+"""
+    val expected = SparqlExpression(List(
+      SparqlPrimaryExpressionLt(SparqlTermExpression(TermVar(Var("manBday"))), SparqlTermExpression(TermVar(Var("empBday")))), 
+      SparqlPrimaryExpressionLt(SparqlTermExpression(TermVar(Var("grandManBday"))), SparqlTermExpression(TermVar(Var("manBday"))))))
+    assert(expected === (a.parseAll(a.filter, e).get))
+  }
+
+  test("SELECT with FILTER") {
+    val a = Sparql()
+    val e = """
+SELECT ?empName ?manageName {
+?emp      <http://hr.example/DB/Employee#lastName>   ?empName
+FILTER(?manBday < ?empBday && ?grandManBday < ?manBday)
+}
+"""
+    val tps =
+      SparqlSelect(
+	SparqlAttributeList(List(Var("empName"), Var("manageName"))),
+	TriplePatterns(
+	  List(
+	    TriplePattern(
+	      SVar(Var("emp")),
+		PUri(Stem("http://hr.example/DB"),Rel("Employee"),Attr("lastName")),
+	      OVar(Var("empName")))), 
+	  Some(SparqlExpression(List(
+	    SparqlPrimaryExpressionLt(SparqlTermExpression(TermVar(Var("manBday"))),
+				      SparqlTermExpression(TermVar(Var("empBday")))), 
+	    SparqlPrimaryExpressionLt(SparqlTermExpression(TermVar(Var("grandManBday"))),
+				      SparqlTermExpression(TermVar(Var("manBday")))))))))
+    assert(tps === a.parseAll(a.select, e).get)
+  }
+
   test("SQLbgp") {
     val a = Sparql()
     val e = """
@@ -73,10 +139,29 @@
 	    TriplePattern(
 	      SVar(Var("manager")),
 	      PUri(Stem("http://hr.example/DB"),Rel("Employee"),Attr("lastName")),
-	      OVar(Var("managName"))))))
+	      OVar(Var("managName")))), None))
     assert(tps === a.parseAll(a.select, e).get)
   }
 
+  test("parse filter1") {
+    val a = Sparql()
+    val e = """
+SELECT ?empName ?grandManagName {
+         ?emp          <http://hr.example/DB/Employee#lastName>   ?empName .
+         ?emp          <http://hr.example/DB/Employee#birthday>   ?empBday .
+         ?lower        <http://hr.example/DB/Manage#manages>   ?emp .
+         ?lower        <http://hr.example/DB/Manage#manager>   ?manager .
+         ?manager      <http://hr.example/DB/Employee#birthday>   ?manBday .
+         ?upper        <http://hr.example/DB/Manage#manages>   ?manager .
+         ?upper        <http://hr.example/DB/Manage#manager>   ?grandManager .
+         ?grandManager <http://hr.example/DB/Employee#birthday>   ?grandManBday .
+         ?grandManager <http://hr.example/DB/Employee#lastName>   ?grandManagName
+         FILTER (?manBday < ?empBday && ?grandManBday < ?manBday)
+}
+"""
+    a.parseAll(a.select, e).get
+  }
+
   test("decompose a predicate uri in stem, rel and attr") {
     val uri = "http://hr.example/our/favorite/DB/Employee#lastName"
     val puri:PUri = Sparql.parsePredicateURI(uri)