Declaring data
xyTalk is the whole interface — there is no separate schema language. Declare a space, an identity, and how data co-locates; the engine owns the rest.
Hello world
A space, an identity, two linked records, one traversal.
LOBE "workspace" ANCHOR "code" UNIQUE IN "workspace" PUT {_type: "Company", code: "ACME", name: "Acme Corp"} IN "workspace" PUT {_type: "Project", project_id: "P1", budget: 50000} IN "workspace" LINK TO "workspace" WHERE code = "ACME" AS "owner" FIND "workspace" WHERE code = "ACME" | PULL depth=2
Writing records
PUT writes one record; PUT BATCH writes up to 10,000 atomically. Add ON CONFLICT UPDATE to upsert on an anchor.
PUT BATCH IN "creditos" [ {*rfc: "ACME-001", _type: "Installment", amount: 1000}, {*rfc: "ACME-001", _type: "Installment", amount: 1500} ]
Declaring gravity
A bare *field in PUT is sugar for raw gravity. GRAVITY BY declares it per lobe — raw, normalized, or composite.
GRAVITY BY rfc IN "creditos" # raw GRAVITY BY lower(email) IN "users" # normalized GRAVITY BY (region, tier) IN "accounts" # composite
Links
LINK adds a _link_<relation> field (the target LID) to each source record, so PULL can traverse it.
LINK "pagos" WHERE credito = "C-1" TO "creditos" WHERE id = "C-1" AS "credit"
Ghosts & vector fields
CREATE GHOST declares like the query it serves — a pipeline ending in TAKE BY (the order step is mandatory); the classic ORDER BY clause form is a live alias of the same statement.
CREATE GHOST "top_exposure_by_rfc" FROM "creditos" WHERE _type = "Credit" | GROUP BY rfc | AGGREGATE count(), sum(monto) | TAKE BY sum(monto) DESC VECTOR embedding IN "memories"
A ghost ordered by a declared metric keeps a metric-ordered rollup, so a matching TAKE n BY reads the first N in O(N) instead of O(M). Manage ghosts with REFRESH GHOST / DROP GHOST.