def write_relation(self, relation: Relationship) -> Relationship: """ Writes relation to file. :param relation: - relation written """ if relation.next_prop is not None: relation.next_prop = self.write_property(relation.next_prop) if relation.id == cfg.INV_ID: relation.id = self._get_relation_id() relation.label = self.write_label(relation.label) if relation.first_node is not None: first_node = self.read_node(relation.first_node.id) if first_node.next_rel is None: first_node.next_rel = relation self.write_node(first_node) else: relation = self._update_next_rel(first_node, relation) if relation.second_node is not None: second_node = self.read_node(relation.second_node.id) if second_node.next_rel is None: second_node.next_rel = relation self.write_node(second_node) else: relation = self._update_next_rel(second_node, relation) value = Packer.pack_relation(relation) self._write_bytes(self.relations, relation.id * cfg.RELATION_SIZE, value) return relation
def _swap_relation_pointer(self, next: Relationship, prev: Relationship): """ Swaps pointer of two relations. Helper function for deletion of relation :param next: :param prev: :return: """ if prev.second_node == next.second_node: prev.second_next_rel = next.id next.second_prev_rel = prev.id elif prev.first_node == next.first_node: prev.first_next_rel = next.id next.first_prev_rel = prev.id elif prev.first_node == next.second_node: prev.first_next_rel = next.id next.second_prev_rel = prev.id elif prev.second_node == next.first_node: prev.second_next_rel = next.id next.first_prev_rel = prev.id else: raise Exception('No same nodes to swap') second_prev_rel_packed = Packer.pack_relation(prev) second_next_rel_packed = Packer.pack_relation(next) self._write_bytes(self.relations, next.id * cfg.RELATION_SIZE, second_next_rel_packed) self._write_bytes(self.relations, prev.id * cfg.RELATION_SIZE, second_prev_rel_packed)
def _fix_prev_rel(self, current: Relationship, prev_rel: Relationship): """ Sets prev_rel next pointer to INV_ID :param current: :param prev_rel: :return: """ if (current.second_node == prev_rel.second_node): prev_rel.second_next_id = cfg.INV_ID elif (current.second_node == prev_rel.first_node): prev_rel.first_next_id = cfg.INV_ID prev_packed = Packer.pack_relation(prev_rel) self._write_bytes(self.relations, prev_rel.id * cfg.RELATION_SIZE, prev_packed)
def _update_next_rel(self, node: Node, relation: Relationship): next_rel = self.read_relation(node.next_rel) node.next_rel = relation.id if next_rel.second_node.id == node.id: next_rel.second_prev_rel = relation.id if next_rel.first_node.id == node.id: next_rel.first_prev_rel = relation.id self.write_node(node) value = Packer.pack_relation(next_rel) self._write_bytes(self.relations, next_rel.id * cfg.RELATION_SIZE, value) if relation.first_node.id == node.id: relation.first_next_rel = next_rel if relation.second_node.id == node.id: relation.second_next_rel = next_rel return relation
def test_del_relation(self): io = Io() value = "You spin my head right ground right ground" label = Label(cfg.INV_ID, value) label = io.write_label(label) prop_value = True property = Property(cfg.INV_ID, PropertyType.BOOL, label, prop_value, None) property = io.write_property(property) node = Node(cfg.INV_ID, label, property, None) node2 = Node(cfg.INV_ID, label, property, None) written1 = io.write_node(node) written2 = io.write_node(node2) relation = Relationship(cfg.INV_ID, False, written1, written2, label, property, None, None, None, None) written = io.write_relation(relation) io.del_relation(written.id) retrived = io.get_relations_by_id([written.id]) node1 = io.read_node(written1.id) node2 = io.read_node(written2.id) self.assertEqual(len(retrived), 0) print(node1.next_rel) print(node2.next_rel) self.assertEqual(node1.next_rel is None, True) self.assertEqual(node2.next_rel is None, True)
def test_relation_io(self): io = Io() value = "You spin my head right ground right ground" label = Label(cfg.INV_ID, value) label = io.write_label(label) prop_value = True property = Property(cfg.INV_ID, PropertyType.BOOL, label, prop_value, None) property = io.write_property(property) node = Node(cfg.INV_ID, label, property, None) node2 = Node(cfg.INV_ID, label, property, None) written1 = io.write_node(node) written2 = io.write_node(node2) relation = Relationship(cfg.INV_ID, False, written1, written2, label, property, None, None, None, None) written = io.write_relation(relation) retrieved = io.read_relation(written.id) written1 = io.read_node(written1.id) written2 = io.read_node(written2.id) self.assertEqual(retrieved.label.value, value) self.assertEqual(retrieved.next_prop.value, prop_value) self.assertEqual(retrieved.first_node.id, written1.id) self.assertEqual(retrieved.second_node.id, written2.id) self.assertEqual(written2.next_rel, retrieved.id) self.assertEqual(written1.next_rel, retrieved.id)
def test_multirelation_deletion(self): io = Io() value = "You spin my head right ground right ground" label = Label(cfg.INV_ID, value) label = io.write_label(label) prop_value = True property = Property(cfg.INV_ID, PropertyType.BOOL, label, prop_value, None) property = io.write_property(property) node = Node(cfg.INV_ID, label, property, None) node2 = Node(cfg.INV_ID, label, property, None) written_node1 = io.write_node(node) written_node2 = io.write_node(node2) relation1 = Relationship(cfg.INV_ID, False, written_node1, written_node2, label, property, None, None, None, None) relation2 = Relationship(cfg.INV_ID, False, written_node1, written_node2, label, property, None, None, None, None) written_rel1 = io.write_relation(relation1) written_rel2 = io.write_relation(relation2) io.del_node(written_node1.id) retrived = io.get_relations_by_id([written_rel1.id, written_rel2.id]) self.assertEqual(len(retrived), 0)
def read_relation(self, id) -> Relationship or None: """ Reads single relation by it's id :param id: offset of relation to read :return: Relation unpacked """ if id > self.last_relation_id: raise Exception('Relation ID is out of range') relation_bytes = self._read_bytes(self.relations, id, cfg.RELATION_SIZE) in_use, type, first_node, second_node, label, property, first_prev_relation, first_next_relation, \ second_prev_relation, second_next_relation = Unpacker.unpack_relation(relation_bytes) if in_use: label = self.read_label(label) if property != cfg.INV_ID: property = self.read_property(property) else: property = None if first_node != cfg.INV_ID: first_node = self.read_node(first_node) else: first_node = None if second_node != cfg.INV_ID: second_node = self.read_node(second_node) else: second_node = None if first_next_relation == cfg.INV_ID: first_next_relation = None if second_next_relation == cfg.INV_ID: second_next_relation = None if first_prev_relation == cfg.INV_ID: first_prev_relation = None if second_prev_relation == cfg.INV_ID: second_prev_relation = None return Relationship(id, type, first_node, second_node, label, property, first_prev_relation, first_next_relation, second_prev_relation, second_next_relation) else: return None
def create_relationship(self, first_node, second_node, relationship): """ Creates new relationships between nodes that match 'first_node' and 'second_node'. :param first_node: A pair (<label>, <properties>) corresponding to the node at the start of a relationship. :param second_node: A pair (<label>, <properties>) corresponding to the node at the end of a relationship. :param relationship: A triplet (<label>, <properties>, <direction>) corresponding to a relationship. :return: Void """ label, next_prop, direction = relationship label = self._dedupe_label(label) next_prop = self._dedupe_next_prop(next_prop) is_directed = False if direction == 1: is_directed = True elif direction == -1: is_directed = True first_node, second_node = second_node, first_node retrieved_first_node = self._retrieve_node(first_node) retrieved_second_node = self._retrieve_node(second_node) for first in retrieved_first_node: for second in retrieved_second_node: created_relationship = self._io.write_relation( Relationship(cfg.INV_ID, is_directed, first, second, label, next_prop, None, None, None, None)) label, next_prop = created_relationship.label, created_relationship.next_prop # Add new labels to the corresponding cache self._lb_cache[label.value] = label while next_prop is not None: self._lb_cache[next_prop.label] = next_prop.label next_prop = next_prop.next_prop