Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Reference Tree

The Reference Tree semantic module marks entity references that form parent-child relationships. A scope-ref applies both the reference-target and reference-tree constraints. The parser validates all scoped references together as one or more trees. Entities without a parent are roots, and each root starts a separate tree.

Why is a target type required?

A type-erased ref cannot form part of the Reference Tree. A scope-ref must name its target entity type so Quent can validate the parent relationships when it processes the schema.

Without a declared target type, different instances of the same child entity type could refer to different parent entity types at runtime. The generated instrumentation API could then no longer guarantee that the resulting relationships form the trees declared by the schema.

This gives analysis tools a preferred path from each root entity to its related entities and their events. In the model below, a task points to the pipeline that contains it. A user interface can open one pipeline and list its tasks, while analysis can associate each task’s events with that pipeline. Other tools can use the same hierarchy for their own purposes.

YAML model

quent: '0.1.0'
model: scoped_references

entities:
  Pipeline:
    events:
      created: {}

  Task:
    events:
      started:
        attributes:
          parent: { scope-ref: Pipeline }
      ended: {}

Instrumentation API

The parent entity’s handle provides the reference. The additional hierarchy meaning belongs to the model and its constraints.

use instrumentation::{Context, Noop, Pipeline, ScopedReferences, Task};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let context = Context::<ScopedReferences>::try_new(Noop)?;

    let mut pipeline = context.observer::<Pipeline>().handle();
    pipeline.created()?;

    let mut task = context.observer::<Task>().handle();
    task.started(pipeline.as_entity_ref())?;
    task.ended()?;

    Ok(())
}
#include "quent-tutorial-scoped-references-cpp-bridge/gen/quent.hpp"

int main() {
  auto context = quent::Context::none();
  auto pipeline = context.pipeline_observer()->handle();
  pipeline.created();

  auto task = context.task_observer()->handle();
  task.started(quent::task::Started{.parent = pipeline.id()});
  task.ended();
  return 0;
}
import quent_tutorial_scoped_references as quent


def main() -> None:
    with quent.Context() as context:
        pipeline = context.pipeline_observer().handle()
        pipeline.created()

        task = context.task_observer().handle()
        task.started(parent=pipeline)
        task.ended()


if __name__ == "__main__":
    main()
Key point

A scoped reference defines a parent relationship in a validated entity tree.

Analysis

Quent’s analysis library provides a tree builder. Given entity instances and their parent IDs, it connects them into a tree and checks their relationships. The builder requires one root instance per input collection. A model with several roots can be analyzed one tree at a time.

Check yourself

What does scope-ref add beyond a normal targeted ref?

Which value is passed as the task's parent?

Full code