From 8c9bea4f941ce9cd1641c5d6530304b1ec3efc30 Mon Sep 17 00:00:00 2001 From: "Alexie (Boyong) Madolid" Date: Tue, 12 Sep 2023 02:34:07 +0800 Subject: [PATCH] [FAST-EDGES]: Additional handling for multiple smart edge saving --- jaseci_core/jaseci/prim/edge.py | 2 + jaseci_core/jaseci/prim/node.py | 28 +++-- jaseci_core/jaseci/tests/jac_test_code.py | 49 ++++++++ .../jaseci_serv/base/tests/test_orm_hooks.py | 105 +++++++++++++++++- 4 files changed, 170 insertions(+), 14 deletions(-) diff --git a/jaseci_core/jaseci/prim/edge.py b/jaseci_core/jaseci/prim/edge.py index d051e03010..2a4ff40945 100644 --- a/jaseci_core/jaseci/prim/edge.py +++ b/jaseci_core/jaseci/prim/edge.py @@ -108,6 +108,8 @@ def save(self): self._persist = False if self.from_node_id: self.from_node().save() + if self.to_node_id: + self.to_node().save() super().save() def destroy(self): diff --git a/jaseci_core/jaseci/prim/node.py b/jaseci_core/jaseci/prim/node.py index 6350a4af6e..b9989ff2ce 100644 --- a/jaseci_core/jaseci/prim/node.py +++ b/jaseci_core/jaseci/prim/node.py @@ -52,18 +52,22 @@ def smart_build_fast_edge_ids(self): for k in self.fast_edges.keys(): for v in self.fast_edges[k]: link_order = [v[0], self.jid] if v[1] == FROM else [self.jid, v[0]] - edge = Edge( - m_id=self._m_id, h=self._h, kind="edge", name=k, auto_save=False - ) - edge.from_node_id = link_order[0] - edge.to_node_id = link_order[1] - edge.bidirected = v[1] == BI - edge.jid = v[2] if len(v) > 2 else uuid.uuid4().urn - edge.context = v[3] if len(v) > 3 else {} - # old `edge.save()` might be confusing - # this line doesn't mean it has to be saved on db - # it only needs to be available on cache (memory, redis) - self._h.commit_obj_to_cache(edge, True) + edge = self._h.get_obj(self._m_id, v[2]) + if edge: + v[3] = edge.context + else: + edge = Edge( + m_id=self._m_id, h=self._h, kind="edge", name=k, auto_save=False + ) + edge.from_node_id = link_order[0] + edge.to_node_id = link_order[1] + edge.bidirected = v[1] == BI + edge.jid = v[2] if len(v) > 2 else uuid.uuid4().urn + edge.context = v[3] if len(v) > 3 else {} + # old `edge.save()` might be confusing + # this line doesn't mean it has to be saved on db + # it only needs to be available on cache (memory, redis) + self._h.commit_obj_to_cache(edge, True) self._fast_edge_ids.add_obj(edge, bypass=True) def smart_add_edge(self, obj): diff --git a/jaseci_core/jaseci/tests/jac_test_code.py b/jaseci_core/jaseci/tests/jac_test_code.py index ef2ac41f4e..b56db4dd18 100644 --- a/jaseci_core/jaseci/tests/jac_test_code.py +++ b/jaseci_core/jaseci/tests/jac_test_code.py @@ -1242,3 +1242,52 @@ b1: take --> node::b2; } """ + +simple_graph2 = """ +node a1 {has val = 0;} +edge e1 {has val = 0;} + +walker sample { + root: report spawn here <+[e1]+ node::a1; +} + +walker sample2 { + root: report <-[e1]- node::a1.edge[0].val; + a1 { + --> node::root.edge[0].val = 4; + report -[e1]->.edge[0].val; + take --> node::root; + } +} + +walker sample3 { + root { + report <-[e1]- node::a1.edge[0].val; + take <-[e1]- node::a1; + } + + a1: report -[e1]->.edge[0].val; +} + +walker sample4 { + root { + <-- node::a1.edge[0].val = 6; + report <-[e1]- node::a1.edge[0].val; + take <-[e1]- node::a1; + } + a1: report -[e1]-> node::root.edge[0].val; +} + +walker sample5 { + root: report <-[e1]-.edge[0].val; + a1 { + report -[e1]-> node::root.edge[0].val; + take -[e1]-> node::root; + } +} + +walker sample6 { + root: <-[e1]- node::a1.edge[0].val = 8; + a1: report --> node::root.edge[0].val; +} +""" diff --git a/jaseci_serv/jaseci_serv/base/tests/test_orm_hooks.py b/jaseci_serv/jaseci_serv/base/tests/test_orm_hooks.py index 839e3fd148..a9ed009ba6 100644 --- a/jaseci_serv/jaseci_serv/base/tests/test_orm_hooks.py +++ b/jaseci_serv/jaseci_serv/base/tests/test_orm_hooks.py @@ -392,8 +392,109 @@ def test_node_saves_on_traversing_graphs(self): test_walker.prime(gph) test_walker.run() - # no a2::node and a3::node since it was already disconnected + # no a3::node since it was already disconnected self.assertSetEqual( - set(["a1::node"]), + set(["a1::node", "a2::node"]), set([item.name + "::" + item.j_type for item in hook.save_obj_list]), ) + + def test_node_saves_on_fast_edges(self): + user = self.user + hook = user._h = JsOrc.hook() + gph = Graph(m_id=0, h=hook) + sent = Sentinel(m_id=0, h=gph._h) + sent.register_code(jtc.simple_graph2) + test_walker = sent.run_architype("sample") + test_walker.prime(gph) + a1 = test_walker.run()["report"][0]["jid"] + + # initial trigger + self.assertSetEqual( + set( + [ + "basic::sentinel", + "root::graph", + "a1::node", + "root::architype", + "a1::architype", + "e1::architype", + "generic::architype", + "sample::architype", + "sample2::architype", + "sample3::architype", + "sample4::architype", + "sample5::architype", + "sample6::architype", + "generic::architype", + ] + ), + set([item.name + "::" + item.j_type for item in hook.save_obj_list]), + ) + + hook.commit() + hook.clear_cache() + + # trigger change value from node::a1 + a1 = hook.get_obj(0, a1) + test_walker = sent.run_architype("sample2") + test_walker.prime(a1) + reps = test_walker.run()["report"] + + self.assertEqual([4, 4], reps) + self.assertSetEqual( + set(["a1::node", "root::graph"]), + set([item.name + "::" + item.j_type for item in hook.save_obj_list]), + ) + + hook.commit() + hook.clear_cache() + + # trigger report value from graph::root + test_walker = sent.run_architype("sample3") + test_walker.prime(gph) + reps = test_walker.run()["report"] + + self.assertEqual([4, 4], reps) + self.assertFalse(hook.save_obj_list) + + # trigger change value from graph::root + test_walker = sent.run_architype("sample4") + test_walker.prime(gph) + reps = test_walker.run()["report"] + + self.assertEqual([6, 6], reps) + self.assertSetEqual( + set(["a1::node", "root::graph"]), + set([item.name + "::" + item.j_type for item in hook.save_obj_list]), + ) + + hook.commit() + hook.clear_cache() + + # trigger report value from node::a1 + test_walker = sent.run_architype("sample5") + test_walker.prime(a1) + reps = test_walker.run()["report"] + + self.assertEqual([6, 6], reps) + self.assertFalse(hook.save_obj_list) + + # trigger change value from graph::root without accessing node::a1 + test_walker = sent.run_architype("sample6") + test_walker.prime(gph) + test_walker.run() + + self.assertSetEqual( + set(["a1::node", "root::graph"]), + set([item.name + "::" + item.j_type for item in hook.save_obj_list]), + ) + + hook.commit() + hook.clear_cache() + + # read node::a1 without accessing graph::root + test_walker = sent.run_architype("sample6") + test_walker.prime(a1) + reps = test_walker.run()["report"] + + self.assertEqual([8], reps)