
RDF-to-RDF and RDF-to-relational query transformation in SWObjects.
This work is licensed under a
Creative Commons Attribution 3.0 License,
with attribution to W3C.
trivial to dump RDB as RDF
test: is the graph query coherent (do generated node identifiers merge)?
Sociopathic RDF graph
?who mydb:fn ?name
Need interface graph
?who foaf:name ?name
The charter says to map RDBs to RDF/OWL.
clear algebra for:
| Patterns | Modifiers | Query Forms |
|---|---|---|
| RDF terms | DISTINCT | SELECT |
| triple patterns | REDUCED | CONSTRUCT |
| Basic graph patterns | PROJECT | DESCRIBE |
| Groups | ORDER BY | ASK |
| OPTIONAL | LIMIT | |
| UNION | OFFSET | |
| GRAPH | ||
| FILTER |
| Graph Pattern | Solution Modifiers |
|---|---|
| BGP | ToList |
| Join | OrderBy |
| LeftJoin | Project |
| Filter | Distinct |
| Union | Reduced |
| Graph | Slice |
| Graph Pattern | Solution Modifiers | Evaluation |
|---|---|---|
| ProjectAlias | MultiSet | NULL |
| TableAlias | OrderBy | |
| Join | Project | |
| LeftJoin | Distinct | |
| Constraint | Reduced | |
| Union | offset/limit |
RelSchema => StemMapping
StemMapping(stemURI, RelData) => StemGraph
StemMapping(stemURI, RelData) = TupleMapping1(stemURI, Table1) + TupleMapping2(stemURI, Table2)...
LexicalMap(string) => string LexicalMap(numeric) => lexical form of numberic
LiteralMap(string) => RDFLiteral(LexicalMap(string), xsd:String) LiteralMap(int) => RDFLiteral(LexicalMap(int), xsd:integer) LiteralMap(float) => RDFLiteral(LexicalMap(float), xsd:float)
S = <stemURI '/' Table '/' PrimaryKey(Table) '.' LexicalMap(Value(PrimaryKey(Table))) '#record' >
P = <stemURI '/' Table '#' Attribute >
O = attribute a FK to T2 ? <stemURI '/' T2 '/' PrimaryKey(T2) '.' LiteralMap(Value(PrimaryKey(T2))) '#record' >
: LiteralMap(Value(Attribute)test: does the StemGraph meet the same use cases as the relational data?
RelQuery(StemQuery) = set(AliasedJoin) + set(equivs)
(Rel, Attr) = match(P, stemURI + '/' + (\w+) + '#' (\w+))
some Alias = hash(Rel + S)
join.insert(Rel AS Alias)
pk = primary_key_attribute(Rel)
match S with
VAR -> if (bindings[S]) equivs.insert(Alias.pk= bindings[S])
bindings[S] = Alias.pk
URI -> (=Rel, PkAttr, PkValue) = match(S, stemURI + '/' (\w+) '/' (\w+) '.' (\w+) '#record')
some PkAlias = hash(PkRel + S)
joins.insert(PkRel AS PkAttr)
=pk = primary_key_attribute(Rel)
equivs.insert(PkAlias.PkPk= PkValue)
match O with
LITERAL -> equivs.insert(alias.attr= O)
VAR -> if (bindings[O]) equivs.insert(Alias.Attr= bindings[O])
bindings[O] = Alias.Attr
URI -> (FkRel, FkAttr, FkValue) = match(S, stemURI + '/' (\w+) '/' (\w+) '.' (\w+) '#record')
some FkAlias = hash(FkRel + O)
joins.insert(FkRel AS FkAttr)
FkPk = primary_key_attribute(FkRel)
equivs.insert(FkAlias.FkPk= FkValue)
equivs.insert(Alias.Attr= FkValue)test: does RelQuery(RelData) == StemQuery(StemGraph)?
VARconstraint(VAR, FQAttribute): if bindings[VAR]) equivs.insert(FQAttribute= bindings[VAR] bindings[VAR]= FQAttribute URIconstraint(URI) => Value: (Rel, Attr, Value) = match(URI, stemURI + '/' (\w+) '/' (\w+) '.' (\w+) '#record') some Alias = hash(Rel + URI) joins.insert(Rel AS Attr) pk = primary_key_attribute(Rel) equivs.insert(Alias.pk= Value) Value (Rel, Attr) = match(P, stemURI + '/' + (\w+) + '#' (\w+)) some Alias = hash(Rel + S) join.insert(Rel AS Alias) pk = primary_key_attribute(Rel) match S with VAR -> VARconstraint(S, Alias.pk) URI -> URIconstraint(S) match O with LITERAL -> equivs.insert(alias.attr= O) VAR -> VARconstraint(O, Alias.Attr) URI -> equivs.insert(Alias.Attr= URIconstraint(O))
| id | manager | section |
|---|---|---|
| 1 | 4 | sales |
| 4 | NULL | exec |
Emp:id=1 emp:id 1 .
Emp:id=1 emp:manager Emp:id=4 .
Emp:id=1 emp:section "sales" .
Emp:id=4 emp:id 4 .
Emp:id=4 emp:manager "exec" .
| employee | lead | name |
|---|---|---|
| 1 | 4 | widgets |
| 4 | 5 | widgets |
_:t1 task.employee Emp:id=1 . _:t1 task.lead Emp:id=2 . _:t1 task.name "widgets" . _:t2 task.employee Emp:id=4 . _:t2 task.lead Emp:id=5 . _:t2 task.name "widgets".
SELECT ?sec ?name
WHERE { GRAPH<HQ> { ?flunky emp:section ?sec OPTIONAL { ?flunky emp:manager ?boss } }
GRAPH<Sales> { _:t task:employee ?flunky; task:lead ?boss; task:name ?task } }
| flunky | boss | sec | task |
|---|---|---|---|
| Emp:id=1 | Emp:id=4 | "sales" | "widgets" |
| Emp:id=4 | "exec" | "widgets" |
| flunky | boss | sec | task |
|---|---|---|---|
| 1 | 4 | sales | widgets |
CONSTRUCT { ?x shop:name ?name ; shop:price ?price }
WHERE { ?x db:name ?name ; db:price ?price }
SELECT ?name
WHERE { <...Items/id=5#record> shop:name ?name ; shop:price ?price }
SELECT id_5.nm AS name FROM Items AS id_5 WHERE id_5.id=5
SELECT id_5.nm AS name FROM Items AS id_5 WHERE id_5.id=5 AND id_5.price IS NOT NULL
CONSTRUCT { ?x a shop:SaleItem ; shop:name ?name ; shop:price ?price }
WHERE { ?x db:name ?name ; db:price ?price ; db:discount ?discount
FILTER (?discount > 0.0) }
SELECT ?cost
WHERE { <...Items/id=5#record> a shop:SaleItem ;
shop:name ?name ;
shop:price ?price }
SELECT id_5.nm AS name FROM Items AS id_5 WHERE id_5.id=5
SELECT id_5.nm AS name FROM Items AS id_5 WHERE id_5.id=5 AND id_5.discount > 0
CONSTRUCT { ?x a shop:SaleItem ; ?x shop:name ?name ; shop:price ?price }
WHERE { ?x db:name ?name { ?x db:price ?price ; db:discount ?discount } }Useful for:
What requirements to we have?
requirements feedback through:
| system | expressivity | |
|---|---|---|
| Virtuoso | d2r/DDL | |
| Triplify | SQL->app? | |
| D2R | d2r | |
| Metatomix | GUI | |
| Ultrawrap | DDL | |
| Oracle | DDL | |
| SWObjects | CONSTRUCT | |
| OKKAM | ??? |
