For the many-to-many case, we would want values in both dicts to be sets, populated with one element when the key is added first, and removed when the last mapping for the key is removed.
For the ternary relation case, we would generally map each index to a tuple corresponding to each recordset, then return the tuple or a slice when queried. This gives us a more general form.
R = Relation( )
R( A, B, C )
These will return the entire recordset, that is, participant in the relation.
x= R[ 0 ][ A ]
x= R[ 1 ][ B ]
They are equivalent to selecting ‘WHERE column0 = A’ and ‘WHERE column1 = B’; and they return their entire tuple. To get C, we would use
x= R[ 0 ][ A ][ 2 ]
or ‘SELECT column2 FROM r WHERE column0 is A’. This gets us a list with one element, so if we know we only have one return, we can declare that fact about the second field when we declare R, or just use the ‘.one’ descriptor or the ‘[ 0 ]’ index.
x= R[ 0 ][ A ][ 2 ].one
x= R[ 0 ][ A ][ 2 ][ 0 ]
My idea here is that we want to be able to use Python equality. I also want rich comparison, which means that some of the mapping types must be sequences, maxing-out at log-N search and keeping sorted order. Ideally, we would keep a hash and a tree for every field, attempting hash lookups first on equality, defaulting to b-tree lookups for failed hash tests and non-equality comparisons, as well as distinguish between equality lookups and identity selections and lookups. For completeness, we could include the unsorted unhashed collection too: linear. However, if we are testing anything but identity, we can’t use getitem syntax; we’ll have to call a method. I also want to fulfill the native mapping interface and additionally the sequence interface for an additional field option.
We have not addressed the issue of return type. Where it’s known that one field is necessarily unique, we want to return an item, not a sequence; and a sequence otherwise.
Let’s look at a generic parent-children structure. We’re not assuming there’s only one graph; it could be disjoint while still participating in R. We want to define elements like this:
def parent( self ):
return self.R.where child is self
We could build the relation into the class, as with a subclass,
class SomethingSpecific( Something ):
R = Relation( )
so ‘self.R’ would succeed (even though R is in the child class!), or use an initializer.
def __init__( self, R ).
My favorite slick idea is to use a string to execute our where clauses, and compile it using the runtime language service. It has to be a valid Python string though we get to do anything we want with identifiers. Or we could eval it in a custom context. Basically we’re freed from the And( Or( ), Or( ) ) construct; we get ‘( a or b ) and ( c or d )’.
For an example of a ternary relation, we can look at the parent-children tree, adding order is important among children.
Other examples are an ordered dict (3-way with sequence), a thread-to-lock association (many-many), xml parent-children, and Panda’s scene graph (3-way, with path IDs).