None versus Not recorded¶
An empty answer is two different statements, and they must never share a shape.
- None — the address bound: the store holds the named attribute somewhere in your visible scope, the count ran, and nothing is at this coordinate. There are none at this spelling. You may stop looking once you have checked the spelling (below).
- Not recorded — the address did not bind. Nothing was addressed, so nothing is known; the documents may well contain it under a name the store does not hold.
The discriminator is enumeration.bound, and it is decided by the store's vocabulary
on the attribute axis alone (clause_type for prose): an address binds when the
count found rows, or — when it found none — when any row in your visible scope carries
the named attribute. Entity and period are not consulted for binding, and every axis is
matched case-sensitively with surrounding whitespace trimmed: Revenue is not
revenue; revenue is.
None: bound, and empty¶
revenue is a known attribute here; no document reports it for FY2021.
r = client.post("/v1/public/facts", json={
"workspace_id": WS, "question": "revenue", "mode": "enumerate",
"coordinate": {"attribute": "revenue", "period": "FY2021"}})
body = r.json()
print(body["enumeration"])
print(body["coverage"]["completeness_licensed"], body["coverage"].get("withheld_because"))
{'coordinate': {'attribute': 'revenue', 'period': 'FY2021'}, 'exist': 0, 'delivered': 0, 'truncated': False, 'bound': True}
True None
completeness_licensed: true over an empty set is the answer none, and it is the one
an agent can act on.
Not recorded: unbound¶
net_sales is not a name this store holds (it says revenue), and neither is
Revenue — the attribute is matched case-sensitively.
for attribute in ("net_sales", "Revenue"):
r = client.post("/v1/public/facts", json={
"workspace_id": WS, "question": "net sales", "mode": "enumerate",
"coordinate": {"attribute": attribute}})
body = r.json()
print(body["enumeration"])
print(body["coverage"]["completeness_licensed"], body["coverage"]["withheld_because"])
{'coordinate': {'attribute': 'net_sales'}, 'exist': 0, 'delivered': 0, 'truncated': False, 'bound': False}
False unbound
{'coordinate': {'attribute': 'Revenue'}, 'exist': 0, 'delivered': 0, 'truncated': False, 'bound': False}
False unbound
exist: 0 here says nothing about the documents. Discover the name with
mode="explore" and enumerate again.
The edge: an unknown entity or period reads as None, not Not recorded¶
Because binding looks at the attribute only, a coordinate that names an entity or a
period the store has never seen is a bound, empty address. This store holds Acme Corp
and FY2023; none of the three spellings below is a row, and all three read as none:
for coordinate in ({"entity": "Zzz Nonexistent Corp", "attribute": "revenue"},
{"entity": "acme corp", "attribute": "revenue"},
{"attribute": "revenue", "period": "FY 2023"}):
r = client.post("/v1/public/facts", json={
"workspace_id": WS, "question": "revenue", "mode": "enumerate",
"coordinate": coordinate})
body = r.json()
e = body["enumeration"]
print(coordinate, "-> bound:", e["bound"], "| exist:", e["exist"],
"| licensed:", body["coverage"]["completeness_licensed"])
{'entity': 'Zzz Nonexistent Corp', 'attribute': 'revenue'} -> bound: True | exist: 0 | licensed: True
{'entity': 'acme corp', 'attribute': 'revenue'} -> bound: True | exist: 0 | licensed: True
{'attribute': 'revenue', 'period': 'FY 2023'} -> bound: True | exist: 0 | licensed: True
So None on a coordinate that names an entity or a period licenses no row at this
exact spelling — not this entity reports no revenue. Before acting on exist: 0
there, read the spellings the store holds off the closed set at the attribute alone
(page through it if truncated is true):
r = client.post("/v1/public/facts", json={
"workspace_id": WS, "question": "revenue", "mode": "enumerate",
"coordinate": {"attribute": "revenue"}, "output_mode": "compact"})
print(sorted({(f["entity"], f["period"]) for f in r.json()["facts"]}))
That set is closed (exist == delivered), so every entity and period spelling at the
attribute is in it. A spelling absent from it is absent from the store, and only then is
exist: 0 for it none in the full sense.
The same rule inside limma_verify¶
An existence check with expect: "absent" is confirmed only where the coordinate
bound. An address nothing knows about cannot confirm an absence — and, by the same
rule, a bound attribute with an unknown entity confirms one:
r = client.post("/v1/public/verify", json={"workspace_id": WS, "checks": [
{"kind": "existence", "expect": "absent",
"coordinate": {"attribute": "revenue", "period": "FY2021"}},
{"kind": "existence", "expect": "absent",
"coordinate": {"attribute": "net_sales"}},
{"kind": "existence", "expect": "absent",
"coordinate": {"entity": "Zzz Nonexistent Corp", "attribute": "revenue"}},
]})
for check in r.json()["checks"]:
print(check["verdict"], "|", check["reason"])
verified | absent_at_bound_coordinate
unverifiable | coordinate_unbound
verified | absent_at_bound_coordinate
verified / absent_at_bound_coordinate is a finding. unverifiable /
coordinate_unbound is the verifier saying I could not look — which is a different
word from no, on purpose. The third line is the edge above from the other side: the
absence it verifies is at this spelling of the entity.
Absence on the relation lane¶
On the coordinate lane "not recorded" is decided per address, from the attribute
vocabulary. A relation query decides it per entity, from a recorded
fact: a coverage row — source S was read for relation R on entity E — written by the
load that looked. A structured export writes one for every entity it pulls, and a column
such as screened can declare one for another relation; a pack says who may write them.
Absence is then a row of its own, and relations.absence carries the verdict for each
entity:
kind |
when | bound_by |
denominator |
licenses |
|---|---|---|---|---|
none |
a coverage row says a load looked, and the entity holds zero rows of the relation | the source whose coverage row binds it | 0 |
no row of this relation is recorded for this entity in source bound_by as of the coverage row's as_of — nothing about sources that did not cover it |
not_recorded |
no binding coverage row: nobody declared looking, or every source that did states a row a correction retired (corrected_rows), or a source states a row its loader could not read (the verdict then carries withheld: one {source_id, reason} per such source) |
null |
null |
NOTHING — never none, never clean |
An entity that holds rows of the relation gets no verdict (absence.entity is null for
it): it is recorded, and its rows are the answer. The examples use
roster(client), where the roles export says it checked
screenings for everyone but Yusuf, and three screenings exist:
from docs.examples.roster import roster
ws = roster(client).workspace_id
for person in ("halvard_quist", "teodor_brask", "yusuf_alderane"):
body = client.post("/v1/public/facts", json={
"workspace_id": ws, "question": "screenings", "mode": "enumerate",
"relation_query": {"relation": "screening", "entity_id": person}}).json()
rel = body["relations"]
verdict = rel["absence"]["entity"]
print(person, "| bound:", rel["bound"], "| count:", rel["count"],
"| licensed:", body["coverage"]["completeness_licensed"])
if verdict is None:
print(" no verdict: the entity holds rows")
else:
print(" kind:", verdict["kind"], "| denominator:", verdict["denominator"],
"| bound_by is a source:", verdict["bound_by"] is not None,
"| coverage declared_by:", [c["declared_by"] for c in verdict["coverage"]])
halvard_quist | bound: True | count: 1 | licensed: True
no verdict: the entity holds rows
teodor_brask | bound: True | count: 0 | licensed: True
kind: none | denominator: 0 | bound_by is a source: True | coverage declared_by: ['source']
yusuf_alderane | bound: False | count: None | licensed: False
kind: not_recorded | denominator: None | bound_by is a source: False | coverage declared_by: []
Teodor has no screening recorded — a finding with its evidence (coverage names the
source that looked and declared_by: source, the export's own column). Yusuf's screening
is not recorded — nobody looked, and a missing screening is not a passed one.
Workspace-wide, without entity_id, absence.split counts every known entity into
none / not_recorded / recorded, and absence.none / absence.not_recorded list the
verdicts (capped at 200, truncated says so). A count taken while some entity is not
recorded withholds the licence — the rows it counted are exact, the claim there are no
others is not:
from docs.examples.roster import roster
ws = roster(client).workspace_id
body = client.post("/v1/public/facts", json={
"workspace_id": ws, "question": "screenings", "mode": "enumerate",
"relation_query": {"relation": "screening"}}).json()
rel = body["relations"]
print("count:", rel["count"], "| split:", rel["absence"]["split"])
print("none:", [v["entity_id"] for v in rel["absence"]["none"]],
"| not_recorded:", [v["entity_id"] for v in rel["absence"]["not_recorded"]])
print("licensed:", body["coverage"]["completeness_licensed"], body["coverage"]["narrowed_by"])
print(sorted(rel["absence"]["licenses"]))
count: 3 | split: {'none': 1, 'not_recorded': 1, 'recorded': 3}
none: ['teodor_brask'] | not_recorded: ['yusuf_alderane']
licensed: False ['entities_not_recorded']
['none', 'not_recorded']
absence.licenses states both sentences on the wire, so an agent that reads only the
response still has the rule. A third, licenses.withheld, is there only when some verdict
in the block lists withheld — a
document-store export held a row for
that entity that its loader could not read, so the entity is not_recorded and never
none while that source is current (the example on that page prints one). none is
taken over rows of the RELATION, not rows matching
where: an entity whose rows exist and do not match is recorded, not absent — "no
failed screening" is a count over rows that exist, never an absence verdict. The search
verb reports the same verdict per condition group
(search).