def save(self, subj, node=None): """ Save an object to a database node. :param subj: the object to save :param node: the database node to save to (if omitted, will re-save to same node as previous save) """ if node is not None: subj.__node__ = node # naively copy properties from object to node props = {} for key, value in subj.__dict__.items(): if not key.startswith("_"): props[key] = value if hasattr(subj, "__node__"): subj.__node__.set_properties(props) query = CypherQuery(self.graph, "START a=node({A}) " "MATCH (a)-[r]->(b) " "DELETE r") query.execute(A=subj.__node__._id) else: subj.__node__, = self.graph.create(props) # write rels if hasattr(subj, "__rel__"): batch = WriteBatch(self.graph) for rel_type, rels in subj.__rel__.items(): for rel_props, endpoint in rels: end_node = self._get_node(endpoint) if end_node not in self.graph: raise ValueError(end_node) batch.create((subj.__node__, rel_type, end_node, rel_props)) batch.run() return subj
class TestIndexedRelationshipAddition(object): @pytest.fixture(autouse=True) def setup(self, graph): try: graph.legacy.delete_index(Relationship, "Friendships") except LookupError: pass self.friendships = graph.legacy.get_or_create_index( Relationship, "Friendships") self.batch = LegacyWriteBatch(graph) self.graph = graph def test_can_add_single_relationship(self, graph): alice, bob, ab = self.graph.create({"name": "Alice"}, {"name": "Bob"}, (0, "KNOWS", 1)) self.batch.add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab) self.batch.run() # check entries rels = self.friendships.get("friends", "alice_&_bob") assert len(rels) == 1 assert ab in rels # done self.recycling = [ab, alice, bob] def test_can_add_two_similar_relationships(self, graph): alice, bob, ab1, ab2 = self.graph.create({"name": "Alice"}, {"name": "Bob"}, (0, "KNOWS", 1), (0, "KNOWS", 1)) self.batch.add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab1) self.batch.add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab2) self.batch.run() # check entries entries = self.friendships.get("friends", "alice_&_bob") assert len(entries) == 2 assert ab1 in entries assert ab2 in entries # done self.recycling = [ab1, ab2, alice, bob] def test_can_add_relationships_only_if_none_exist(self): alice, bob, ab1, ab2 = self.graph.create({"name": "Alice"}, {"name": "Bob"}, (0, "KNOWS", 1), (0, "KNOWS", 1)) self.batch.get_or_add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab1) self.batch.get_or_add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab2) results = self.batch.submit() assert results[0] == results[1] # check entries entries = self.friendships.get("friends", "alice_&_bob") assert len(entries) == 1 assert ab1 in entries # done self.recycling = [ab1, ab2, alice, bob]
class TestIndexedRelationshipAddition(object): @pytest.fixture(autouse=True) def setup(self, legacy_graph): try: legacy_graph.delete_index(Relationship, "Friendships") except LookupError: pass self.friendships = legacy_graph.get_or_create_index(Relationship, "Friendships") self.batch = LegacyWriteBatch(legacy_graph) self.graph = legacy_graph def test_can_add_single_relationship(self, legacy_graph): alice, bob, ab = self.graph.create({"name": "Alice"}, {"name": "Bob"}, (0, "KNOWS", 1)) self.batch.add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab) self.batch.run() # check entries rels = self.friendships.get("friends", "alice_&_bob") assert len(rels) == 1 assert ab in rels # done self.recycling = [ab, alice, bob] def test_can_add_two_similar_relationships(self, legacy_graph): alice, bob, ab1, ab2 = self.graph.create( {"name": "Alice"}, {"name": "Bob"}, (0, "KNOWS", 1), (0, "KNOWS", 1)) self.batch.add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab1) self.batch.add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab2) self.batch.run() # check entries entries = self.friendships.get("friends", "alice_&_bob") assert len(entries) == 2 assert ab1 in entries assert ab2 in entries # done self.recycling = [ab1, ab2, alice, bob] def test_can_add_relationships_only_if_none_exist(self): alice, bob, ab1, ab2 = self.graph.create( {"name": "Alice"}, {"name": "Bob"}, (0, "KNOWS", 1), (0, "KNOWS", 1)) self.batch.get_or_add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab1) self.batch.get_or_add_to_index(Relationship, self.friendships, "friends", "alice_&_bob", ab2) results = self.batch.submit() assert results[0] == results[1] # check entries entries = self.friendships.get("friends", "alice_&_bob") assert len(entries) == 1 assert ab1 in entries # done self.recycling = [ab1, ab2, alice, bob]
class TestIndexedNodeAddition(object): @pytest.fixture(autouse=True) def setup(self, graph): try: graph.legacy.delete_index(Node, "People") except LookupError: pass self.people = graph.legacy.get_or_create_index(Node, "People") self.batch = LegacyWriteBatch(graph) self.graph = graph def test_can_add_single_node(self): alice, = self.graph.create({"name": "Alice Smith"}) self.batch.add_to_index(Node, self.people, "surname", "Smith", alice) self.batch.run() # check entries smiths = self.people.get("surname", "Smith") assert len(smiths) == 1 assert alice in smiths # done self.graph.delete(alice) def test_can_add_two_similar_nodes(self): alice, bob = self.graph.create({"name": "Alice Smith"}, {"name": "Bob Smith"}) self.batch.add_to_index(Node, self.people, "surname", "Smith", alice) self.batch.add_to_index(Node, self.people, "surname", "Smith", bob) nodes = self.batch.submit() assert nodes[0] != nodes[1] # check entries smiths = self.people.get("surname", "Smith") assert len(smiths) == 2 assert alice in smiths assert bob in smiths # done self.graph.delete(alice, bob) def test_can_add_nodes_only_if_none_exist(self): alice, bob = self.graph.create({"name": "Alice Smith"}, {"name": "Bob Smith"}) self.batch.get_or_add_to_index(Node, self.people, "surname", "Smith", alice) self.batch.get_or_add_to_index(Node, self.people, "surname", "Smith", bob) nodes = self.batch.submit() assert nodes[0] == nodes[1] # check entries smiths = self.people.get("surname", "Smith") assert len(smiths) == 1 assert alice in smiths # done self.graph.delete(alice, bob)
class TestIndexedNodeAddition(object): @pytest.fixture(autouse=True) def setup(self, legacy_graph): try: legacy_graph.delete_index(Node, "People") except LookupError: pass self.people = legacy_graph.get_or_create_index(Node, "People") self.batch = LegacyWriteBatch(legacy_graph) self.graph = legacy_graph def test_can_add_single_node(self): alice, = self.graph.create({"name": "Alice Smith"}) self.batch.add_to_index(Node, self.people, "surname", "Smith", alice) self.batch.run() # check entries smiths = self.people.get("surname", "Smith") assert len(smiths) == 1 assert alice in smiths # done self.graph.delete(alice) def test_can_add_two_similar_nodes(self): alice, bob = self.graph.create( {"name": "Alice Smith"}, {"name": "Bob Smith"}) self.batch.add_to_index(Node, self.people, "surname", "Smith", alice) self.batch.add_to_index(Node, self.people, "surname", "Smith", bob) nodes = self.batch.submit() assert nodes[0] != nodes[1] # check entries smiths = self.people.get("surname", "Smith") assert len(smiths) == 2 assert alice in smiths assert bob in smiths # done self.graph.delete(alice, bob) def test_can_add_nodes_only_if_none_exist(self): alice, bob = self.graph.create( {"name": "Alice Smith"}, {"name": "Bob Smith"}) self.batch.get_or_add_to_index(Node, self.people, "surname", "Smith", alice) self.batch.get_or_add_to_index(Node, self.people, "surname", "Smith", bob) nodes = self.batch.submit() assert nodes[0] == nodes[1] # check entries smiths = self.people.get("surname", "Smith") assert len(smiths) == 1 assert alice in smiths # done self.graph.delete(alice, bob)
class TestIndexedNodeRemoval(object): @pytest.fixture(autouse=True) def setup(self, graph): self.graph = graph try: graph.legacy.delete_index(Node, "node_removal_test_index") except LookupError: pass self.index = graph.legacy.get_or_create_index( Node, "node_removal_test_index") self.fred, self.wilma, = graph.create( {"name": "Fred Flintstone"}, {"name": "Wilma Flintstone"}, ) self.index.add("name", "Fred", self.fred) self.index.add("name", "Wilma", self.wilma) self.index.add("name", "Flintstone", self.fred) self.index.add("name", "Flintstone", self.wilma) self.index.add("flintstones", "%", self.fred) self.index.add("flintstones", "%", self.wilma) self.batch = LegacyWriteBatch(graph) def check(self, key, value, *entities): e = self.index.get(key, value) assert len(entities) == len(e) for entity in entities: assert entity in e def test_remove_key_value_entity(self): self.batch.remove_from_index(Node, self.index, key="name", value="Flintstone", entity=self.fred) self.batch.run() self.check("name", "Fred", self.fred) self.check("name", "Wilma", self.wilma) self.check("name", "Flintstone", self.wilma) self.check("flintstones", "%", self.fred, self.wilma) def test_remove_key_entity(self): self.batch.remove_from_index(Node, self.index, key="name", entity=self.fred) self.batch.run() self.check("name", "Fred") self.check("name", "Wilma", self.wilma) self.check("name", "Flintstone", self.wilma) self.check("flintstones", "%", self.fred, self.wilma) def test_remove_entity(self): self.batch.remove_from_index(Node, self.index, entity=self.fred) self.batch.run() self.check("name", "Fred") self.check("name", "Wilma", self.wilma) self.check("name", "Flintstone", self.wilma) self.check("flintstones", "%", self.wilma)
class TestIndexedNodeRemoval(object): @pytest.fixture(autouse=True) def setup(self, legacy_graph): self.graph = legacy_graph try: legacy_graph.delete_index(Node, "node_removal_test_index") except LookupError: pass self.index = legacy_graph.get_or_create_index(Node, "node_removal_test_index") self.fred, self.wilma, = legacy_graph.create( {"name": "Fred Flintstone"}, {"name": "Wilma Flintstone"}, ) self.index.add("name", "Fred", self.fred) self.index.add("name", "Wilma", self.wilma) self.index.add("name", "Flintstone", self.fred) self.index.add("name", "Flintstone", self.wilma) self.index.add("flintstones", "%", self.fred) self.index.add("flintstones", "%", self.wilma) self.batch = LegacyWriteBatch(legacy_graph) def check(self, key, value, *entities): e = self.index.get(key, value) assert len(entities) == len(e) for entity in entities: assert entity in e def test_remove_key_value_entity(self): self.batch.remove_from_index(Node, self.index, key="name", value="Flintstone", entity=self.fred) self.batch.run() self.check("name", "Fred", self.fred) self.check("name", "Wilma", self.wilma) self.check("name", "Flintstone", self.wilma) self.check("flintstones", "%", self.fred, self.wilma) def test_remove_key_entity(self): self.batch.remove_from_index(Node, self.index, key="name", entity=self.fred) self.batch.run() self.check("name", "Fred") self.check("name", "Wilma", self.wilma) self.check("name", "Flintstone", self.wilma) self.check("flintstones", "%", self.fred, self.wilma) def test_remove_entity(self): self.batch.remove_from_index(Node, self.index, entity=self.fred) self.batch.run() self.check("name", "Fred") self.check("name", "Wilma", self.wilma) self.check("name", "Flintstone", self.wilma) self.check("flintstones", "%", self.wilma)