Relations that route — joins, FK placement, salting #
A fact collection can now behave like one. Declare the relationship in the schema and the engine takes over:
"org_id": {
"type": "keyword",
"immutable": true,
"references": {"collection": "orgs"},
"routing": true,
"ref": true
}
- Join queries are live — a
semi-join over the wire: the inner query runs on the referenced
collection, its matching keys become a membership filter on the outer
side, and joins through a declared
referencesfield resolve without a second fan-out. - Writes place by the foreign key’s value —
hash(org_id)picks the home partition, not the document key, so an org’s facts colocate. Key-addressed operations take?routing=<value>( fetch, update, delete). - Salting splits the hot org — when one routing value dominates, the _salting API spreads its writes across a partition SET (deterministic per key, round-robin for auto-IDs), with a terms feed that flags candidates against your threshold. Assignments are never auto-applied and searches stay oblivious — salting changes placement, never visibility.
One declaration, three consequences:
graph LR
D["one field declaration
references: orgs · routing: true"] --> J("joins — a semi-join,
keys ride the declared field")
D --> P("placement — hash(org_id)
picks the home partition")
D --> S("salting — a hot value spreads
over a partition SET")
The
joins tutorial walks the whole story against
a running node — placement observable in the response _id, fail-closed
400s, the salting feed, the salt-split spread, joins before and after.