Skip to content

Commit 56ecea4

Browse files
docs: sharpen distinction between cascade() and restrict()
Lead each description with its purpose rather than using parallel structure. cascade() prepares a delete (one-shot, trims graph, OR). restrict() selects a data subset (chainable, preserves graph, AND). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fbbafc2 commit 56ecea4

2 files changed

Lines changed: 11 additions & 13 deletions

File tree

src/explanation/whats-new-22.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,13 @@ This is valuable when working with unfamiliar pipelines, large datasets, or mult
234234

235235
### Two Propagation Modes
236236

237-
The diagram supports two restriction propagation modes with different convergence semantics:
237+
The diagram supports two restriction propagation modes designed for fundamentally different tasks.
238238

239-
**`cascade()` uses OR at convergence.** When a child table has multiple restricted ancestors, the child row is affected if *any* parent path reaches it. This is the right semantics for delete — if any reason exists to remove a row, it should be removed. `cascade()` is one-shot: it can only be called once on an unrestricted diagram.
239+
**`cascade()` prepares a delete.** It takes a single restricted table expression, propagates the restriction downstream through all descendants, and **trims the diagram** to the resulting subgraph — ancestors and unrelated tables are removed entirely. Convergence uses OR: a descendant row is marked for deletion if *any* ancestor path reaches it, because if any reason exists to remove a row, it should be removed. `cascade()` is one-shot and is always followed by `preview()` or `delete()`.
240240

241-
**`restrict()` uses AND at convergence.** A child row is included only if *all* restricted ancestors match. This is the right semantics for data subsetting and export only rows satisfying every condition are selected. `restrict()` is chainable: call it multiple times to build up conditions from different tables.
241+
**`restrict()` selects a data subset.** It propagates a restriction downstream but **preserves the full diagram**, allowing `restrict()` to be called again from a different seed table. This makes it possible to build up multi-condition subsets incrementally — for example, restricting by species from one table and by date from another. Convergence uses AND: a descendant row is included only if *all* restricted ancestors match, because an export should contain only rows satisfying every condition. After chaining restrictions, use `prune()` to remove empty tables and `preview()` to inspect the result.
242242

243-
Both modes propagate **downstream only** — from the seed table to its descendants. `cascade()` goes further: it trims the returned Diagram to the cascade subgraph, removing all ancestors and unrelated tables. This means `delete()` operates on the entire trimmed diagram with no additional filtering. `restrict()` keeps the full graph intact (to support chaining from multiple seed tables) but only restricts the seed's descendants. This matches the semantics of foreign key cascades: deleting a session deletes its trials, not its subject.
244-
245-
The two modes are mutually exclusive on the same diagram. This prevents accidental mixing of incompatible semantics.
243+
The two modes are mutually exclusive on the same diagram. This prevents accidental mixing of incompatible semantics — a delete diagram should never be reused for subsetting, and vice versa.
246244

247245
### Pruning Empty Tables
248246

src/reference/specs/diagram.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ Diagrams can propagate restrictions through the dependency graph and execute dat
130130
diag.cascade(table_expr, part_integrity="enforce")
131131
```
132132

133-
Apply a cascade restriction and propagate it downstream through the dependency graph. The returned Diagram is trimmed to the **cascade subgraph**only the seed table and its descendants remain. All ancestors and unrelated tables are removed. Uses **OR** semantics at convergence — a child row is affected if *any* restricted ancestor reaches it. Designed for delete operations.
133+
Prepare a cascading delete. Starting from a restricted table expression, propagate the restriction downstream through all descendants using **OR** semantics — a descendant row is marked for deletion if *any* ancestor path reaches it. The returned Diagram is **trimmed** to the cascade subgraph: only the seed table and its descendants remain; all ancestors and unrelated tables are removed. The trimmed diagram is ready for `preview()` and `delete()`.
134134

135135
| Parameter | Type | Default | Description |
136136
|-----------|------|---------|-------------|
@@ -141,8 +141,8 @@ Apply a cascade restriction and propagate it downstream through the dependency g
141141

142142
**Constraints:**
143143

144-
- `cascade()` can only be called **once** on an unrestricted Diagram
145-
- Cannot be mixed with `restrict()` — the two modes are mutually exclusive
144+
- **One-shot**can only be called once on an unrestricted Diagram
145+
- Mutually exclusive with `restrict()`
146146
- `table_expr.full_table_name` must be a node in the diagram
147147

148148
**`part_integrity` values:**
@@ -165,19 +165,19 @@ restricted = diag.cascade(Session & {'subject_id': 'M001'})
165165
diag.restrict(table_expr)
166166
```
167167

168-
Apply a restrict condition and propagate it downstream. Only the seed table and its descendants receive restrictions — ancestors remain in the diagram but are not restricted. Unlike `cascade()`, the diagram is not trimmed (to support chaining from multiple seed tables). Uses **AND** semantics at convergence — a child row is included only if it satisfies *all* restricted ancestors. Designed for data subsetting and export operations.
168+
Select a subset of data for export or inspection. Starting from a restricted table expression, propagate the restriction downstream through all descendants using **AND** semantics — a descendant row is included only if *all* restricted ancestors match. The full diagram is preserved (ancestors, unrelated tables) so that `restrict()` can be called again from a different seed table, building up a multi-condition subset incrementally.
169169

170170
| Parameter | Type | Default | Description |
171171
|-----------|------|---------|-------------|
172172
| `table_expr` | QueryExpression || A restricted table expression |
173173

174-
**Returns:** New `Diagram` with restrict conditions applied.
174+
**Returns:** New `Diagram` with restrict conditions applied. The graph is not trimmed.
175175

176176
**Constraints:**
177177

178-
- Cannot be called on a cascade-restricted Diagram (mutually exclusive with `cascade()`)
178+
- **Chainable** — call multiple times to add conditions from different seed tables
179+
- Mutually exclusive with `cascade()`
179180
- `table_expr.full_table_name` must be a node in the diagram
180-
- **Can be chained** — call `restrict()` multiple times to add conditions from different tables
181181

182182
```python
183183
# Chain multiple restrictions (AND semantics)

0 commit comments

Comments
 (0)