Your first graph query
Graph Query gq/1.3 keeps the compact, entity-first usability associated with Diffbot DQL while adding a stable product-owned AST, ontology validation, parameters, explain plans, and bounded execution. Every query begins with type:EntityType. Adjacent filters imply AND.
type:Organization location.country:"FI" employees>=100 sort:-employees return:entities,evidence limit:25Syntax reference
| Clause | Meaning |
|---|---|
| type:Organization | Required entity type |
| field:"value" · field="value" | Contains · exact equality |
| field!=value · > · >= · < · <= | Typed comparisons |
| AND · OR · NOT · ( ) | Nested Boolean predicates; AND is implicit between adjacent filters |
| field:any(a,b) · all(a,b) · none(a,b) | Array and multi-value matching |
| text:"search phrase" | Full-text match across identity and properties |
| has:field.path · missing:field.path | Existence and missing-value tests |
| sort:-employees,+label | Deterministic multi-field sort |
| select:businessId,employees | Property projection; identity fields remain stable |
| facet:industry,location.country | Value distribution before pagination, up to five fields |
| after:gqc_25 · limit:25 | Cursor pagination; maximum 100 entities |
| relation:subsidiary_of · direction:both · depth<=2 | Bounded relationship traversal, maximum four hops |
| return:entities,relationships,evidence | Returned graph layers |
Boolean, arrays, time, and ontology
Use nested AND/OR/NOT groups for predicates and any(), all(), or none() when a field has multiple values. ISO timestamps participate in ordered comparisons. target.* constrains traversal targets and relationship.* constrains relationship facts. Entity types, fields, capabilities, and relationships are validated against the product ontology; unknown fields return a suggestion instead of silently matching nothing.
type:Organization (industry:"Logistics" OR employees>=500) AND NOT missing:businessId aliases:any("Northstar","Polar Freight") observedAt>="2026-08-01T00:00:00Z"Bind untrusted values as parameters
Reference a named value as $name and send it in GraphQueryInput.parameters. Values are typed and never reparsed as query syntax, so SDK callers do not need to concatenate user input. Validation, explain, and execution resolve parameters identically.
{
"query": "type:Organization location.country:$country employees>=$minimum",
"parameters": { "country": "FI", "minimum": 100 }
}Facet, project, sort, and page
facet computes up to 100 value buckets from the complete filtered root set before pagination. select narrows entity properties without removing stable id, type, label, or evidence_ids. Multi-sort is deterministic and the response page.next_cursor can be supplied as cursor in the next API request or as after: in the language.
type:Organization location.country:"FI" facet:industry select:businessId,employees,industry sort:-employees,+label limit:25Traverse relationships safely
Traversal is opt-in and always bounded. direction and depth are valid only with a relation clause. Queries may traverse at most four hops, preventing accidental open-ended graph scans.
type:Organization businessId="3391028-4" relation:subsidiary_of direction:out depth<=2 target.location.country:"FI" relationship.confidence>=0.9 return:entities,relationships,evidenceExplain before execution
POST /v1/graph/query/validate checks syntax and ontology. POST /v1/graph/query/explain returns the canonical plan, index use, warnings, result range, and credit estimate without executing. POST /v1/graph/query executes the same expression with the graph:read scope.
curl -X POST https://api.enrich.nordicdevhouse.com/v1/graph/query \
-H "Authorization: Bearer enrich_live_••••••••" \
-H "Content-Type: application/json" \
-d '{"query":"type:Organization location.country:\"FI\" employees>=100 return:entities,evidence"}'Publish claims as immutable workspace releases
POST /v1/graph/publications accepts an idempotent batch of entity, relationship, and evidence claims with the graph:write scope. The complete batch becomes one content-addressed release, so readers never observe a partially published graph. Supply expected_parent_release_id when concurrent writers must not overwrite a newer active release.
{
"source_id": "crm-import-2026-08-17",
"expected_parent_release_id": "wgr_previous",
"claims": [
{ "entity_id": "organization:3391028-4", "entity_type": "organization", "label": "Northstar Oy", "attribute": "businessId", "value": "3391028-4" }
]
}Inspect, compare, and activate releases
List or read releases, compare any release to an explicit base, and activate a previous release as a controlled rollback. Activation uses expected_current_release_id for optimistic concurrency and records actor and reason in channel history. Queries always report the exact workspace_graph_release_id they used.
Separate metadata from immutable artifacts
Production stores workspace channels, idempotency keys, ingestion state, ontologies, and events in PostgreSQL. Content-addressed Parquet releases live in S3-compatible object storage and become visible only after a commit marker. SQLite and local files implement the same boundary for local development.
Ingest only changed source records
Create a source with its connector, entity type, identity field, label field, and field mapping. Every run uses a stable source_event_id and can run inline or through the durable worker queue. Per-record fingerprints skip unchanged input, valid changes become one immutable graph release, abandoned leases are reclaimed, and invalid rows move to a dead-letter list for corrected retry.
Plan against release-scoped indexes
The query engine intersects type, exact-value, existence, and full-text indexes before evaluating fallback predicates. Candidate and wall-time budgets stop expensive plans, repeated plans use an LRU cache, and every query response reports indexes, candidate count, evaluated count, cache status, fallbacks, and elapsed time.
Scale reads with cost statistics, shards, and query workers
Every graph release carries a query-projection/2 artifact. A bounded local LRU caches immutable projections, while stable workspace shards partition artifacts and durable query work. Explain plans use field cardinality and relationship degree statistics to estimate fanout, relationship reads, working-set bytes, and execution class. Query workers heartbeat leases, retry with backoff, expose dead letters, and support explicit cancel and operator retry.
Validate before activating the workspace ontology
Create an immutable draft, validate compatibility and live-data impact, then activate it. Controlled migrations rename existing graph attributes; portable export/import moves schemas between workspaces. Incompatible or data-affecting activation returns 409 unless explicitly allowed.
Consume events and release deltas
List append-only graph events with a cursor or consume the same sequence as server-sent events. Event subscriptions project matching changes into a durable webhook outbox. Bulk consumers can export an NDJSON delta from an explicit base release to a target release and resume without rebuilding the graph.
The same contract in every SDK
Generated clients expose queryKnowledgeGraph, validateKnowledgeGraphQuery, and explainKnowledgeGraphQuery. All accept GraphQueryInput, so copied queries and cost plans behave identically in every generated language.
const graphApi = new KnowledgeGraphApi(configuration);
const result = await graphApi.queryKnowledgeGraph({
graphQueryInput: {
query: 'type:Organization location.country:$country employees>=100 facet:industry return:entities,evidence',
parameters: { country: 'FI' }
}
});Evidence remains explicit
Include evidence only when the caller needs provenance. Entities and relationships carry evidence_ids; the response evidence array resolves those identifiers to source, observed time, and URL. Confidence never turns an unreviewed relationship into an accepted fact.
Limits and errors
Queries are limited to 2,000 characters, 25 filters, 100 returned entities, and four relationship hops. Invalid expressions return stable error codes with the character position. Unknown fields fail ontology validation and include a suggested field when one is close.