Skip to content

Interface: OsfqlPreviewAffected

@kortexya/reasoninglayer


@kortexya/reasoninglayer / OsfqlPreviewAffected

Defined in: src/types/osfql.ts:677

The rows a destructive statement would affect.

The engine counts them by deriving a read-only MATCH from the destructive statement’s own pattern and running it against a CLONE of the tenant. Nothing runs against the live store.

exact says whether count is the number of rows that would go, or an upper bound. Measured on the dev engine: for MATCH customer(name: ?N, spend: ?S); IF ?S > 60 THEN RETRACT customer(name: ?N); over three customers of which one has spend > 60, the nested RETRACT answers {count: 3, exact: false} — the one-statement probe cannot carry the IF condition, because ?S is bound by an earlier statement and folding the guard in would raise an unbound-variable error and lose the count entirely. So the engine states the superset and marks it. RETRACT customer(spend: 100) at the top level answers {count: 1, exact: true}.

Read exact rather than guessing from nesting: a nested statement whose enclosing conditions name only its own variables is exact too.

const affected: OsfqlPreviewAffected = {
count: 2,
exact: true,
sampleRows: [{ term_id: { type: 'term_ref', value: '…' }, tier: { type: 'string', value: 'gold' } }],
};

optional bySort?: OsfqlPreviewSortCount[]

Defined in: src/types/osfql.ts:737

How the affected rows fall across sorts, most rows first.

Present for a whole-tenant statement (CLEAR FACTS). Absent for a targeted RETRACT, whose sort is already in OsfqlPreviewStatement.sorts.


count: number

Defined in: src/types/osfql.ts:697

How many rows the statement would affect.

0 means zero rows, and it is answered whenever the rows were counted — measured, both RETRACT customer(name: "nobody") against a populated sort and RETRACT event(at: ?T) against an empty one answer {count: 0, exact: true}. It is the absence of the whole OsfqlPreviewStatement.affected block that means the statement has no affected-row set at all.

For a MATCH that carries an inline UPDATE, REMOVE, UNLINK or DELETE, it counts the distinct existing TERMS those clauses change, designated from the matched rows the way the run designates them: a term that several joined rows bind counts once, and an UNLINK counts a source only when it holds the edge.

Exact unless exact says otherwise.


exact: boolean

Defined in: src/types/osfql.ts:712

Whether count is exact, or an upper bound.

true for every top-level statement, and for a nested statement whose enclosing conditions name only its own variables or no variable at all. A constant condition is evaluated by the probe itself, so a branch that cannot run — IF 1 > 2 THEN RETRACT … — answers {count: 0, exact: true}, and a branch that must run answers its exact count.

false only for a statement nested under a condition that names a variable an EARLIER statement bound. The probe cannot carry that condition, so it counts a superset. Render it as “up to N rows”.


optional sampleRows?: Record<string, OsfqlValue>[]

Defined in: src/types/osfql.ts:729

Up to 10 sample rows, one map per matched row.

Each row IDENTIFIES the row it stands for: a term_id column, the features the statement’s own pattern projected, and the declaring sort’s @key features. The keys are DATA — OSF feature names — so they are left verbatim, never camelCased.

For a MATCH with inline UPDATE, REMOVE, UNLINK or DELETE clauses, a row is one changed term: its term_id and the declaring sort’s @key features.

Absent for a whole-tenant statement, which answers with bySort instead.


optional unparsedSampleRows?: number

Defined in: src/types/osfql.ts:753

How many sample rows were dropped because a value in them did not match the published OsfqlValue contract.

Absent when every row parsed, which is the normal case. Present with a count when at least one did not.

The rows are ILLUSTRATIVE — count, exact, bySort and everything on the enclosing statement are decided without them — so an unrecognised value tag in one of ten samples drops that row and keeps the plan. A write gate therefore still gets its verdict. Surface this count if the samples are shown to a user, so a short table is not read as a short answer.