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) self.graph.cypher.run("START a=node({a}) MATCH (a)-[r]->(b) DELETE r", {"a": subj.__node__}) 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
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) self.graph.cypher.run( "START a=node({a}) MATCH (a)-[r]->(b) DELETE r", {"a": subj.__node__}) 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 TestNodeCreation(object): @pytest.fixture(autouse=True) def setup(self, graph): self.batch = WriteBatch(graph) self.graph = graph def test_can_create_single_empty_node(self): self.batch.create(node()) a, = self.batch.submit() assert isinstance(a, Node) assert a.properties == {} def test_can_create_single_node_with_streaming(self): self.batch.create(Node(name="Alice")) for result in self.batch.stream(): assert isinstance(result, Node) assert result.properties == {"name": "Alice"} def test_can_create_multiple_nodes(self): self.batch.create({"name": "Alice"}) self.batch.create(node({"name": "Bob"})) self.batch.create(node(name="Carol")) alice, bob, carol = self.batch.submit() assert isinstance(alice, Node) assert isinstance(bob, Node) assert isinstance(carol, Node) assert alice["name"] == "Alice" assert bob["name"] == "Bob" assert carol["name"] == "Carol"
def test_can_handle_json_response_with_no_content(graph): # This example might fail if the server bug is fixed that returns # a 200 response with application/json content-type and no content. batch = WriteBatch(graph) batch.create((0, "KNOWS", 1)) results = batch.submit() assert results == []
def test_can_handle_json_response_with_no_content(graph): # This example might fail if the server bug is fixed that returns # a 200 response with application/json content-type and no content. batch = WriteBatch(graph) batch.create((0, "KNOWS", 1)) results = batch.submit() assert results == []
class TestNodeCreation(object): @pytest.fixture(autouse=True) def setup(self, graph): self.batch = WriteBatch(graph) self.graph = graph def test_can_create_single_empty_node(self): self.batch.create(node()) a, = self.batch.submit() assert isinstance(a, Node) assert a.properties == {} def test_can_create_single_node_with_streaming(self): self.batch.create(Node(name="Alice")) for result in self.batch.stream(): assert isinstance(result, Node) assert result.properties == {"name": "Alice"} def test_can_create_multiple_nodes(self): self.batch.create({"name": "Alice"}) self.batch.create(node({"name": "Bob"})) self.batch.create(node(name="Carol")) alice, bob, carol = self.batch.submit() assert isinstance(alice, Node) assert isinstance(bob, Node) assert isinstance(carol, Node) assert alice["name"] == "Alice" assert bob["name"] == "Bob" assert carol["name"] == "Carol"
def test_can_use_return_values_as_references(graph): batch = WriteBatch(graph) a = batch.create(node(name="Alice")) b = batch.create(node(name="Bob")) batch.create(rel(a, "KNOWS", b)) results = batch.submit() ab = results[2] assert isinstance(ab, Relationship) assert ab.start_node["name"] == "Alice" assert ab.end_node["name"] == "Bob"
def test_can_use_return_values_as_references(graph): batch = WriteBatch(graph) a = batch.create(node(name="Alice")) b = batch.create(node(name="Bob")) batch.create(rel(a, "KNOWS", b)) results = batch.submit() ab = results[2] assert isinstance(ab, Relationship) assert ab.start_node["name"] == "Alice" assert ab.end_node["name"] == "Bob"
class TestDeletion(object): @pytest.fixture(autouse=True) def setup(self, graph): self.batch = WriteBatch(graph) self.graph = graph def test_can_delete_relationship_and_related_nodes(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) self.batch.create((0, "KNOWS", 1)) alice, bob, ab = self.batch.submit() assert alice.exists assert bob.exists assert ab.exists self.batch.clear() self.batch.delete(ab) self.batch.delete(alice) self.batch.delete(bob) self.batch.run() assert not alice.exists assert not bob.exists assert not ab.exists
class TestDeletion(object): @pytest.fixture(autouse=True) def setup(self, graph): self.batch = WriteBatch(graph) self.graph = graph def test_can_delete_relationship_and_related_nodes(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) self.batch.create((0, "KNOWS", 1)) alice, bob, ab = self.batch.submit() assert alice.exists assert bob.exists assert ab.exists self.batch.clear() self.batch.delete(ab) self.batch.delete(alice) self.batch.delete(bob) self.batch.run() assert not alice.exists assert not bob.exists assert not ab.exists
class TestUniqueRelationshipCreation(object): @pytest.fixture(autouse=True) def setup(self, graph): self.batch = WriteBatch(graph) def test_can_create_relationship_if_none_exists(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) alice, bob = self.batch.submit() self.batch.clear() self.batch.get_or_create_path( alice, ("KNOWS", {"since": 2000}), bob) path, = self.batch.submit() knows = path.relationships[0] assert isinstance(knows, Relationship) assert knows.start_node == alice assert knows.type == "KNOWS" assert knows.end_node == bob assert knows["since"] == 2000 self.recycling = [knows, alice, bob] def test_will_get_relationship_if_one_exists(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) alice, bob = self.batch.submit() self.batch.clear() self.batch.get_or_create_path( alice, ("KNOWS", {"since": 2000}), bob) self.batch.get_or_create_path( alice, ("KNOWS", {"since": 2000}), bob) path1, path2 = self.batch.submit() assert path1 == path2 def test_will_fail_batch_if_more_than_one_exists(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) self.batch.create((0, "KNOWS", 1)) self.batch.create((0, "KNOWS", 1)) alice, bob, k1, k2 = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(alice, "KNOWS", bob) try: self.batch.submit() except BatchError as error: cause = error.__cause__ assert isinstance(cause, GraphError) assert cause.__class__.__name__ == "UniquePathNotUniqueException" else: assert False def test_can_create_relationship_and_start_node(self): self.batch.create({"name": "Bob"}) bob, = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(None, "KNOWS", bob) path, = self.batch.submit() knows = path.relationships[0] alice = knows.start_node assert isinstance(knows, Relationship) assert isinstance(alice, Node) assert knows.type == "KNOWS" assert knows.end_node == bob self.recycling = [knows, alice, bob] def test_can_create_relationship_and_end_node(self): self.batch.create({"name": "Alice"}) alice, = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(alice, "KNOWS", None) path, = self.batch.submit() knows = path.relationships[0] bob = knows.end_node assert isinstance(knows, Relationship) assert knows.start_node == alice assert knows.type == "KNOWS" assert isinstance(bob, Node) self.recycling = [knows, alice, bob]
class TestUniqueRelationshipCreation(object): @pytest.fixture(autouse=True) def setup(self, graph): self.batch = WriteBatch(graph) def test_can_create_relationship_if_none_exists(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) alice, bob = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(alice, ("KNOWS", {"since": 2000}), bob) path, = self.batch.submit() knows = path.relationships[0] assert isinstance(knows, Relationship) assert knows.start_node == alice assert knows.type == "KNOWS" assert knows.end_node == bob assert knows["since"] == 2000 self.recycling = [knows, alice, bob] def test_will_get_relationship_if_one_exists(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) alice, bob = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(alice, ("KNOWS", {"since": 2000}), bob) self.batch.get_or_create_path(alice, ("KNOWS", {"since": 2000}), bob) path1, path2 = self.batch.submit() assert path1 == path2 def test_will_fail_batch_if_more_than_one_exists(self): self.batch.create({"name": "Alice"}) self.batch.create({"name": "Bob"}) self.batch.create((0, "KNOWS", 1)) self.batch.create((0, "KNOWS", 1)) alice, bob, k1, k2 = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(alice, "KNOWS", bob) try: self.batch.submit() except BatchError as error: cause = error.__cause__ assert cause.__class__.__name__ == "UniquePathNotUniqueException" else: assert False def test_can_create_relationship_and_start_node(self): self.batch.create({"name": "Bob"}) bob, = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(None, "KNOWS", bob) path, = self.batch.submit() knows = path.relationships[0] alice = knows.start_node assert isinstance(knows, Relationship) assert isinstance(alice, Node) assert knows.type == "KNOWS" assert knows.end_node == bob self.recycling = [knows, alice, bob] def test_can_create_relationship_and_end_node(self): self.batch.create({"name": "Alice"}) alice, = self.batch.submit() self.batch.clear() self.batch.get_or_create_path(alice, "KNOWS", None) path, = self.batch.submit() knows = path.relationships[0] bob = knows.end_node assert isinstance(knows, Relationship) assert knows.start_node == alice assert knows.type == "KNOWS" assert isinstance(bob, Node) self.recycling = [knows, alice, bob]