Skip to content

Commit 77fd402

Browse files
committed
Clean up last bug.
1 parent 7d1f99a commit 77fd402

File tree

1 file changed

+66
-2
lines changed

1 file changed

+66
-2
lines changed

cpp/src/cpp/nodes/reduce_node.cpp

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,77 @@ namespace hgraph {
8989
void ReduceNode::eval() {
9090
mark_evaluated();
9191

92-
auto &key_set = ts()->key_set();
92+
auto tsd = ts();
93+
auto &key_set = tsd->key_set();
9394

9495
// Process removals first, then additions
95-
// Build vectors from Value-based iteration
9696
auto removed_keys = key_set.collect_removed();
9797
remove_nodes_from_views(removed_keys);
9898

99+
// Check for stale REF values: when the upstream REF becomes empty,
100+
// the accessor doesn't explicitly remove keys (only modified_items are processed).
101+
// We need to detect keys bound to empty/invalid REF values and remove them.
102+
bool accessor_input_empty = false;
103+
if (tsd->has_output() && tsd->output()) {
104+
auto tsd_output = tsd->output();
105+
if (tsd_output->has_owning_node()) {
106+
auto accessor_node = tsd_output->owning_node();
107+
auto accessor_input = accessor_node->input();
108+
if (accessor_input) {
109+
for (size_t i = 0; i < accessor_input->size(); ++i) {
110+
auto input_item = (*accessor_input)[i];
111+
// Check if accessor input is a REF with empty value
112+
if (auto ref_input = dynamic_cast<TimeSeriesReferenceInput*>(input_item.get())) {
113+
if (ref_input->value().is_empty()) {
114+
accessor_input_empty = true;
115+
break;
116+
}
117+
}
118+
// Check if accessor input is a TSD with no output binding
119+
if (auto tsd_input = dynamic_cast<TimeSeriesDictInput*>(input_item.get())) {
120+
if (!tsd_input->has_output()) {
121+
accessor_input_empty = true;
122+
break;
123+
}
124+
}
125+
}
126+
}
127+
}
128+
}
129+
130+
if (!bound_node_indexes_.empty()) {
131+
std::vector<value::ConstValueView> stale_keys;
132+
133+
// If the TSD has no output OR accessor's input is empty/unbound, all keys are stale
134+
if (!tsd->has_output() || accessor_input_empty) {
135+
for (const auto& [key, ndx] : bound_node_indexes_) {
136+
stale_keys.push_back(key.const_view());
137+
}
138+
} else {
139+
// Check individual keys for stale REF values
140+
for (const auto& [key, ndx] : bound_node_indexes_) {
141+
if (tsd->contains(key.const_view())) {
142+
auto ts_ptr = (*tsd)[key.const_view()];
143+
if (auto ref_input = dynamic_cast<TimeSeriesReferenceInput*>(ts_ptr.get())) {
144+
auto ref_value = ref_input->value();
145+
bool has_out = ref_value.has_output();
146+
bool out_valid = has_out && ref_value.output()->valid();
147+
if (ref_value.is_empty() || (has_out && !out_valid)) {
148+
stale_keys.push_back(key.const_view());
149+
}
150+
}
151+
} else {
152+
// Key in bound_node_indexes_ but not in TSD - was removed elsewhere
153+
stale_keys.push_back(key.const_view());
154+
}
155+
}
156+
}
157+
158+
if (!stale_keys.empty()) {
159+
remove_nodes_from_views(stale_keys);
160+
}
161+
}
162+
99163
auto added_keys = key_set.collect_added();
100164
add_nodes_from_views(added_keys);
101165

0 commit comments

Comments
 (0)