package synchronizator import ( "fmt" "slices" ) type StandardNode interface { GetClass() string GetName() string GetMetadata() []byte AddRelationship(relationship StandardRelationship) error AddRelationships(to int64, relationship []StandardRelationship) SetId(int64) SetConnection(*DB) } // A node in the database. // It adds some helper methods to easily manage the node. type Node struct { _conn *DB // Underlaying connection to the database. _class string // The class of the node, should not be modified to avoid inconsistencies. _relationships []StandardRelationship // Relationships of the node Id int64 // The id of the node name string // The name of the node metadata []byte // Arbitrary data. This is stored as a jsonb in the database } func NewNode(name string, metadata []byte) *Node { return &Node{ name: name, _class: "NODE", metadata: metadata, Id: -1, // Use -1 to indicate not persisted } } func (node *Node) GetClass() string { return node._class } func (node *Node) GetName() string { return node.name } func (node *Node) GetMetadata() []byte { return node.metadata } func (node *Node) AddRelationship(relationship StandardRelationship) error { node1, node2 := relationship.GetNodes() if node1 != node.Id && node2 != node.Id { return fmt.Errorf( "The current node (%v) is neither used as the source (%v), nor the destination (%v)", node.Id, node1, node2, ) } node._relationships = append(node._relationships, relationship) return nil } func (node *Node) AddRelationships(to int64, relationships []StandardRelationship) { node._relationships = slices.Concat(node._relationships, relationships) } func (node *Node) SetId(id int64) { node.Id = id } func (node *Node) SetConnection(conn *DB) { node._conn = conn } // Creates a new relation of type StandardRelationship to the node with the // provided id. An error is returned if the relation already exists. // // This method is a wrapper around the AddRelation method of the connection. func (node *Node) StoreRelation(relation StandardRelationship, to int64) (*Relationship, error) { return node._conn.AddRelation(node.Id, relation, to) } // Update a relation with the node of the provided id. // // This method is a wrapper around the UpdateRelation method of the connection. func (node *Node) UpdateRelation(metadata any, to int64) error { return node._conn.UpdateRelation(node.Id, metadata, to) } // Delete a relation with the node of the provided id. // // This method is a wrapper around the DeleteRelation method of the connection. func (node *Node) DeleteRelation(to int64) error { return node._conn.DeleteRelation(node.Id, to) } // Fetch all the outgoing relations for this node. This method will return a // slice of relationships pointers and also store them in the node for further // use. func (node *Node) GetOutRelations() ([]StandardRelationship, error) { sql := ` WITH RECURSIVE NodeRelationships AS ( SELECT *, relationships._class AS relationship_class, relationships.metadata AS relationship_metadata FROM nodes as src JOIN relationships ON src.id = relationships.node_from WHERE src.id = $1 ) SELECT NodeRelationships.id as src_node, NodeRelationships.relationship_class, NodeRelationships.relationship_metadata, dst.id as dst_id FROM NodeRelationships JOIN nodes as dst ON dst.id = NodeRelationships.node_to ` rows, err := node._conn.Query(sql, node.Id) if err != nil { return nil, err } defer rows.Close() node._relationships = make([]StandardRelationship, 0) for rows.Next() { relationship := &Relationship{ _conn: node._conn, } if err := rows.Scan(&relationship.From, &relationship._class, relationship.GetMetadata(), &relationship.To); err != nil { return nil, err } node._relationships = append(node._relationships, relationship) } return node._relationships, nil } // Deletes this node from the database. // This method is a wrapper around the DeleteNode method of the connection. func (node *Node) Delete() error { return node._conn.DeleteNode(node.Id) }