Skip to content
Settings

Searching entities

Cyoda exposes search over REST for any query that returns a set of entities. Use it when you need more than one entity back, when the filter goes beyond a single id lookup, or when you want to scope by workflow state. For single-entity reads, stay on the CRUD endpoints in working with entities; for cross-entity analytics, use SQL; for event-driven compute, use gRPC compute nodes.

Cyoda splits search into Immediate and Background modes. Pick by expected result size and urgency; the filter grammar is identical.

  • Immediate (API term: direct) — synchronous. The request returns matching entities in the response body. Result size is capped, so direct is the right default only when you know the filter produces a bounded, small set: a UI list, a lookup, a small report. If a query hits the cap, switch it to async.
  • Background (API term: async) — queued. The request returns a job handle; poll it to retrieve results. Result size is unbounded and results are paged. On the Cassandra-backed tier (Cyoda Cloud, or a licensed Enterprise install), async runs distributed across the cluster: for a fixed query shape, throughput scales roughly linearly with the number of nodes.

The decision tree is short:

  • Small bounded result, UI-facing → direct.
  • Might be large, can tolerate a second or two of queuing, exports, reports, batch jobs → async.
  • Hitting the cap or the request timeout on directasync.

Filter by a combination of entity fields and workflow state:

Terminal window
curl -X POST http://localhost:8080/api/search/direct/orders/1 \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{
"filter": {
"state": "submitted",
"customerId": "CUST-7"
}
}'

The path is /api/search/direct/{entityName}/{modelVersion}. The response is the list of matching entities, each with its current state, revision, and timestamps.

Submit the search to /api/search/async/{entityName}/{modelVersion} and capture the handle:

Terminal window
curl -X POST http://localhost:8080/api/search/async/orders/1 \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{
"filter": { "state": "submitted" }
}'

Poll the returned jobId until the job is ready, then fetch pages (pageNumber is zero-indexed; pageSize caps the page):

GET /api/search/async/{jobId}/status
GET /api/search/async/{jobId}/results?pageNumber=0&pageSize=1000
GET /api/search/async/{jobId}/results?pageNumber=1&pageSize=1000

A single jobId can be paged repeatedly until the result is expired; expiry is controlled per deployment.

If a job is no longer needed — the user navigated away, a replacement query was submitted, the deployment is shutting down — cancel it rather than letting it run to completion:

Terminal window
curl -X DELETE http://localhost:8080/api/search/async/{jobId}/cancel \
-H "Authorization: Bearer $TOKEN"

Cancellation is cooperative: in-flight work is stopped at the next safe point and any partial results for that jobId are discarded.

The filter is a JSON document whose fields are entity field paths, metadata (state, createdAt, …), or workflow labels. The authoritative operator grammar — equality, comparisons, ranges, set membership, AND/OR combinators, nested-field access — lives in the REST API reference. The shape used in the simple examples above (flat field→value map) is the equality short form; use the full object form when you need operators:

{
"and": [
{ "field": "state", "eq": "submitted" },
{ "field": "amount", "gte": 1000 }
]
}

For the full predicate grammar — every operator, nesting rule, and function — run cyoda help search against your binary.

Every search accepts a pointInTime parameter to run against the world as it existed at a given timestamp. Each entity maintains a history of revisions; point-in-time queries return results using the entity state that was current at the specified timestamp. The result is the set of entities that would have matched, using the revision active at that time.

Terminal window
curl -X POST http://localhost:8080/api/search/direct/orders/1 \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $TOKEN" \
-d '{
"pointInTime": "2026-03-01T00:00:00Z",
"filter": { "state": "submitted", "customerId": "CUST-7" }
}'

This is the primary way to answer audit and regulatory questions from REST — what did this customer’s open orders look like at quarter close? The Trino surface exposes the same capability as a column named point_time (snake-case, matching SQL convention); for the analytical form, see point_time in analytics.

  • pageSize and pageNumber are query parameters on /search/async/{jobId}/results; they apply at result-fetch time, not at job submission. pageNumber is zero-indexed.
  • Sort is not documented on the REST async surface at this release; results are returned in insertion order.
  • A completed jobId is stable for its retention window — page reads are idempotent.
  • Scope by state or a high-selectivity field first — the workflow state is indexed on every entity and is almost always the right first predicate.
  • Prefer async as soon as the result set might be thousands of entities; the distributed execution on the Cassandra tier makes it cheaper per entity than a series of direct pages.
  • Avoid open-ended pointInTime scans across every revision — anchor the query at a specific timestamp or a short window.