~ toString to yield syntactically valid SQL on limit/offset
authorEric Prud'hommeaux <eric@w3.org>
Tue, 15 Jun 2010 16:11:39 -0700
changeset 221 27990ceea967
parent 220 8473f50ae920
child 222 152edb04d504
~ toString to yield syntactically valid SQL on limit/offset
src/main/scala/SPARQL.scala
src/main/scala/SparqlToSparql.scala
src/main/scala/SparqlToSparqlToSql.scala
src/test/scala/SparqlToSparqlTest.scala
src/test/scala/SparqlToSparqlToSqlTest.scala
--- a/src/main/scala/SPARQL.scala	Sun Jun 13 19:42:52 2010 -0400
+++ b/src/main/scala/SPARQL.scala	Tue Jun 15 16:11:39 2010 -0700
@@ -22,8 +22,8 @@
     { if (distinct) "DISTINCT " else "" }+
     attrs+"\n"+gp+" "+
     { if (order.size > 0) {order.map(o => o.toString).mkString("ORDER BY ", " ", " ") } else "" }+
-    { if (offset.isDefined) {"OFFSET " + offset + " "} else "" }+
-    { if (limit.isDefined) {"LIMIT " + limit + " "} else "" }
+    { if (offset.isDefined) {"OFFSET " + offset.get + " "} else "" }+
+    { if (limit.isDefined) {"LIMIT " + limit.get + " "} else "" }
 }
 case class Construct(head:TriplesBlock, gp:GraphPattern)
 case class SparqlAttributeList(attributelist:List[Var]) {
--- a/src/main/scala/SparqlToSparql.scala	Sun Jun 13 19:42:52 2010 -0400
+++ b/src/main/scala/SparqlToSparql.scala	Tue Jun 15 16:11:39 2010 -0700
@@ -9,6 +9,21 @@
 import scala.util.parsing.combinator._
 import w3c.sw.sparql
 
+case class MapTarget (term:sparql.Term, nm:Option[NodePattern])
+
+case class NodePatternMap (m:Map[sparql.Var, NodePattern]) {
+  def var2maptarget (t:sparql.Term) : MapTarget = {
+    t match {
+      case sparql.TermVar(v) => MapTarget(t, m.get(v))
+      case _ => MapTarget(t, None)
+    }
+  }
+}
+
+case class NodePattern (iface:String, direct:String)
+
+case class SparqlMap (construct:sparql.Construct, nodemap:NodePatternMap)
+
 object SparqlToSparql {
 
   /**
@@ -27,53 +42,95 @@
   /**
    * Substitute the terms in a triple pattern or a graph pattern with other terms.
    */
-  def substituteTerm (changeMe:sparql.Term, from:sparql.Term, to:sparql.Term):sparql.Term = {
-    if (changeMe == from) to
-    else changeMe
+  def substituteTerm (changeMe:sparql.Term, from:sparql.Term, to:sparql.Term, nm:Option[NodePattern]):sparql.Term = {
+    if (changeMe == from) {
+      from match {
+	case sparql.TermVar(v) => {
+    	  if (nm.isDefined) {
+    	    val nodeMap = nm.get
+	    to match {
+    	    case sparql.TermVar(v2) => {
+    	      println("reverse map(" + v2 + ",\n            " + nodeMap.direct + ",\n            " + nodeMap.iface)
+	      to
+	    }
+    	    case sparql.TermBNode(b) => {
+    	      println("reverse map(" + b + ",\n            " + nodeMap.direct + ",\n            " + nodeMap.iface)
+	      to
+	    }
+    	    case sparql.TermUri(u) => {
+	      val s:String = u.s
+	      val r = u.s.replaceAll(nodeMap.iface, nodeMap.direct)
+	      //println("\"" + u.s + "\".replaceAll("+nodeMap.iface+", "+nodeMap.direct+") = \""+r+"\"")
+	      sparql.TermUri(sparql.Uri(u.s.replaceAll(nodeMap.iface, nodeMap.direct))) // !!! failure here should error()
+	    }
+    	    case sparql.TermLit(l) => 
+    	      error("variable " + v + " bound to literlal " + l + " but should be a node as indicated by " + nodeMap)
+	    }
+	  } else to
+	}
+	case _ => to
+      }
+    } else changeMe
   }
-  def substitute (gp:sparql.GraphPattern, from:sparql.Term, to:sparql.Term) = {
+
+  def substitute (gp:sparql.GraphPattern, from:sparql.Term, target:MapTarget) = {
     gp match {
       case sparql.TriplesBlock(triplepatterns) => sparql.TriplesBlock(
 	triplepatterns.map((tp) => {
 	  // println("sub(" + tp.o + ", " + from + ", " + to + ") => ")
-	  // println(substituteTerm(toTerm(tp.o), tp.o, from, to))
-	  sparql.TriplePattern(substituteTerm(tp.s, from, to),
+	  // println(substituteTerm(toTerm(tp.o), tp.o, from, target.term))
+	  sparql.TriplePattern(substituteTerm(tp.s, from, target.term, target.nm),
 			       tp.p,
-			       substituteTerm(tp.o, from, to))
+			       substituteTerm(tp.o, from, target.term, target.nm))
 	}
       ))
       case _ => error("not implemented" + gp)
     }
   }
 
-  type VarMap = Map[sparql.Var, sparql.Term]
+  type VarMap = Map[sparql.Var, MapTarget]
 
-  def substituteGraphPattern (gp:sparql.GraphPattern, vartermmap:VarMap, varPrefix:String):sparql.GraphPattern = {
+  def substituteGraphPattern (gp:sparql.GraphPattern, vartargetmap:VarMap, varPrefix:String):sparql.GraphPattern = {
     /**
      * Substitute gp (the rule body) with the variables and constants from the
      * query which matched variables in the rule head.
      */
-    val mapped = vartermmap.foldLeft(gp)((incrementalGP, varterm) => {
-      val (varr, term) = varterm
-      substitute(incrementalGP, sparql.TermVar(varr), term)
+    val mapped = vartargetmap.foldLeft(gp)((incrementalGP, vartarget) => {
+      val (varr, target) = vartarget
+      val ret = substitute(incrementalGP, sparql.TermVar(varr), target)
+      //println("^^incrementalGP: " + incrementalGP + "\nvartarget: " + vartarget + "\nret: " + ret)
+      ret;
     })
 
     /**
      * "Uniquely" prefix unmapped vars to void conflict with other rules.
      */
-    // val bound = Set[sparql.Var](vartermmap.map((varterm) => varterm._1))
-    val bound = vartermmap.foldLeft(Set[sparql.Var]())((s, varterm) => s + varterm._1)
-    val mappedTo = vartermmap.foldLeft(Set[sparql.Term]())((s, varterm) => s + varterm._2)
+    // val bound = Set[sparql.Var](vartargetmap.map((vartarget) => vartarget._1))
+    val bound = vartargetmap.foldLeft(Set[sparql.Var]())((s, vartarget) => s + vartarget._1)
+    val mappedTo = vartargetmap.foldLeft(Set[sparql.Term]())((s, vartarget) => {
+      val MapTarget(term, optnp) = vartarget._2
+      (term, optnp) match {
+	case (sparql.TermUri(u), Some(np)) => {
+	  s + sparql.TermUri(sparql.Uri(u.s.replaceAll(np.iface, np.direct))) // !!! failure here should error()
+	}
+	case (_, _) => s + vartarget._2.term
+      }
+    })
     val vars = gp.findVars
     val diff = vars -- bound
     diff.foldLeft(mapped)((incrementalGP, varr) => {
-      substitute(incrementalGP, sparql.TermVar(varr), sparql.TermVar(sparql.Var(varPrefix + varr.s))).trim(mappedTo)
+      val full = substitute(incrementalGP, sparql.TermVar(varr), 
+		 MapTarget(sparql.TermVar(sparql.Var(varPrefix + varr.s)),
+			   None// !! vartargetmap(varr).nm
+			 ))
+      full.trim(mappedTo)
     })
   }
-  case class RuleIndex (trigger:sparql.TriplePattern, construct:sparql.Construct) {
-    override def toString = "{ \"" + trigger + "\" } => {\"\n  " + _shorten(construct.gp.toString).replace("\n", "\n  ") + "\n\"}"
+  case class RuleIndex (trigger:sparql.TriplePattern, smap:SparqlMap) {
+    override def toString = "{ \"" + trigger + "\" } => {\"\n  " + _shorten(smap.construct.gp.toString).replace("\n", "\n  ") + "\n\"}"
     def transform (tp:sparql.TriplePattern):sparql.GraphPattern = {
-      substitute(substitute(construct.gp, trigger.s, tp.s), trigger.o, tp.o)
+      substitute(substitute(smap.construct.gp, trigger.s, smap.nodemap.var2maptarget(tp.s)),
+		 trigger.o, smap.nodemap.var2maptarget(tp.o))
     }
   }
 
@@ -125,20 +182,20 @@
     }
     // val varsS:Option[Bindings] = vars.maybeRebind(construct, v, tos)
     // b:Map[sparql.Construct, List[VarMap]]
-    def mustBind (construct:sparql.Construct, vs:sparql.Term, tos:sparql.Term, vo:sparql.Term, too:sparql.Term):Bindings = {
+    def mustBind (construct:sparql.Construct, nodemap:NodePatternMap, vs:sparql.Term, tos:sparql.Term, vo:sparql.Term, too:sparql.Term):Bindings = {
       /* ridiculous traversal for the first viably matching rule edition. */
       var matched = false
       val existing:List[VarMap] = b(construct).map((map:VarMap) => {
 	def _matches (l:sparql.Term, r:sparql.Term):(Boolean, VarMap) = {
-	  val empty:VarMap = Map[sparql.Var, sparql.Term]()
+	  val empty:VarMap = Map[sparql.Var, MapTarget]()
 	  (l, r) match {
 	    case (v:sparql.TermVar, x) =>
 	      // println("(v:sparql.TermVar, x)" + v.v + ":" + x)
 	      if (map.contains(v.v)) {
-		if (r == map(v.v)) (true, empty)
+		if (r == map(v.v).term) (true, empty)
 		else (false, empty)
 	      } else {
-		(true, Map[sparql.Var, sparql.Term](v.v -> r))
+		(true, Map[sparql.Var, MapTarget](v.v -> MapTarget(r, nodemap.m.get(v.v))))
 	      }
 	    case (x, v:sparql.TermVar) => {
 	      // println("query variable " + v + " known equal to " + x + " at compile time")
@@ -166,21 +223,21 @@
 	  val (oldConstr, l) = constructlist
 	  if (oldConstr == construct) {
 	    def _newBinding (l:sparql.Term, r:sparql.Term):VarMap = {
-	      val empty:VarMap = Map[sparql.Var, sparql.Term]()
+	      val empty:VarMap = Map[sparql.Var, MapTarget]()
 	      (l, r) match {
 		case (v:sparql.TermVar, _) =>
-		  Map[sparql.Var, sparql.Term](v.v -> r)
+		  Map[sparql.Var, MapTarget](v.v -> MapTarget(r, nodemap.m.get(v.v)))
 		case (b:sparql.TermBNode, _) => {
 		  println(".. synthetic query variable " + b + "")
-		  Map[sparql.Var, sparql.Term]()
+		  Map[sparql.Var, MapTarget]()
 		  // println("@@ mustBind:_newBinding(BNode) + " + b)
 		  // Map(sparql.Var("bnode_" + b.b.s) -> r) // !!!
 		}
 		case (_, v:sparql.TermVar) => {
 		  println(".. query variable " + v + " known equal to " + l + " at compile time")
-		  Map[sparql.Var, sparql.Term]()
+		  Map[sparql.Var, MapTarget]()
 		}
-		case (_, _) => Map[sparql.Var, sparql.Term]()
+		case (_, _) => Map[sparql.Var, MapTarget]()
 	      }
 	    }
 	    val ent = _newBinding(vs, tos) ++ _newBinding(vo, too)
@@ -221,9 +278,9 @@
 	    val _prefix = "rule:" + _ruleNo
 	    _ruleNo = _ruleNo + 1
 	    _deepPrint1(_prefix, "trying " + _shorten(hornRule.trigger.toString))
-	    val vars = varsP.ensureGraphPattern(hornRule.construct)
+	    val vars = varsP.ensureGraphPattern(hornRule.smap.construct)
 	    // try matching the subject
-	    val varss:Bindings = vars.mustBind(hornRule.construct, hornRule.trigger.s, car.s, hornRule.trigger.o, car.o)
+	    val varss:Bindings = vars.mustBind(hornRule.smap.construct, hornRule.smap.nodemap, hornRule.trigger.s, car.s, hornRule.trigger.o, car.o)
 	    val ret =
 	      if (cdr.size > 0) {
 		transform(cdr, used + car, varss)
@@ -281,10 +338,11 @@
     }
   }
 
-  def apply (query:sparql.Select, constructs:List[sparql.Construct]) : sparql.Select = {
+  def apply (query:sparql.Select, maps:List[SparqlMap]) : sparql.Select = {
     var _ruleNo = 0
     val ruleMap = RuleMap({
-      constructs.foldLeft(Map[sparql.Uri, List[RuleIndex]]())((m, rule) => {
+      maps.foldLeft(Map[sparql.Uri, List[RuleIndex]]())((m, sm) => {
+	val SparqlMap(rule, nodemap) = sm
 	// Register abbreviations for debugging output.
 	RuleLabels.update(rule.head.toString, "head" + _ruleNo)
 	RuleLabels.update(rule.gp.toString, "body" + _ruleNo)
@@ -293,8 +351,8 @@
 	rule.head.triplepatterns.foldLeft(m)((m, tp) => m + ({
 	  tp.p match {
 	    case sparql.TermUri(u) => u -> {
-	      if (m.contains(u)) m(u) ++ List(RuleIndex(tp, rule))
-	      else List(RuleIndex(tp, rule))}
+	      if (m.contains(u)) m(u) ++ List(RuleIndex(tp, sm))
+	      else List(RuleIndex(tp, sm))}
 	    case _ => error("not implemented: " + tp.p)
 	  }
 	}))
--- a/src/main/scala/SparqlToSparqlToSql.scala	Sun Jun 13 19:42:52 2010 -0400
+++ b/src/main/scala/SparqlToSparqlToSql.scala	Tue Jun 15 16:11:39 2010 -0700
@@ -47,7 +47,8 @@
 	)
 
 	/* Convert to equivalent SQL SELECT. */
-	val asStem = sparql2sparql.SparqlToSparql(select, List(rule))
+	val emptyPatternMap = sparql2sparql.NodePatternMap(Map[sparql.Var, sparql2sparql.NodePattern]())
+	val asStem = sparql2sparql.SparqlToSparql(select, List(sparql2sparql.SparqlMap(rule, emptyPatternMap)))
 	// println("triple: "+triple)
 	// println("asStem: "+asStem)
 	val sql.Select(distinct, sql.AttributeList(attributes), tablelist, expression, order,  offset, limit) = sparql2sql.SparqlToSql(schema, asStem, stemUri, false, true)._1
--- a/src/test/scala/SparqlToSparqlTest.scala	Sun Jun 13 19:42:52 2010 -0400
+++ b/src/test/scala/SparqlToSparqlTest.scala	Tue Jun 15 16:11:39 2010 -0700
@@ -7,13 +7,15 @@
 import org.scalatest.FunSuite
 import java.net.URI
 import w3c.sw.sparql.Sparql
-import w3c.sw.sparql2sparql.{SparqlToSparql}
+import w3c.sw.sparql2sparql.{SparqlToSparql,NodePatternMap,NodePattern,SparqlMap}
 
 /* The SparqlToSparqlTest class transforms SPARQL queries to a relational data
  * structure and compares them to a structure parsed from SQL.
  */
 class SparqlToSparqlTest extends FunSuite {
 
+  val emptyPatternMap = NodePatternMap(Map[sparql.Var, NodePattern]())
+
   test("foaf:last_name simple head") {
     val sparqlParser = Sparql()
     /* Query to be fired over view created by some transformation rules. */
@@ -41,7 +43,7 @@
 CONSTRUCT { ?who foaf:first_name ?fname }
     WHERE { ?who empP:firstName  ?fname }
 """).get
-    val transformed = SparqlToSparql(query, List(fname, lname))
+    val transformed = SparqlToSparql(query, List(SparqlMap(fname, emptyPatternMap), SparqlMap(lname, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX empP : <http://hr.example/DB/Employee#>
 PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
@@ -69,7 +71,7 @@
     WHERE { ?who empP:firstName  ?fname .
             ?who empP:lastName   ?lname }
 """).get
-    val transformed = SparqlToSparql(query, List(flname))
+    val transformed = SparqlToSparql(query, List(SparqlMap(flname, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX empP : <http://hr.example/DB/Employee#>
 PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
@@ -98,7 +100,7 @@
     WHERE { ?who empP:firstName  ?fname .
             ?who empP:lastName   ?lname }
 """).get
-    val transformed = SparqlToSparql(query, List(flname))
+    val transformed = SparqlToSparql(query, List(SparqlMap(flname, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX empP : <http://hr.example/DB/Employee#>
 PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
@@ -126,7 +128,7 @@
 CONSTRUCT { ?who foaf:last_name  ?lname }
     WHERE { ?who empP:lastName   ?lname }
 """).get
-    val transformed = SparqlToSparql(query, List(lname))
+    val transformed = SparqlToSparql(query, List(SparqlMap(lname, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX empP : <http://hr.example/DB/Employee#>
 PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
@@ -170,7 +172,7 @@
   ?who  foaf:knows        ?whom  .
   ?whom foaf:last_name    "Smith"^^xsd:string }
 """).get
-    val transformed = SparqlToSparql(query, List(Emp_TaskToFoaf))
+    val transformed = SparqlToSparql(query, List(SparqlMap(Emp_TaskToFoaf, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX empP : <http://hr.example/DB/Employee#>
 PREFIX task : <http://hr.example/DB/Task#>
@@ -197,7 +199,7 @@
 """).get
     SparqlToSparql.Abbreviations ++= Emp_TaskToFoaf_abbr
 
-    val transformed = SparqlToSparql(query, List(Emp_TaskToFoaf))
+    val transformed = SparqlToSparql(query, List(SparqlMap(Emp_TaskToFoaf, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX empP : <http://hr.example/DB/Employee#>
 PREFIX task : <http://hr.example/DB/Task#>
@@ -294,7 +296,7 @@
   ?indicCode  NDCcodes:NDC         ?indicNDC .
   ?indicCode  NDCcodes:ingredient  ?ingred }
 """).get
-    val transformed = SparqlToSparql(query, List(rule1))
+    val transformed = SparqlToSparql(query, List(SparqlMap(rule1, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX Person: <http://hospital.example/DB/Person#>
 PREFIX Sex_DE: <http://hospital.example/DB/Sex_DE#>
@@ -347,7 +349,7 @@
   ?indicCode  NDCcodes:NDC         ?indicNDC .
   ?indicCode  NDCcodes:ingredient  ?ingredCode }
 """).get
-    val transformed = SparqlToSparql(query, List(rule1))
+    val transformed = SparqlToSparql(query, List(SparqlMap(rule1, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX Item_Medication: <http://hospital.example/DB/Item_Medication#>
 PREFIX Medication: <http://hospital.example/DB/Medication#>
@@ -431,7 +433,7 @@
   ?indicCode  NDCcodes:NDC         ?indicNDC .
   ?indicCode  NDCcodes:ingredient  ?ingredCode }
 """).get
-    val transformed = SparqlToSparql(query, List(rule1))
+    val transformed = SparqlToSparql(query, List(SparqlMap(rule1, emptyPatternMap)))
     val expected = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX Person: <http://hospital.example/DB/Person#>
 PREFIX Sex_DE: <http://hospital.example/DB/Sex_DE#>
--- a/src/test/scala/SparqlToSparqlToSqlTest.scala	Sun Jun 13 19:42:52 2010 -0400
+++ b/src/test/scala/SparqlToSparqlToSqlTest.scala	Tue Jun 15 16:11:39 2010 -0700
@@ -8,7 +8,7 @@
 import scala.util.matching.Regex
 import java.net.URI
 import w3c.sw.sparql.Sparql
-import w3c.sw.sparql2sparql.{SparqlToSparql}
+import w3c.sw.sparql2sparql.{SparqlToSparql,NodePatternMap,NodePattern,SparqlMap}
 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
@@ -45,7 +45,9 @@
     WHERE { ?who empP:firstName  ?fname .
             ?who empP:lastName   ?lname }
 """).get
-    val asStem = SparqlToSparql(foafQuery, List(hr2foaf))
+    val hrPatternMap = NodePatternMap(Map[sparql.Var, NodePattern]())
+
+    val asStem = SparqlToSparql(foafQuery, List(SparqlMap(hr2foaf, hrPatternMap)))
     val stemQuery = sparqlParser.parseAll(sparqlParser.select, """
 PREFIX empP : <http://hr.example/DB/Employee#>
 PREFIX xsd : <http://www.w3.org/2001/XMLSchema#>
@@ -157,6 +159,8 @@
   ?indicCode  NDCcodes:ingredient  ?ingredCode }
 """).get
 
+  val hl7PatternMap = NodePatternMap(Map[sparql.Var, NodePattern]())
+
   test("~/swobjects/tests/healthCare/lists-notBound/hl7.rq short") {
     val sparqlParser = Sparql()
     val hl7Query = sparqlParser.parseAll(sparqlParser.select, """
@@ -202,7 +206,7 @@
     ?_0_indicCode NDCcodes:ingredient 6809
 }
 """).get
-    val asStem = SparqlToSparql(hl7Query, List(db2hl7))
+    val asStem = SparqlToSparql(hl7Query, List(SparqlMap(db2hl7, hl7PatternMap)))
     assert(asStem === stemQuery)
     val sqlParser = Sql()
     val sqlQuery = sqlParser.parseAll(sqlParser.select, """
@@ -321,7 +325,7 @@
    AND R_patient.MiddleName IS NOT NULL
    AND R__0_0_sexEntry.EntryName IS NOT NULL
 """).get
-    val asStem = SparqlToSparql(hl7Query, List(db2hl7))
+    val asStem = SparqlToSparql(hl7Query, List(SparqlMap(db2hl7, hl7PatternMap)))
     if (!(asStem == stemQuery)) {
       println(asStem.toString())
       println("---")
@@ -484,8 +488,8 @@
    */
   val bsbmDb:DatabaseDesc = DDLParser.parseAll(DDLParser.ddl, bsbmDdl).get
 
-  val delme = Sparql() // re-use ConstructParser ?
-  val db2bsbm = delme.parseAll(delme.construct, """
+  val db2bsbmP = Sparql() // re-use ConstructParser ?
+  val db2bsbm = db2bsbmP.parseAll(db2bsbmP.construct, """
 PREFIX map: <file:/E:/code/d2r-server-0.4/d2r-mapping.n3#>
 PREFIX vocab: <vocab/>
 PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
@@ -524,6 +528,15 @@
   ?ptp     producttypeproduct:product ?product .
 }""" //"
 			      ).get
+  val bsbmPatternMap = NodePatternMap(Map(
+    sparql.Var("pfp") ->
+    NodePattern("http://www4.wiwiss.fu-berlin.de/bizer/bsbm/v01/instances/ProductFeature([0-9]+)",
+		"http://bsbm.example/db/productfeatureproduct/productFeature.$1#record"), 
+    sparql.Var("ptp") ->
+    NodePattern("http://www4.wiwiss.fu-berlin.de/bizer/bsbm/v01/instances/ProductType([0-9]+)",
+		"http://bsbm.example/db/producttypeproduct/productType.$1#record")
+  ))
+
   test("bsbm1") {
     val sparqlParser = Sparql()
     val queryStr = """
@@ -544,10 +557,6 @@
 ORDER BY ?label
 LIMIT 10
 """
-    .replaceAll("<http://www4.wiwiss.fu-berlin.de/bizer/bsbm/v01/instances/ProductFeature([0-9]+)>",
-		"<http://bsbm.example/db/productfeatureproduct/productFeature.$1#record>")
-    .replaceAll("<http://www4.wiwiss.fu-berlin.de/bizer/bsbm/v01/instances/ProductType([0-9]+)>",
-		"<http://bsbm.example/db/producttypeproduct/productType.$1#record>")
 
     val bsbmQuery = sparqlParser.parseAll(sparqlParser.select, queryStr).get
 
@@ -586,7 +595,8 @@
 ORDER BY R_product.label
 LIMIT 10
 """).get
-    val asStem = SparqlToSparql(bsbmQuery, List(db2bsbm))
+
+    val asStem = SparqlToSparql(bsbmQuery, List(SparqlMap(db2bsbm, bsbmPatternMap)))
     assert(stemExpected === asStem)
     val (asSql, _) = SparqlToSql(bsbmDb, asStem, StemURI("http://bsbm.example/db/"), false, false)
     assert(sqlExpected === asSql)