Resolve index levels from the registry instead of persisting them

This commit is contained in:
Dennis Kobert
2026-08-24 20:40:51 +00:00
parent 2c84bb478f
commit a6c56c8655
3 changed files with 29 additions and 15 deletions

View File

@@ -498,17 +498,34 @@ impl ProtoNetwork {
nullification_node_id
}
/// The node's declared index levels and per-input pushed levels, both read
/// from the registry since they follow the node's signature. A node with no
/// registry entry pushes nothing and names no level.
fn registry_index_levels(&self, node_index: usize) -> (core_types::context::IndexLevels, Vec<u8>) {
let identifier = &self.nodes[node_index].1.identifier;
let metadata = core_types::registry::NODE_METADATA.lock().unwrap();
match metadata.get(identifier) {
Some(entry) => (
ContextDependencies::from(entry.context_features.as_slice()).index_levels,
entry.fields.iter().map(|field| field.pushed_levels).collect(),
),
None => (core_types::context::IndexLevels::empty(), Vec::new()),
}
}
fn find_context_dependencies(&mut self, id: NodeId) -> (ContextModification, Option<NodeId>) {
let mut branch_dependencies = Vec::new();
let mut combined_deps = ContextModification::default();
let node_index = id.0 as usize;
let (declared_levels, pushed_levels) = self.registry_index_levels(node_index);
let (extract, inject, own_deps) = {
let dependencies = &self.nodes[node_index].1.context_features;
// Documents predating the level mask deserialize as all-levels, so a
// node that declares no index read must not contribute one.
let index_levels = match dependencies.extract.contains(core_types::context::ContextFeatures::INDEX) {
true => dependencies.index_levels,
// A wrapper node declaring its dependencies by hand keeps `INDEX`
// without the signature naming a level, and addresses the innermost.
true if declared_levels.is_empty() => core_types::context::IndexLevels::innermost(),
true => declared_levels,
false => core_types::context::IndexLevels::empty(),
};
let own_deps = ContextModification::from_sources(dependencies.extract, dependencies.sources()).with_index_levels(index_levels);
@@ -523,7 +540,6 @@ impl ProtoNetwork {
};
// Compute the dependencies for each branch and combine all of them
let pushed_levels = self.nodes[node_index].1.context_features.pushed_levels.clone();
for (input, &node) in inputs.iter().enumerate() {
let branch = self.find_context_dependencies(node);

View File

@@ -219,7 +219,7 @@ impl IndexLevels {
}
/// The innermost level alone, which is what every reader but `read_index`
/// addresses, so documents predating the mask migrate to it.
/// addresses.
pub const fn innermost() -> Self {
Self(1)
}
@@ -325,12 +325,14 @@ impl ContextFeatures {
pub struct ContextDependencies {
pub extract: ContextFeatures,
pub inject: ContextFeatures,
/// Which index levels the node reads; empty means it reads none. Documents
/// written before the field existed default to the whole chain.
#[cfg_attr(feature = "serde", serde(default = "IndexLevels::innermost"))]
/// Which index levels the node reads, non-empty exactly when `extract` keeps
/// `INDEX`. Follows the node's signature, so it resolves from the registry
/// instead of persisting.
#[cfg_attr(feature = "serde", serde(skip))]
pub index_levels: IndexLevels,
/// Index levels pushed per input, in input order; a missing entry is 0.
#[cfg_attr(feature = "serde", serde(default))]
/// Resolves from the registry alongside [`Self::index_levels`].
#[cfg_attr(feature = "serde", serde(skip))]
pub pushed_levels: Vec<u8>,
#[cfg_attr(feature = "serde", serde(default, deserialize_with = "deserialize_sorted_sources"))]
sources: Vec<SourceId>,
@@ -356,10 +358,6 @@ impl ContextDependencies {
self
}
pub fn with_pushed_levels(mut self, pushed_levels: Vec<u8>) -> Self {
self.pushed_levels = pushed_levels;
self
}
pub fn sources(&self) -> &[SourceId] {
&self.sources

View File

@@ -205,7 +205,7 @@ impl Preprocessor {
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
visible: true,
skip_deduplication: false,
context_features: ContextDependencies::from(metadata.context_features.as_slice()).with_pushed_levels(metadata.fields.iter().map(|field| field.pushed_levels).collect()),
context_features: ContextDependencies::from(metadata.context_features.as_slice()),
..Default::default()
};
@@ -256,7 +256,7 @@ impl Preprocessor {
call_argument: node_io.call_argument.clone(),
implementation: DocumentNodeImplementation::ProtoNode(id.clone()),
visible: true,
context_features: ContextDependencies::from(metadata.context_features.as_slice()).with_pushed_levels(metadata.fields.iter().map(|field| field.pushed_levels).collect()),
context_features: ContextDependencies::from(metadata.context_features.as_slice()),
..Default::default()
};
inject_scopes.insert(id.clone(), (template, node_io.return_value.clone()));