+ multiple rule invocations
authorEric Prud'hommeaux <eric@w3.org>
Mon, 08 Feb 2010 15:19:02 -0500
changeset 159 c7bf17241c5d
parent 158 4e74abad8304
child 160 517697fc09ab
+ multiple rule invocations
src/main/scala/SparqlToSparql.scala
src/test/scala/SparqlToSparqlTest.scala
--- a/src/main/scala/SparqlToSparql.scala	Mon Feb 08 12:52:08 2010 -0500
+++ b/src/main/scala/SparqlToSparql.scala	Mon Feb 08 15:19:02 2010 -0500
@@ -59,6 +59,13 @@
     }
   }
   case class Bindings (b:Map[sparql.Construct, List[Map[sparql.Var, sparql.Term]]]) {
+    def countEditions () = {
+      var count = 0
+      b.map((constructlist) =>
+	constructlist._2.map((_) =>
+	  count = count + 1))
+      count
+    }
     def toGraphPattern ():sparql.GraphPattern = {
       var conjNo = 0
       val conjuncts = b.foldLeft(List[sparql.GraphPattern]())((conj, constructlist) => {
@@ -89,40 +96,58 @@
       if (b.contains(construct)) this
       else Bindings(b + (construct -> List[Map[sparql.Var, sparql.Term]]()))
     }
-    // val varsS:Option[Bindings] = vars.maybeRebind(construct, v, to)
-    def maybeBind (construct:sparql.Construct, v:sparql.Term, to:sparql.Term):Option[Bindings] = {
-      v match {
-	case v:sparql.TermVar =>
-	  if (b(construct).size > 0 && b(construct)(0).contains(v.v)) {
-	    println(v.v + ":" + b(construct)(0)(v.v) + " == " + to + " => " + (to == b(construct)(0)(v.v)))
-	    if (to == b(construct)(0)(v.v)) Some(this) // !!! iterate on 0
-	    else None
+    // val varsS:Option[Bindings] = vars.maybeRebind(construct, v, tos)
+    // b:Map[sparql.Construct, List[Map[sparql.Var, sparql.Term]]]
+    def mustBind (construct:sparql.Construct, vs:sparql.Term, tos:sparql.Term, vo:sparql.Term, too:sparql.Term):Bindings = {
+      /* rediculous traversal for the first viably matching rule edition. */
+      var matched = false
+      val existing:List[Map[sparql.Var, sparql.Term]] = b(construct).map((map) => {
+	def _matches (l:sparql.Term, r:sparql.Term):Boolean = {
+	  l match {
+	    case v:sparql.TermVar =>
+	      if (map.contains(v.v)) {
+		if (r == map(v.v)) true
+		else false
+	      } else {
+		true
+	      }
+	    case u:sparql.TermUri =>
+	      if (r == u.u) true
+	      else false
+	    case l:sparql.TermLit =>
+	      if (r == l.lit) true
+	      else false
+	  }
+	}
+	val sMatches = _matches(vs, tos)
+	val oMatches = _matches(vo, too)
+
+	if (sMatches & oMatches) {
+	  matched = true
+	  map + (vs.asInstanceOf[sparql.TermVar].v -> tos) + (vo.asInstanceOf[sparql.TermVar].v -> too)
+	} else
+	  map
+      })
+      if (matched)
+	Bindings(b + (construct -> existing))
+      else {
+	Bindings(b.map((constructlist) => {
+	  val (oldConstr, l) = constructlist
+	  if (oldConstr == construct) {
+	    val ent = Map[sparql.Var, sparql.Term](vs.asInstanceOf[sparql.TermVar].v -> tos,
+						   vo.asInstanceOf[sparql.TermVar].v -> too)
+	    val list = l ++ List(ent)
+	    (construct -> list)
 	  } else {
-	    val old = if (b(construct).size > 0) b(construct)(0)
-		      else Map[sparql.Var, sparql.Term]()
-	    println(v.v + " unbound in " + old)
-	    val m:Map[sparql.Var, sparql.Term] = old ++ Map(v.v -> to)
-	    Some(Bindings(b + (construct -> List(m))))
+	    (oldConstr -> l)
 	  }
-	case u:sparql.TermUri =>
-	  if (to == u.u) Some(this)
-	  else None
-	case l:sparql.TermLit =>
-	  if (to == l.lit) Some(this)
-	  else None
+	}))
       }
     }
   }
   def createEmptyBindings () = Bindings(Map[sparql.Construct, List[Map[sparql.Var, sparql.Term]]]())
 
   case class RuleMap (rules:Map[sparql.Uri, List[HornRule]]) {
-    override def toString = "RuleMap(Map(" + rules.map((ent) => {
-      val (u, l) = ent
-      "\"" + _shorten(u.toString) + "\" -> List(" + l.map((hr) => {
-	_shorten(hr.toString).replace("\n", "\n    ")
-      }).mkString("\n    ", ",\n    ", "") + ")"
-    }).mkString("\n  ", ",\n  ", "") + "))"
-
     def transform (prove:List[sparql.TriplePattern], used:Set[sparql.TriplePattern], varsP:Bindings):Bindings = {
       val _pad = used.foldLeft("")((s, x) => s + " ")
       def _deepPrint (s:String):Unit = { println(used.size + ":" + _pad + s.replace("\n", "\n" + _pad)) }
@@ -134,7 +159,7 @@
       val car = prove(0)
       val cdr = prove.filterNot (_ == car)
       _deepPrint("RuleMap.transform(" + _shorten(car.toString) + ", " + varsP.toString + ")")
-      val ret = car.p match {
+      val xform = car.p match {
 	case sparql.TermUri(u) =>
 	  // for each rule that supplies predicate u
 	  var _ruleNo = 0;
@@ -144,28 +169,35 @@
 	    _deepPrint1(_prefix, "trying " + _shorten(hornRule.trigger.toString))
 	    val vars = varsP.ensureGraphPattern(hornRule.construct)
 	    // try matching the subject
-	    val varsS:Option[Bindings] = vars.maybeBind(hornRule.construct, hornRule.trigger.s, car.s)
-	    val ret:Bindings = if (varsS.isDefined) {
-	      // try matching the object
-	      val varsO:Option[Bindings] = varsS.get.maybeBind(hornRule.construct, hornRule.trigger.o, car.o)
-	      if (varsO.isDefined) {
-		if (cdr.size > 0) {
-		  transform(cdr, used + car, varsO.get)
-		} else {
-		  _deepPrint1("match!", _shorten(hornRule.trigger.toString) + "(" + _shorten(car.toString) + ") matches with " + _shorten(varsO.get.toString))
-		  varsO.get
-		}
-	      } else createEmptyBindings
-	    } else createEmptyBindings
+	    val varss:Bindings = vars.mustBind(hornRule.construct, hornRule.trigger.s, car.s, hornRule.trigger.o, car.o)
+	    val ret =
+	      if (cdr.size > 0) {
+		transform(cdr, used + car, varss)
+	      } else {
+		_deepPrint1("match!", _shorten(hornRule.trigger.toString) + "(" + _shorten(car.toString) + ") matches with " + _shorten(varss.toString))
+		varss
+	      }
 	    _deepPrint1(_prefix, _shorten(hornRule.trigger.toString) + "(" + _shorten(car.toString) + ") matches ..." + bindings.toString + ret.toString)
-	    if (ret.b.size > bindings.b.size) ret
+
+	    /* Magic Huristics */
+	    if (bindings.countEditions == 0) ret
+	    else if (ret.countEditions == 0) bindings
+	    else if (ret.countEditions < bindings.countEditions) ret
 	    else bindings
 	  })
 	case _ => error("not implemented: " + car.p)
       }
-      _deepPrint("RuleMap.transform(" + _shorten(car.toString) + ") => " + _shorten(ret.toString))
-      ret
+      _deepPrint("RuleMap.transform(" + _shorten(car.toString) + ") => " + _shorten(xform.toString))
+      xform
     }
+
+    override def toString = "RuleMap(Map(" + rules.map((ent) => {
+      val (u, l) = ent
+      "\"" + _shorten(u.toString) + "\" -> List(" + l.map((hr) => {
+	_shorten(hr.toString).replace("\n", "\n    ")
+      }).mkString("\n    ", ",\n    ", "") + ")"
+    }).mkString("\n  ", ",\n  ", "") + "))"
+
   }
 
   def mapGraphPattern (gp:sparql.GraphPattern, ruleMap:RuleMap):sparql.GraphPattern = {
--- a/src/test/scala/SparqlToSparqlTest.scala	Mon Feb 08 12:52:08 2010 -0500
+++ b/src/test/scala/SparqlToSparqlTest.scala	Mon Feb 08 15:19:02 2010 -0500
@@ -129,50 +129,53 @@
     assert(transformed === expected)
   }
 
-//   test("trans head") {
-//     val sparqlParser = Sparql()
-//     val query = sparqlParser.parseAll(sparqlParser.select, """
-// PREFIX foaf : <http://xmlns.com/foaf/0.1/>
-// PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
-// SELECT ?lname {
-//   ?who  foaf:last_name    ?lname .
-//   ?who  foaf:knows        ?whom  .
-//   ?whom foaf:knows        ?whom2 .
-//   ?whom2 foaf:last_name    "Smith"^^xsd:string }
-// """).get
-//     val rule1 = sparqlParser.parseAll(sparqlParser.construct, """
-// PREFIX foaf : <http://xmlns.com/foaf/0.1/>
-// PREFIX empP : <http://hr.example/DB/Employee#>
-// PREFIX task : <http://hr.example/DB/Task#>
-// CONSTRUCT { ?emp  foaf:last_name  ?wname .
-//             ?emp  foaf:knows      ?man .
-//             ?man  foaf:last_name  ?mname }
-//     WHERE { ?emp  empP:lastName   ?wname .
-//             ?pair task:drone      ?emp .
-//             ?pair task:manager    ?man .
-//             ?man  empP:lastName   ?mname }
-// """).get
-//     SparqlToSparql.Abbreviations.update("<http://xmlns.com/foaf/0.1/last_name>", "foaf:last_name")
-//     SparqlToSparql.Abbreviations.update("<http://xmlns.com/foaf/0.1/knows>", "foaf:knows")
-//     SparqlToSparql.Abbreviations.update("<http://hr.example/DB/Employee#lastName>", "Employee:lastName")
-//     SparqlToSparql.Abbreviations.update("<http://hr.example/DB/Task#drone>", "Task:drone")
-//     SparqlToSparql.Abbreviations.update("<http://hr.example/DB/Task#manager>", "Task:manager")
-//     SparqlToSparql.Abbreviations.update("^^http://www.w3.org/2001/XMLSchema#string", "")
+  test("trans head") {
+    val sparqlParser = Sparql()
+    val query = sparqlParser.parseAll(sparqlParser.select, """
+PREFIX foaf : <http://xmlns.com/foaf/0.1/>
+PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
+SELECT ?lname {
+  ?who  foaf:last_name    ?lname .
+  ?who  foaf:knows        ?whom  .
+  ?whom foaf:knows        ?whom2 .
+  ?whom2 foaf:last_name    "Smith"^^xsd:string }
+""").get
+    val rule1 = sparqlParser.parseAll(sparqlParser.construct, """
+PREFIX foaf : <http://xmlns.com/foaf/0.1/>
+PREFIX empP : <http://hr.example/DB/Employee#>
+PREFIX task : <http://hr.example/DB/Task#>
+CONSTRUCT { ?emp  foaf:last_name  ?wname .
+            ?emp  foaf:knows      ?man .
+            ?man  foaf:last_name  ?mname }
+    WHERE { ?emp  empP:lastName   ?wname .
+            ?pair task:drone      ?emp .
+            ?pair task:manager    ?man .
+            ?man  empP:lastName   ?mname }
+""").get
+    SparqlToSparql.Abbreviations.update("<http://xmlns.com/foaf/0.1/last_name>", "foaf:last_name")
+    SparqlToSparql.Abbreviations.update("<http://xmlns.com/foaf/0.1/knows>", "foaf:knows")
+    SparqlToSparql.Abbreviations.update("<http://hr.example/DB/Employee#lastName>", "Employee:lastName")
+    SparqlToSparql.Abbreviations.update("<http://hr.example/DB/Task#drone>", "Task:drone")
+    SparqlToSparql.Abbreviations.update("<http://hr.example/DB/Task#manager>", "Task:manager")
+    SparqlToSparql.Abbreviations.update("^^http://www.w3.org/2001/XMLSchema#string", "")
 
-//     val transformed = SparqlToSparql(query, List(rule1))
-//     val expected = sparqlParser.parseAll(sparqlParser.select, """
-// PREFIX empP : <http://hr.example/DB/Employee#>
-// PREFIX task : <http://hr.example/DB/Task#>
-// PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
-// SELECT ?lname
-//      { ?who  empP:lastName   ?lname .
-//        ?pair task:drone      ?who .
-//        ?pair task:manager    ?whom .
-//        ?pair2 task:drone      ?whom .
-//        ?pair2 task:manager    ?whom2 .
-//        ?whom2 empP:lastName   "Smith"^^xsd:string }
-// """).get
-//     assert(transformed === expected)
-//   }
+    val transformed = SparqlToSparql(query, List(rule1))
+    val expected = sparqlParser.parseAll(sparqlParser.select, """
+PREFIX empP : <http://hr.example/DB/Employee#>
+PREFIX task : <http://hr.example/DB/Task#>
+PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
+SELECT ?lname
+     {{?who     empP:lastName   ?lname .
+       ?_0_pair task:drone      ?who .
+       ?_0_pair task:manager    ?whom .
+       ?whom    empP:lastName   ?_0_mname }
+      {?whom    empP:lastName   ?_1_wname .
+       ?_1_pair task:drone      ?whom .
+       ?_1_pair task:manager    ?whom2 .
+       ?whom2   empP:lastName   "Smith"^^xsd:string }
+     }
+""").get
+    assert(transformed === expected)
+  }
 
 }