示例#1
0
 def setUp(self):
     try:
         self.index_manager.delete_index(Node, "People")
     except LookupError:
         pass
     self.people = self.index_manager.get_or_create_index(Node, "People")
     self.batch = ManualIndexWriteBatch(self.graph)
示例#2
0
 def setUp(self):
     self.batch = ManualIndexWriteBatch(self.graph)
     self.alice = cast_node({"name": "Alice", "surname": "Allison"})
     self.bob = cast_node({"name": "Bob", "surname": "Robertson"})
     self.friends = cast_relationship((self.alice, "KNOWS", self.bob, {
         "since": 2000
     }))
     self.graph.create(self.alice | self.bob | self.friends)
示例#3
0
 def setUp(self):
     try:
         self.index_manager.delete_index(Relationship, "Friendships")
     except LookupError:
         pass
     self.friendships = self.index_manager.get_or_create_index(
         Relationship, "Friendships")
     self.batch = ManualIndexWriteBatch(self.graph)
def put_in_node():
    Index = ManualIndexManager(graph)  # manage index
    tx = graph.begin()
    count = 0
    index_list = []  # temporal put in
    filter_set = set()
    ts = time.time()
    #domain_name = "domain_index"
    domain_name = "topic_index"
    domain_index = Index.get_or_create_index(Node, domain_name)
    Batch_index = ManualIndexWriteBatch(graph)

    #for item in domain_list:
    for item in topic_list:
        #st_node = Index.get_or_create_indexed_node(node_name, "uid", start_node, {"uid":start_node})
        #ed_node = Index.get_or_create_indexed_node(node_name, "uid", end_node, {"uid":end_node})
        #exist = Index.get_indexed_node(domain_name, "domain", item)
        exist = Index.get_indexed_node(domain_name, "topic", item)
        if not exist:
            #node = Node("Domain", domain=item)
            node = Node("Topic", topic=item)
            tx.create(node)
            index_list.append([item, node])
    tx.commit()
    for item in index_list:
        #domain_index.add('domain', item[0], item[1])
        domain_index.add('topic', item[0], item[1])
    print domain_index
    print domain_index.get("topic", '科技类')
示例#5
0
 def setUp(self):
     try:
         self.index_manager.delete_index(Node, "node_removal_test_index")
     except LookupError:
         pass
     self.index = self.index_manager.get_or_create_index(
         Node, "node_removal_test_index")
     self.fred = Node(name="Fred Flintstone")
     self.wilma = Node(name="Wilma Flintstone")
     self.graph.create(self.fred | self.wilma)
     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 = ManualIndexWriteBatch(self.graph)
示例#6
0
class NodeCreationTestCase(GraphTestCase):
    def setUp(self):
        self.batch = ManualIndexWriteBatch(self.graph)

    def test_can_create_single_empty_node(self):
        self.batch.create(Node())
        a, = self.batch.run()
        assert isinstance(a, Node)
        assert not a

    def test_can_create_multiple_nodes(self):
        self.batch.create({"name": "Alice"})
        self.batch.create(cast_node({"name": "Bob"}))
        self.batch.create(Node(name="Carol"))
        alice, bob, carol = self.batch.run()
        assert isinstance(alice, Node)
        assert isinstance(bob, Node)
        assert isinstance(carol, Node)
        assert alice["name"] == "Alice"
        assert bob["name"] == "Bob"
        assert carol["name"] == "Carol"
示例#7
0
def put_in():
    Index = ManualIndexManager(graph)  # manage index
    tx = graph.begin()
    count = 0
    index_list = []  # temporal put in
    filter_set = set()
    ts = time.time()
    node_name = "node_index"
    #rel_name = "rel_index"
    node_index = Index.get_or_create_index(Node, node_name)
    #rel_index = Index.get_or_create_index(Relationship, rel_name)
    Batch_index = ManualIndexWriteBatch(graph)
    user_list = json.loads(r.get("user_set"))
    for user in user_list:
        #st_node = Index.get_or_create_indexed_node(node_name, "uid", start_node, {"uid":start_node})
        #ed_node = Index.get_or_create_indexed_node(node_name, "uid", end_node, {"uid":end_node})
        exist = Index.get_indexed_node(node_name, "uid", user)
        if not exist and user not in filter_set:
            node = Node("User", uid=user)
            tx.create(node)
            index_list.append([user, node])
            filter_set.add(node)

        count += 1
        if count % 1000 == 0:
            print count
            te = time.time()
            print "cost time: %s" % (te - ts)
            ts = te
            tx.commit()
            tx = graph.begin()
            for item in index_list:
                Batch_index.add_to_index(Node, node_name, "uid", item[0],
                                         item[1])
            index_list = []
            filter_set = set()
    tx.commit()
示例#8
0
class IndexedRelationshipAdditionTestCase(IndexTestCase):
    def setUp(self):
        try:
            self.index_manager.delete_index(Relationship, "Friendships")
        except LookupError:
            pass
        self.friendships = self.index_manager.get_or_create_index(
            Relationship, "Friendships")
        self.batch = ManualIndexWriteBatch(self.graph)

    def test_can_add_single_relationship(self):
        alice = Node(name="Alice Smith")
        bob = Node(name="Bob Smith")
        ab = Relationship(alice, "KNOWS", bob)
        self.graph.create(alice | bob | ab)
        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]
示例#9
0
def put_in_user_portrait():
    Index = ManualIndexManager(graph)  # manage index
    Batch_index = ManualIndexWriteBatch(graph)
    tx = graph.begin()
    index_list = []  # temporal put in
    node_name = "node_index"
    node_index = Index.get_index(Node, node_name)

    f = open("user_portrait.txt", "rb")
    count = 0
    filter_set = set()
    for item in f:
        user_dict = json.loads(item)
        user = user_dict["uid"]
        exist = node_index.get("uid", user)
        if not exist and user not in filter_set:
            node = Node("User", uid=user)
            tx.create(node)
            index_list.append([user, node])
            filter_set.add(node)

            count += 1
            if count % 1000 == 0:
                print count
                tx.commit()
                tx = graph.begin()
                for item in index_list:
                    node_index.add("uid", item[0], item[1])
                    #Batch_index.add_to_index(Node, node_name, "uid", item[0], item[1])
                index_list = []
                filter_set = set()
    tx.commit()
    if index_list:
        for item in index_list:
            node_index.add("uid", item[0], item[1])
            #Batch_index.add_to_index(Node, node_name, "uid", item[0], item[1])
    print count

    f.close()
示例#10
0
class PropertyManagementTestCase(GraphTestCase):
    def setUp(self):
        self.batch = ManualIndexWriteBatch(self.graph)
        self.alice = cast_node({"name": "Alice", "surname": "Allison"})
        self.bob = cast_node({"name": "Bob", "surname": "Robertson"})
        self.friends = cast_relationship((self.alice, "KNOWS", self.bob, {
            "since": 2000
        }))
        self.graph.create(self.alice | self.bob | self.friends)

    def _check_properties(self, entity, expected_properties):
        self.graph.pull(entity)
        actual_properties = dict(entity)
        assert len(actual_properties) == len(expected_properties)
        for key, value in expected_properties.items():
            assert key in actual_properties
            assert str(actual_properties[key]) == str(value)

    def test_can_add_new_node_property(self):
        self.batch.set_property(self.alice, "age", 33)
        self.batch.run()
        self._check_properties(self.alice, {
            "name": "Alice",
            "surname": "Allison",
            "age": 33
        })

    def test_can_overwrite_existing_node_property(self):
        self.batch.set_property(self.alice, "name", "Alison")
        self.batch.run()
        self._check_properties(self.alice, {
            "name": "Alison",
            "surname": "Allison"
        })

    def test_can_replace_all_node_properties(self):
        props = {"full_name": "Alice Allison", "age": 33}
        self.batch.set_properties(self.alice, props)
        self.batch.run()
        self._check_properties(self.alice, props)

    def test_can_delete_node_property(self):
        self.batch.delete_property(self.alice, "surname")
        self.batch.run()
        self._check_properties(self.alice, {"name": "Alice"})

    def test_can_delete_all_node_properties(self):
        self.batch.delete_properties(self.alice)
        r = self.batch.run()
        self._check_properties(self.alice, {})
示例#11
0
class IndexedNodeCreationTestCase(IndexTestCase):
    def setUp(self):
        try:
            self.index_manager.delete_index(Node, "People")
        except LookupError:
            pass
        self.people = self.index_manager.get_or_create_index(Node, "People")
        self.batch = ManualIndexWriteBatch(self.graph)

    def test_can_create_single_indexed_node(self):
        properties = {"name": "Alice Smith"}
        # need to execute a pair of commands as "create in index" not available
        self.batch.create(properties)
        self.batch.add_to_index(Node, self.people, "surname", "Smith", 0)
        alice, index_entry = self.batch.run()
        assert isinstance(alice, Node)
        assert dict(alice) == properties
        self.graph.delete(alice)

    def test_can_create_two_similarly_indexed_nodes(self):
        # create Alice
        alice_props = {"name": "Alice Smith"}
        # need to execute a pair of commands as "create in index" not available
        self.batch.create(alice_props)
        self.batch.add_to_index(Node, self.people, "surname", "Smith", 0)
        alice, alice_index_entry = self.batch.run()
        assert isinstance(alice, Node)
        assert dict(alice) == alice_props
        self.batch.jobs = []
        # create Bob
        bob_props = {"name": "Bob Smith"}
        # need to execute a pair of commands as "create in index" not available
        self.batch.create(bob_props)
        self.batch.add_to_index(Node, self.people, "surname", "Smith", 0)
        bob, bob_index_entry = self.batch.run()
        assert isinstance(bob, Node)
        assert dict(bob) == bob_props
        # 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_get_or_create_uniquely_indexed_node(self):
        # create Alice
        alice_props = {"name": "Alice Smith"}
        self.batch.get_or_create_in_index(Node, self.people, "surname",
                                          "Smith", alice_props)
        alice, = self.batch.run()
        assert isinstance(alice, Node)
        assert dict(alice) == alice_props
        self.batch.jobs = []
        # create Bob
        bob_props = {"name": "Bob Smith"}
        self.batch.get_or_create_in_index(Node, self.people, "surname",
                                          "Smith", bob_props)
        bob, = self.batch.run()
        assert isinstance(bob, Node)
        assert dict(bob) != bob_props
        assert dict(bob) == alice_props
        assert bob == alice
        # check entries
        smiths = self.people.get("surname", "Smith")
        assert len(smiths) == 1
        assert alice in smiths
        # done
        self.graph.delete(alice | bob)
示例#12
0
class IndexedNodeRemovalTestCase(IndexTestCase):
    def setUp(self):
        try:
            self.index_manager.delete_index(Node, "node_removal_test_index")
        except LookupError:
            pass
        self.index = self.index_manager.get_or_create_index(
            Node, "node_removal_test_index")
        self.fred = Node(name="Fred Flintstone")
        self.wilma = Node(name="Wilma Flintstone")
        self.graph.create(self.fred | self.wilma)
        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 = ManualIndexWriteBatch(self.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)
示例#13
0
class RelationshipCreationTestCase(GraphTestCase):
    def setUp(self):
        self.batch = ManualIndexWriteBatch(self.graph)

    def test_can_create_relationship_with_new_nodes(self):
        self.batch.create({"name": "Alice"})
        self.batch.create({"name": "Bob"})
        self.batch.create((0, "KNOWS", 1))
        alice, bob, knows = self.batch.run()
        assert isinstance(knows, Relationship)
        assert knows.start_node() == alice
        assert knows.type() == "KNOWS"
        assert knows.end_node() == bob
        assert dict(knows) == {}
        self.recycling = [knows, alice, bob]

    def test_can_create_relationship_with_existing_nodes(self):
        self.batch.create({"name": "Alice"})
        self.batch.create({"name": "Bob"})
        alice, bob = self.batch.run()
        self.batch.jobs = []
        self.batch.create((alice, "KNOWS", bob))
        knows, = self.batch.run()
        assert isinstance(knows, Relationship)
        assert knows.start_node() == alice
        assert knows.type() == "KNOWS"
        assert knows.end_node() == bob
        assert dict(knows) == {}
        self.recycling = [knows, alice, bob]

    def test_can_create_relationship_with_existing_start_node(self):
        self.batch.create({"name": "Alice"})
        alice, = self.batch.run()
        self.batch.jobs = []
        self.batch.create({"name": "Bob"})
        self.batch.create((alice, "KNOWS", 0))
        bob, knows = self.batch.run()
        assert isinstance(knows, Relationship)
        assert knows.start_node() == alice
        assert knows.type() == "KNOWS"
        assert knows.end_node() == bob
        assert dict(knows) == {}
        self.recycling = [knows, alice, bob]

    def test_can_create_relationship_with_existing_end_node(self):
        self.batch.create({"name": "Bob"})
        bob, = self.batch.run()
        self.batch.jobs = []
        self.batch.create({"name": "Alice"})
        self.batch.create((0, "KNOWS", bob))
        alice, knows = self.batch.run()
        assert isinstance(knows, Relationship)
        assert knows.start_node() == alice
        assert knows.type() == "KNOWS"
        assert knows.end_node() == bob
        assert dict(knows) == {}
        self.recycling = [knows, alice, bob]

    def test_can_create_multiple_relationships(self):
        self.batch.create({"name": "Alice"})
        self.batch.create({"name": "Bob"})
        self.batch.create({"name": "Carol"})
        self.batch.create((0, "KNOWS", 1))
        self.batch.create((1, "KNOWS", 2))
        self.batch.create((2, "KNOWS", 0))
        alice, bob, carol, ab, bc, ca = self.batch.run()
        for relationship in [ab, bc, ca]:
            assert isinstance(relationship, Relationship)
            assert relationship.type() == "KNOWS"

    def test_can_create_overlapping_relationships(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, knows1, knows2 = self.batch.run()
        assert isinstance(knows1, Relationship)
        assert knows1.start_node() == alice
        assert knows1.type() == "KNOWS"
        assert knows1.end_node() == bob
        assert dict(knows1) == {}
        assert isinstance(knows2, Relationship)
        assert knows2.start_node() == alice
        assert knows2.type() == "KNOWS"
        assert knows2.end_node() == bob
        assert dict(knows2) == {}
        self.recycling = [knows1, knows2, alice, bob]

    def test_can_create_relationship_with_properties(self):
        self.batch.create({"name": "Alice"})
        self.batch.create({"name": "Bob"})
        self.batch.create((0, "KNOWS", 1, {"since": 2000}))
        alice, bob, knows = self.batch.run()
        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_create_function(self):
        self.batch.create(Node(name="Alice"))
        self.batch.create(Node(name="Bob"))
        self.batch.create((0, "KNOWS", 1))
        alice, bob, ab = self.batch.run()
        assert isinstance(alice, Node)
        assert alice["name"] == "Alice"
        assert isinstance(bob, Node)
        assert bob["name"] == "Bob"
        assert isinstance(ab, Relationship)
        assert ab.start_node() == alice
        assert ab.type() == "KNOWS"
        assert ab.end_node() == bob
        self.recycling = [ab, alice, bob]
示例#14
0
 def setUp(self):
     self.batch = ManualIndexWriteBatch(self.graph)
示例#15
0
class IndexedNodeAdditionTestCase(IndexTestCase):
    def setUp(self):
        try:
            self.index_manager.delete_index(Node, "People")
        except LookupError:
            pass
        self.people = self.index_manager.get_or_create_index(Node, "People")
        self.batch = ManualIndexWriteBatch(self.graph)

    def test_can_add_single_node(self):
        alice = Node(name="Alice Smith")
        self.graph.create(alice)
        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 = Node(name="Alice Smith")
        bob = Node(name="Bob Smith")
        self.graph.create(alice | bob)
        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.run()
        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 = Node(name="Alice Smith")
        bob = Node(name="Bob Smith")
        self.graph.create(alice | bob)
        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.run()
        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)
示例#16
0
 def setUp(self):
     self.batch = ManualIndexWriteBatch(self.graph)
     self.runner = self.batch.runner
示例#17
0
class MiscellaneousTestCase(GraphTestCase):
    def setUp(self):
        self.batch = ManualIndexWriteBatch(self.graph)
        self.runner = self.batch.runner

    def test_can_use_return_values_as_references(self):
        a = self.batch.create(Node(name="Alice"))
        b = self.batch.create(Node(name="Bob"))
        self.batch.create(Relationship(a, "KNOWS", b))
        results = self.batch.run()
        ab = results[2]
        assert isinstance(ab, Relationship)
        assert ab.start_node()["name"] == "Alice"
        assert ab.end_node()["name"] == "Bob"

    def test_can_handle_json_response_with_no_content(self):
        # This example might fail if the server bug is fixed that returns
        # a 200 response with application/json content-type and no content.
        self.batch.create((0, "KNOWS", 1))
        results = self.batch.run()
        assert results == []

    @skipIf(version_2_1, "this test highlights a server bug in 2.1")
    def test_cypher_job_with_invalid_syntax(self):
        self.batch.append(CypherJob("X"))
        with self.assertRaises(BatchError) as error:
            self.batch.run()
        cause = error.exception.__cause__
        assert isinstance(cause, CypherSyntaxError)

    @skipIf(version_2_1, "this test highlights a server bug in 2.1")
    def test_cannot_resubmit_finished_job(self):
        self.batch.append(CypherJob("CREATE (a)"))
        self.runner.run(self.batch)
        with self.assertRaises(BatchFinished):
            self.runner.run(self.batch)
示例#18
0
class UniqueRelationshipCreationRestCase(GraphTestCase):
    def setUp(self):
        self.batch = ManualIndexWriteBatch(self.graph)

    @skipIf(version_2_1, "this test highlights a server bug in 2.1")
    def test_can_create_relationship_if_none_exists(self):
        self.batch.create({"name": "Alice"})
        self.batch.create({"name": "Bob"})
        alice, bob = self.batch.run()
        self.batch.jobs = []
        self.batch.get_or_create_path(alice, ("KNOWS", {"since": 2000}), bob)
        path, = self.batch.run()
        knows = path[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]

    @skipIf(version_2_1, "this test highlights a server bug in 2.1")
    def test_will_get_relationship_if_one_exists(self):
        self.batch.create({"name": "Alice"})
        self.batch.create({"name": "Bob"})
        alice, bob = self.batch.run()
        self.batch.jobs = []
        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.run()
        assert path1 == path2

    @skipIf(version_2_1, "this test highlights a server bug in 2.1")
    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.run()
        self.batch.jobs = []
        self.batch.get_or_create_path(alice, "KNOWS", bob)
        with self.assertRaises(BatchError) as error:
            self.batch.run()
        cause = error.exception.__cause__
        assert isinstance(cause, ConstraintError)

    @skipIf(version_2_1, "this test highlights a server bug in 2.1")
    def test_can_create_relationship_and_start_node(self):
        self.batch.create({"name": "Bob"})
        bob, = self.batch.run()
        self.batch.jobs = []
        self.batch.get_or_create_path(None, "KNOWS", bob)
        path, = self.batch.run()
        knows = path[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]

    @skipIf(version_2_1, "this test highlights a server bug in 2.1")
    def test_can_create_relationship_and_end_node(self):
        self.batch.create({"name": "Alice"})
        alice, = self.batch.run()
        self.batch.jobs = []
        self.batch.get_or_create_path(alice, "KNOWS", None)
        path, = self.batch.run()
        knows = path[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]
示例#19
0
class DeletionTestCase(GraphTestCase):
    def setUp(self):
        self.batch = ManualIndexWriteBatch(self.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.run()
        assert self.graph.exists(alice | bob | ab)
        self.batch.jobs = []
        self.batch.delete(ab)
        self.batch.delete(alice)
        self.batch.delete(bob)
        self.batch.run()
        assert not self.graph.exists(alice | bob | ab)