--- 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)