--- a/directmapping/src/main/scala/DirectMapping.scala Sat Feb 12 22:48:05 2011 -0500
+++ b/directmapping/src/main/scala/DirectMapping.scala Sat Feb 12 23:57:45 2011 -0500
@@ -37,14 +37,14 @@
* ∀ r:Relation, ∀ ck:CandidateKey, ck ∊ Relation, ∀ t1:Tuple, ∀ t2:Tuple,
* t1 ≠ t2 -> l1 = t1(ck) -> l2 = t2(ck) -> l1 = l2 -> nodemap(r)(ck)(l1) ≠ nodemap(r)(ck)(l1)
*/
- type NodeMap = PartialFunction[RelName, KeyMap]
+ type NodeMap = PartialFunction[Relation, KeyMap]
/**
* dbToNodeMap builds the NodeMap making the tuple Node accessible to their candidate keys
* it's defined only for the indexable table, as we need to have at least one candidate key
*/
def dbToNodeMap(db:Database):NodeMap =
- db.indexables map { rn => rn -> keyMapForRelation(db(rn)) } toMap
+ db.indexables map { r => r -> keyMapForRelation(r) } toMap
/**
* given:
@@ -111,34 +111,53 @@
Graph(r.body flatMap { t => tupleSemantics(db, nodemap, r, t) })
def tupleSemantics (db:Database, nodemap:NodeMap, r:Relation, t:Tuple):Set[Triple] = {
- val s:Node =
+ val s:SubjectNode =
// look for the first candidate key if available
r.candidates.headOption match {
// if there is a candidate key, we know we can retrieve the mapped node
// null values are ok at that point
case Some(firstKey) => {
val cellvalues = t.cellvalues(firstKey)
- nodemap(r.name)(firstKey)(cellvalues)
+ val mappedNode = nodemap(r)(firstKey)(cellvalues)
+ SubjectNode(mappedNode)
}
// there is no candidate key, we have to come up with a new bnode
- case None =>
- NodeBNode(freshbnode())
+ case None => {
+ val freshnode = NodeBNode(freshbnode())
+ SubjectNode(freshnode)
+ }
}
- // the foreign keys create triples
- val triplesFromFKs = t.references(r) map { referenceSemantics(s, _, r, t, nodemap) }
- // the lexical values (ie. not null values) also create triples
- val triplesFromLexicalValues = t.scalars(r) flatMap { lexicalValueSemantics(r, s, _, t) }
+ val fromFKs = t.references(r) map { fk => referenceSemantics(r, nodemap, t, fk) }
+ //
+ val fromLexicalValues = t.scalars(r) flatMap { a => lexicalValueSemantics(r, t, a) }
// the relation provenance is mapped to an RDF type information, computed from the relation itself
- val triple = Triple(SubjectNode(s),
- PredicateIRI(IRI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")),
+ val fromRelation = (PredicateIRI(IRI("http://www.w3.org/1999/02/22-rdf-syntax-ns#type")),
ObjectNode(NodeIRI(IRI(UE(r)))))
- triplesFromFKs ++ triplesFromLexicalValues + triple
+ (fromFKs ++ fromLexicalValues + fromRelation) map { case (p, o) => Triple(s, p, o) }
}
/**
- *
+ * a foreign key contribute to generating triples
*/
- def lexicalValueSemantics(r:Relation, s:Node, a:AttrName, t:Tuple):Option[Triple] = {
+ def referenceSemantics (r:Relation, nodes:NodeMap, t:Tuple, fk:ForeignKey):(Predicate, Object) = {
+ val p = predicateSemantics (r, fk)
+ val cellvalues:List[CellValue] = t.cellvalues(fk)
+ val ForeignKey(as, target@Target(_, key)) = fk
+ val rel = target.r
+ if (!(nodes isDefinedAt rel))
+ error("No referent relation \"" + rel + "\" to match " + r.name + t)
+ if (!(nodes(rel) isDefinedAt key))
+ error("Relation " + rel + " has no attributes (" + key + ") to match " + r.name + t)
+ if (!(nodes(rel)(key) isDefinedAt cellvalues))
+ error("Relation " + rel + "(" + key + ") has no values " + cellvalues + " to match " + r.name + t)
+ val o:Object = ObjectNode(nodes(rel)(key)(cellvalues))
+ (PredicateIRI(p), o)
+ }
+
+ /**
+ * a lexical value contribute to generating triples (only if it is not null)
+ */
+ def lexicalValueSemantics(r:Relation, t:Tuple, a:AttrName):Option[(Predicate, Object)] = {
// a is implicitly promoted to an AttrList
val p = predicateSemantics(r, a)
val cellValue = t(a)
@@ -146,30 +165,17 @@
(cellValue, datatype) match {
case (LexicalValue(l), Datatype.STRING) => {
val o = PlainLiteral(l, None)
- Some(Triple(SubjectNode(s), PredicateIRI(p), ObjectLiteral(o)))
+ Some(PredicateIRI(p), ObjectLiteral(o))
}
case (LexicalValue(l), d) => {
val o = TypedLiteral(l, datatypeSemantics(d))
- Some(Triple(SubjectNode(s), PredicateIRI(p), ObjectLiteral(o)))
+ Some(PredicateIRI(p), ObjectLiteral(o))
}
case (␀, _) => None
}
+
}
- def referenceSemantics (s:Node, fk:ForeignKey, r:Relation, t:Tuple, nodes:NodeMap) : Triple = {
- val p = predicateSemantics (r, fk)
- val cellvalues:List[CellValue] = t.cellvalues(fk)
- val ForeignKey(as, Target(rel, key)) = fk
- if (!(nodes isDefinedAt rel))
- error("No referent relation \"" + rel + "\" to match " + r.name + t)
- if (!(nodes(rel) isDefinedAt key))
- error("Relation " + rel + " has no attributes (" + key + ") to match " + r.name + t)
- if (!(nodes(rel)(key) isDefinedAt cellvalues))
- error("Relation " + rel + "(" + key + ") has no values " + cellvalues + " to match " + r.name + t)
- val o:Object = ObjectNode(nodes(rel)(key)(cellvalues))
- Triple(SubjectNode(s), PredicateIRI(p), o)
- }
-
def predicateSemantics (r:Relation, as:AttrList) : IRI =
IRI(UE(r) + "#" + as.attrs.mkString("_"))
@@ -188,8 +194,7 @@
// These invariants make nodemap and predicateSemantics functions prettier.
def UE(s:String):String = s.replaceAll(" ", "+")
- def UE(rn:RelName):String = UE(rn.n)
- def UE(r:Relation):String = UE(r.name)
+ def UE(r:Relation):String = UE(r.name.n)
def UE(a:AttrName):String = UE(a.n)
def iri(rn:Relation, as:AttrList, ls:List[LexicalValue]):IRI = {
--- a/rdb/src/main/scala/RDB.scala Sat Feb 12 22:48:05 2011 -0500
+++ b/rdb/src/main/scala/RDB.scala Sat Feb 12 23:57:45 2011 -0500
@@ -9,12 +9,22 @@
* a Database maps a relation name to the actual Relation
*/
case class Database(private val m:Map[RelName, Relation]) extends PartialFunction[RelName, Relation] {
+ // tricky and evil
+ // when the Database is created, it can go through all the foreign key and set the database field
+ // this way, a Target can know directly the Relation without using Database
+ for {
+ (_, r) <- m
+ fk <- r.fks
+ } {
+ fk.target.db = Some(this)
+ }
+
def apply(rn:RelName) = m(rn)
def isDefinedAt(rn:RelName) = m isDefinedAt rn
/** returns all the relation names */
def relNames:Set[RelName] = m.keySet.toSet
/** return all the relations with at least on Candidate Key */
- def indexables = m collect { case (rn, r) if r.isIndexable => rn }
+ def indexables = m collect { case (rn, r) if r.isIndexable => r }
}
object Database {
@@ -33,6 +43,7 @@
candidates:List[CandidateKey],
pk:Option[CandidateKey],
fks:ForeignKeys) {
+
/** adds a tuple in the body of the relation */
def +(t:Tuple):Relation = this.copy(body = body :+ t)
/** a relation is indexable if it has at least one candidate key */
@@ -116,6 +127,7 @@
def unaryFKs:Set[ForeignKey] = fks filter { _.isUnary }
def definesActuallyUnaryFK(a:AttrName):Boolean = unaryFKs exists { _.attrs contains a }
def filter(p: ForeignKey => Boolean):Set[ForeignKey] = fks filter p
+ def foreach(f:ForeignKey => Unit) = fks foreach f
}
object ForeignKeys {
@@ -125,7 +137,12 @@
case class ForeignKey(attrs:List[AttrName], target:Target) extends AttrList
- case class Target(rel:RelName, key:CandidateKey)
+ case class Target(rel:RelName, key:CandidateKey) {
+ var db:Option[Database] = None
+ lazy val r:Relation = db.get(rel)
+ }
+
+
case class CandidateKey (attrs:List[AttrName]) extends AttrList