I'm implementing in terms of SQLAlchemy a structure that has the mathematical characteristic of Partially Ordered Set, in which I need to be able to add and remove edges one at a time.
In my current, best design, I use two adjacency lists, one being the assignment list (approximately edges in the Hass Diagram), since I need to preserve which pairs of nodes are explicitly set as ordered, and the other adjacency list is the transitive closure of the first, so that I can efficiently query if one node is ordered with respect to another. Right now, I recompute the transitive closure each time an edge is added to or removed from the assignment adjacency list.
It looks something like this:
assignment = Table('assignment', metadata,
Column('parent', Integer, ForeignKey('node.id')),
Column('child', Integer, ForeignKey('node.id')))
closure = Table('closure', metadata,
Column('ancestor', Integer, ForeignKey('node.id')),
Column('descendent', Integer, ForeignKey('node.id')))
class Node(Base):
__tablename__ = 'node'
id = Column(Integer, primary_key=True)
parents = relationship(Node, secondary=assignment,
backref='children',
primaryjoin=id == assignment.c.parent,
secondaryjoin=id == assignment.c.child)
ancestors = relationship(Node, secondary=closure,
backref='descendents',
primaryjoin=id == closure.c.ancestor,
secondaryjoin=id == closure.c.descendent,
viewonly=True)
@classmethod
def recompute_ancestry(cls.conn):
conn.execute(closure.delete())
adjacent_values = conn.execute(assignment.select()).fetchall()
conn.execute(closure.insert(), floyd_warshall(adjacent_values))
where floyd_warshall()
is an implementation of the algorithm by the same name.
This is leading me to two problems. The first is that It doesn't seem to be very efficient, but I'm not sure of what sort of algorithm I could use instead.
The second is more about the practicality of having to explicitly call Node.recompute_ancestry()
each time an assignment occurs, and only after the assignments are flushed into the session and with the proper connections. If I want to see the changes reflected in the ORM, I'd have to flush the session again. It would be much easier, I think, If I could express the recompute ancestry operation in terms of the orm.