project-m36 icon indicating copy to clipboard operation
project-m36 copied to clipboard

Recursive relationships

Open jmatsushita opened this issue 3 years ago β€’ 10 comments

Hi there πŸ‘‹

I'm trying to work with recursive relationships.

TutorialD (master/main): rec := relation{tuple{src 0, tgt 1}, tuple{src 1, tgt 2}, tuple{src 2, tgt 3}}
TutorialD (master/main): :showexpr rec
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚src::Integerβ”‚tgt::Integerβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚0           β”‚1           β”‚
β”‚1           β”‚2           β”‚
β”‚2           β”‚3           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

It seems to work ok for a depth 1 traversal:

TutorialD (master/main): :showexpr ((rec where src=0) join (rec rename {tgt as tgt2, src as tgt}))
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚src::Integerβ”‚tgt::Integerβ”‚tgt2::Integerβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚0           β”‚1           β”‚2            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Also if I add an edge:

TutorialD (master/main): insert rec relation{tuple{src 1, tgt 4}}
TutorialD (master/main): :showexpr rec
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚src::Integerβ”‚tgt::Integerβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚1           β”‚4           β”‚
β”‚2           β”‚3           β”‚
β”‚1           β”‚2           β”‚
β”‚0           β”‚1           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
TutorialD (master/main): :showexpr ((rec where src=0) join (rec rename {tgt as tgt2, src as tgt}))
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚src::Integerβ”‚tgt::Integerβ”‚tgt2::Integerβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚0           β”‚1           β”‚2            β”‚
β”‚0           β”‚1           β”‚4            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

I can work out a way to do a depth 2 traversal:

TutorialD (master/main): :showexpr ((rec where src=0) join (rec rename {tgt as tgt2, src as tgt}) join (rec rename {tgt as tgt3, src as tgt2}))
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚src::Integerβ”‚tgt::Integerβ”‚tgt2::Integerβ”‚tgt3::Integerβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚0           β”‚1           β”‚2            β”‚3            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Can this be generalised to a depth n traversal? Also would it be possible to group the result by depth?

Thanks for your time!

Jun

jmatsushita avatar Jul 30 '22 15:07 jmatsushita

Also unfortunately:

TutorialD (master/main): foreign key src_tgt rec{src} in rec{tgt}
ERR: RelationTypeMismatchError attributesFromList [(Attribute "src" IntegerAtomType)] attributesFromList [(Attribute "tgt" IntegerAtomType)]

UPDATE: I think this is non-sensical though πŸ˜…

jmatsushita avatar Jul 30 '22 15:07 jmatsushita

Ok what I was really trying to do was:

TutorialD (master/main): nod := relation{tuple{nid 0, lbl "a"}}
TutorialD (master/main): insert nod relation{tuple{nid 1, lbl "b"}}
TutorialD (master/main): :showexpr nod
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚lbl::Textβ”‚nid::Integerβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚"b"      β”‚1           β”‚
β”‚"a"      β”‚0           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
TutorialD (master/main): foreign key src_nod (rec rename {src as nid}){nid} in nod{nid}
ERR: InclusionDependencyCheckError "src_nod" Nothing
TutorialD (master/main): insert nod relation{tuple{nid 2, lbl "c"}}
TutorialD (master/main): insert nod relation{tuple{nid 3, lbl "d"}}
TutorialD (master/main): insert nod relation{tuple{nid 4, lbl "e"}}
TutorialD (master/main): foreign key src_nod (rec rename {src as nid}){nid} in nod{nid}
TutorialD (master/main): foreign key tgt_nod (rec rename {tgt as nid}){nid} in nod{nid}

Works nicely

jmatsushita avatar Jul 30 '22 15:07 jmatsushita

Oh wow, actually this does work if I make the root loop on itself (tuple {src 0, tgt 0}) and map src and tgt attributes to another name x:

TutorialD (master/main): rec := relation{tuple{src 0, tgt 0},tuple{src 0, tgt 1}, tuple{src 1, tgt 2}, tuple{src 2, tgt 3}}
TutorialD (master/main): foreign key src_tgt (rec rename {src as x}){x} in (rec rename {tgt as x}){x}

jmatsushita avatar Jul 30 '22 15:07 jmatsushita

That's a clever workaround. I suppose it wouldn't hurt to make a utility wrapper command to make this pattern more convenient. Do you have some syntax in mind?

By the way, the foreign key syntax is itself a convenience wrapper around inclusion dependencies which have been proven to be able to represent any database constraint.

agentm avatar Jul 31 '22 16:07 agentm

I've also been thinking about how to represent n-ary, nested relationships within a relation outside the scope of adjacency lists since we could leverage nested relations. However, the top-level relation type does not seem to be able to represent arbitrarily-nested relation types, so I don't see off-hand how to represent arbitrary levels of nesting.

agentm avatar Jul 31 '22 16:07 agentm

Thanks for the feedback πŸ™ I'll try to think about a syntax for traversing recursive relationships. Datalog might be an inspiration ? I'm very interested in modeling n-ary relations but I don't quite visualise how to do it with a nested relation even for the arity 2 case. Could you give an example?

jmatsushita avatar Aug 02 '22 15:08 jmatsushita

Sort of broadening the scope quite a bit here, but I was wondering what are principled approaches to model graphs in a relation database. This seems like a very interesting paper on implementing graph algorithms in RDBMSes using new operations and a while syntax.

https://dsl.cds.iisc.ac.in/~course/TIDS/papers/allinone.pdf

To support graph processing, in this work, we propose 4 new relational algebra operations, MM- join, MV-join, anti-join, and union-by-update.

jmatsushita avatar Aug 02 '22 18:08 jmatsushita

Thanks for linking to that paper- it proposes new SQL syntax to support said traversals. However, it is still limited by the capabilities of SQL (which Project:M36 is not). For example, Project:M36 supports algebraic data types and relation-valued attributes, both of which can be leveraged to represent graph more effectively.

For example, relation-valued attributes would be a great way to query a database to service an ORM. Here's a sample dashboard using the C.J. Date example. This would typically done by an outer join in SQL, but the Project:M36 representation is quite natural:

:showexpr (s join sp){p#,qty,s#,sname} group ({p#,qty} as parts)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚parts::relation {p#::Text,qty::Integer}β”‚s#::Textβ”‚sname::Textβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚"S4"    β”‚"Clark"    β”‚
β”‚β”‚p#::Textβ”‚qty::Integerβ”‚                β”‚        β”‚           β”‚
β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                β”‚        β”‚           β”‚
β”‚β”‚"P4"    β”‚300         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P5"    β”‚400         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P2"    β”‚200         β”‚                β”‚        β”‚           β”‚
β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚        β”‚           β”‚
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚"S1"    β”‚"Smith"    β”‚
β”‚β”‚p#::Textβ”‚qty::Integerβ”‚                β”‚        β”‚           β”‚
β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                β”‚        β”‚           β”‚
β”‚β”‚"P4"    β”‚200         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P5"    β”‚100         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P6"    β”‚100         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P3"    β”‚400         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P1"    β”‚300         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P2"    β”‚200         β”‚                β”‚        β”‚           β”‚
β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚        β”‚           β”‚
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚"S3"    β”‚"Blake"    β”‚
β”‚β”‚p#::Textβ”‚qty::Integerβ”‚                β”‚        β”‚           β”‚
β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                β”‚        β”‚           β”‚
β”‚β”‚"P2"    β”‚200         β”‚                β”‚        β”‚           β”‚
β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚        β”‚           β”‚
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                β”‚"S2"    β”‚"Jones"    β”‚
β”‚β”‚p#::Textβ”‚qty::Integerβ”‚                β”‚        β”‚           β”‚
β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                β”‚        β”‚           β”‚
β”‚β”‚"P2"    β”‚400         β”‚                β”‚        β”‚           β”‚
β”‚β”‚"P1"    β”‚300         β”‚                β”‚        β”‚           β”‚
β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                β”‚        β”‚           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

We could consider some syntax addition to TutorialD which would generate graph traversals using RVAs, but I'm not yet sure what that would look like.

agentm avatar Aug 15 '22 17:08 agentm

I encounter this issue too when I want to record tree-like comments.

I found an article talking about tree structure in sql (in Chinese). Tree structure in SQL

Basically, it says there are four ways to do that:

Adjacency List: easiest to implement and understand Path Enumeration: convenient when paths are needed Nested Sets: good for query frequently but modify occasionally. Closure Table: flexible, needs extra space.

YuMingLiao avatar Dec 09 '23 03:12 YuMingLiao

There's actually a more natural way to represent graphs in the relational algebra which I have not seen before. It's not quite possible in Project:M36 because we don't support recursive data types, but it should be possible to support in the future.

Here's an example that works today that hints at what could be possible:

TutorialD (master/main): :showexpr relation{tuple{val 4, children relation{tuple{val 6,children relation{tuple{}}}}},                   tuple{val 10, children relation{tuple{val 1, children relation{tuple{}}},                                                   tuple{val 2, children relation{tuple{}}}}}}
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚children::relation {children::relation {},val::Integer}β”‚val::Integerβ”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚4           β”‚
β”‚β”‚children::relation {}β”‚val::Integerβ”‚                   β”‚            β”‚
β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                   β”‚            β”‚
β”‚β”‚β”Œβ”                   β”‚6           β”‚                   β”‚            β”‚
β”‚β”‚β”‚β”‚                   β”‚            β”‚                   β”‚            β”‚
β”‚β”‚β”œβ”€                   β”‚            β”‚                   β”‚            β”‚
β”‚β”‚β””β”˜                   β”‚            β”‚                   β”‚            β”‚
β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚            β”‚
β”‚β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚10          β”‚
β”‚β”‚children::relation {}β”‚val::Integerβ”‚                   β”‚            β”‚
β”‚β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                   β”‚            β”‚
β”‚β”‚β”Œβ”                   β”‚2           β”‚                   β”‚            β”‚
β”‚β”‚β”‚β”‚                   β”‚            β”‚                   β”‚            β”‚
β”‚β”‚β”œβ”€                   β”‚            β”‚                   β”‚            β”‚
β”‚β”‚β””β”˜                   β”‚            β”‚                   β”‚            β”‚
β”‚β”‚β”Œβ”                   β”‚1           β”‚                   β”‚            β”‚
β”‚β”‚β”‚β”‚                   β”‚            β”‚                   β”‚            β”‚
β”‚β”‚β”œβ”€                   β”‚            β”‚                   β”‚            β”‚
β”‚β”‚β””β”˜                   β”‚            β”‚                   β”‚            β”‚
β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

If we could define a recursive type for the top-level relation, then we could conceivably enable unlimited levels of nested structures using nested relations. This would allow us, for example, to enable fine-grained constraints on the graph use normal database constraints.

This is certainly more natural than adjanceny lists and less error-prone than number range-based graphs structures.

agentm avatar Dec 14 '23 05:12 agentm