Querying with HXQL
This tutorial shows how to search the Cloud Content Repository with HXQL (Hyland Experience Query Language) through the Query APIs. It covers running a query, the HXQL syntax and its gotchas, filtering on ingest properties, context properties and knowledge graph processing states, counting without SUM or COUNT, parameterised and named queries, and terms aggregations.
The examples were run against a live Cloud Content Repository environment. The counts show what that environment returned; yours will differ.
Prerequisites
- A valid user access token for the signed-in user you are searching for, not a service user token: document ACLs are applied to the caller (see Authentication)
- The base URL of your environment
Set two variables before running the curl examples. The HTTP examples use the equivalent {{baseUrl}} and {{accessToken}} placeholders.
export BASE_URL="https://{environment_id}.content-lake.ai.experience.hyland.com"
export TOKEN="<access token of the signed-in user>"
Tutorial Overview
In this tutorial, you'll learn to:
- Run a query with
POST /api/queryand page through the results - Write HXQL:
SELECT,WHERE, operators, literals, sorting and pagination - Filter on ingest properties, context properties, and KG processing states
- Count and aggregate documents
- Build parameterised queries with
POST /api/query/advanced - Run server-defined named queries
- Get value distributions with
POST /api/query/termsAggregation - Wait for indexing to finish after ingestion
Run a Query
Send an HXQL statement in the query field of POST /api/query.
- HTTP
- curl
POST {{baseUrl}}/api/query
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
Content-Type: application/json
{
"repositoryId": "default",
"query": "SELECT * FROM SysContent WHERE cin_type = 'CLI Ingestion Type'",
"syntax": "hxql",
"limit": 10,
"offset": 0,
"sort": ["cin_dateCreated DESC"],
"trackTotalCount": true
}
curl -X POST "$BASE_URL/api/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.hxcs.v1+json" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"repositoryId": "default",
"query": "SELECT * FROM SysContent WHERE cin_type = 'CLI Ingestion Type'",
"syntax": "hxql",
"limit": 10,
"offset": 0,
"sort": ["cin_dateCreated DESC"],
"trackTotalCount": true
}
JSON
Parameters
| Field | Type | Required | Description |
|---|---|---|---|
repositoryId | string | No | Repository to search (default "default") |
query | string | No | Query in the syntax given by syntax. If blank, defaults to SELECT * FROM SysContent |
syntax | string | No | "hxql" (default): a full HXQL statement. "expr": an expression used as a filter, e.g. content.sys_title == "foo" |
limit | integer | No | Max documents to return (default 10). Values above 10,000 are capped at 10,000 |
offset | integer | No | Number of documents to skip (default 0). limit + offset must be ≤ 10,000, otherwise 400 |
searchAfter | string | No | Opaque cursor token for deep pagination: pass the nextSearchAfter value from the previous response. When provided, offset is ignored and the 10,000 window check does not apply |
sort | string[] | No | Sort fields, each optionally followed by " ASC" or " DESC" (e.g. "sys_title DESC"). When present, replaces any ORDER BY in the query |
trackTotalCount | boolean | No | Whether to compute totalCount (default true) |
trackTotalCountUpTo | integer | No | Max number of documents to count (default 10,000). -1 removes the limit, which is expensive on large indexes |
The request body itself is optional; an empty request runs SELECT * FROM SysContent.
| Parameter | In | Type | Description |
|---|---|---|---|
sync | query | boolean | Default false (search the OpenSearch index). true runs the query directly against the repository instead. Also supported on /advanced and /named. With sync=true, totalCount and nextSearchAfter are not reliable (for example totalCount: -2). |
Response
{
"repositoryId": "default",
"query": "SELECT * FROM SysContent WHERE cin_type = 'CLI Ingestion Type' ORDER BY cin_dateCreated DESC",
"limit": 10,
"offset": 0,
"documents": [ ... ],
"count": 10,
"totalCount": 42,
"nextSearchAfter": "eyJ..."
}
| Field | Description |
|---|---|
query | The query as executed (including any sort and bound parameters) |
syntax | Only present when it is not hxql |
documents | The matching documents |
count | Number of documents in this page |
totalCount | Total matching documents. Omitted when trackTotalCount is false (the response then contains "trackTotalCount": false) |
totalCountIsTruncated | Only present (as true) when the count stopped at trackTotalCountUpTo |
nextSearchAfter | Cursor for the next page. Only returned when the page is full and the order comes from the request's sort field; it is not returned when the order comes only from an ORDER BY in the HXQL. |
- All query endpoints require authentication. Results only include documents the caller is allowed to read.
- Errors are returned as
{"entity-type": "exception", "status": <code>, "message": "..."}. An invalid HXQL query returns400. - If the search index is unreachable, the search endpoints return
503with aRetry-Afterheader (seconds). The request can be retried.
- Use
SELECT *. On the default (OpenSearch) path, naming fields inSELECT(e.g.SELECT sys_title FROM SysContent) returns an emptydocumentslist and"totalCount": 0, with no error. This applies to every field, not only typeanyfields - Type
anyfields (cin_ingestProperties,cinctx_contextProperties) CAN be used inWHEREclauses with the slash path syntax:WHERE cin_ingestProperties/Status = 'Executed',WHERE cinctx_contextProperties/textMetadataGeneration/contract:type = 'Amendment' - On list fields, both
=andIN()match when any element matches:WHERE cin_ingestPropertyNames IN ('BuyerOrg', 'Meta') - HXQL has no
SUM,COUNT,GROUP BY,DISTINCT, orLIMIT. See Counting and aggregating for the supported alternatives - See the HXQL Guide for the full syntax and operator caveats (
LIKE,ILIKE,STARTSWITH)
Basic Syntax
HXQL queries are passed as a string in the query field of the POST /api/query request body. The syntax resembles SQL but operates against the Cloud Content Repository document index.
{
"limit": 10,
"offset": 0,
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Executed'",
"repositoryId": "default",
"sort": ["sys_title"],
"syntax": "hxql",
"trackTotalCount": true,
"trackTotalCountUpTo": 100
}
"syntax" defaults to "hxql", so it can be omitted. The other accepted value, "expr", takes an expression used as a filter instead of an HXQL statement. If query is blank, SELECT * FROM SysContent is used.
The FROM clause names a document type and matches that type and all its subtypes. SysContent is the base type that all documents inherit from, so FROM SysContent searches everything. You can name a more specific type (e.g. FROM SysFile) or several types separated by commas (FROM SysFile, SysFolder). An unknown type name makes the query fail with 400 Unknown type: <name>.
Keywords are case-insensitive (select, SELECT, and Select are equivalent). ORDER BY is written with a single space. HXQL has no LIMIT or OFFSET clause; use the limit and offset fields of the request body. SELECT * FROM SysContent LIMIT 5 fails with 400 mismatched input 'LIMIT' expecting {<EOF>, 'WHERE', 'ORDER BY', ','}.
A syntax error returns 400 with the position of the problem, for example SELECT * FROM SysContent WHERE returns Query: SELECT * FROM SysContent WHERE, At position 1:30 mismatched input '<EOF>' expecting {...}.
SELECT — What You Get Back
SELECT *
Returns all fields on each matching document, including system fields, ingest metadata, and context enrichment data:
SELECT * FROM SysContent
Named Fields
The grammar accepts a list of named fields:
SELECT sys_id, sys_title, cin_type, cin_createdBy, cin_dateCreated
FROM SysContent
Critical rule: On the default (OpenSearch) query path, POST /api/query returns documents only for SELECT *. Naming fields in SELECT — any fields, not only type any ones — returns an empty documents list and "totalCount": 0, with no error message. Always use SELECT *. You cannot mix * with named fields.
WHERE — Filtering Documents
Use WHERE to filter documents by their properties:
SELECT * FROM SysContent
WHERE cin_type = 'CLI Ingestion Type'
Combine multiple conditions with AND / OR:
SELECT * FROM SysContent
WHERE cin_type = 'CLI Ingestion Type'
AND cin_createdBy = 'CLI Ingestion'
AND cin_dateCreated >= '2025-01-01T00:00:00Z'
The values in these examples (and in the galleries below) come from a live test environment. Use terms aggregation to discover the values that exist in yours.
Type any Fields — The Critical Rule
Several important fields in the content model are typed as any — meaning they hold untyped JSON blobs. These fields follow special rules in HXQL:
| Field | Mixin Type | What It Holds |
|---|---|---|
cin_ingestProperties | CinRemote | Metadata written during ingestion (DocType, BuyerOrg, etc.) |
cinctx_contextProperties | CinContext | Context enrichment results (summaries, classifications, NER, etc.) |
cinctx_kgProcessingStates is not type any: it is a typed list of {ontologyId, state, status} entries. See Querying KG Processing States.
The rule for type any fields:
- SELECT: Use
SELECT *and they will appear in the response. Naming them (or any other field) in SELECT returns no documents. - WHERE: Works with the slash path syntax to reach into the JSON structure. This is how you filter by these fields.
- IS NOT NULL: Works — tests whether the field exists on the document.
-- WRONG: naming fields in SELECT returns 0 documents
SELECT cin_ingestProperties FROM SysContent -- returns 0!
SELECT cinctx_contextProperties FROM SysContent -- returns 0!
-- CORRECT: use SELECT * and filter in WHERE
SELECT * FROM SysContent
WHERE cin_ingestProperties/DocType = 'SLA'
-- CORRECT: mix named fields with SELECT * is not possible,
-- so just use SELECT * when you need type 'any' fields
SELECT * FROM SysContent
WHERE cinctx_contextProperties/textClassification/result = 'Test Document'
Path Syntax for Nested Properties
Type any fields store arbitrary JSON. You can reach into them using the slash path syntax in WHERE clauses, going as deep as needed:
-- 1 level deep
WHERE cin_ingestProperties/DocType = 'SLA'
-- 2 levels deep
WHERE cinctx_contextProperties/textClassification/result = 'Test Document'
-- 3 levels deep
WHERE cinctx_contextProperties/textSummary/isSuccess = true
-- 4 levels deep (into a nested object within an enrichment result)
WHERE cinctx_contextProperties/namedEntityText/result/organizations IN ('Hyland Software')
-- Keys that contain ':' are written as-is
WHERE cinctx_contextProperties/textMetadataGeneration/contract:type = 'Amendment'
Tip: To find out what keys exist inside a type any field, run SELECT * FROM SysContent WHERE sys_id = 'your-doc-id' and inspect the full JSON response. The field structure depends on what was written during ingestion or enrichment.
Array Fields and IN()
When a field or nested value is an array, a condition matches if any element of the array matches. This is true for both = and IN(); the difference is that IN() accepts several candidate values (it matches if the field equals any of them):
-- cinctx_contextPropertyNames is a stringList (array of strings)
WHERE cinctx_contextPropertyNames = 'textSummary' -- matches if the array contains this value
-- organizations is an array inside namedEntityText.result
WHERE cinctx_contextProperties/namedEntityText/result/organizations = 'Hyland Software'
-- IN(): matches if the array contains any of the listed values
WHERE cinctx_contextProperties/namedEntityText/result/organizations IN ('Hyland Software', 'Acme Corp')
Rule of thumb: Use = to test for one value and IN() to test for any of several values. Use NOT IN() to exclude values.
Operators Reference
| Operator | Description | Example |
|---|---|---|
= | Equals | cin_type = 'Invoice' |
<>, != | Not equals | cin_type <> 'Draft' |
>, >=, <, <= | Comparison | cin_dateCreated >= '2025-01-01T00:00:00Z' |
[NOT] BETWEEN x AND y | Inclusive range | cin_dateCreated BETWEEN '2025-01-01T00:00:00Z' AND '2025-12-31T23:59:59Z' |
[NOT] LIKE | Case-sensitive pattern match (see LIKE rules below) | cin_ingestProperties/BuyerOrg LIKE '%Evergreen%' |
[NOT] ILIKE | Case-insensitive pattern match. It matches against the field's .lowercase sub-field in the index, so it only works where the index mapping defines one. In the tested environment it returned 0 results on sys_title, sys_name, and ingest properties, without an error | sys_title ILIKE 'cli%' → 0 results |
STARTSWITH | Path-hierarchy match, not a string prefix. sys_path STARTSWITH '/folder' matches the documents below /folder (not the folder itself). On an ordinary text field it matches nothing: sys_name STARTSWITH 'doc' returned 0 results. For a string prefix, use LIKE (see LIKE rules) | sys_path STARTSWITH '/folder' |
[NOT] IN () | Equals any of the listed values | cinctx_contextPropertyNames IN ('textSummary', 'piiRecognition') |
IS NULL | Field does not exist | cinctx_contextPropertyNames IS NULL |
IS NOT NULL | Field exists | cinctx_contextPropertyNames IS NOT NULL |
sys_fulltext = '...' | Full-text search across the document. Terms are combined with AND; use OR between terms for either | sys_fulltext = 'contract renewal' |
AND | Logical AND | cin_type = 'SLA' AND cin_createdBy = 'ADMIN' |
OR | Logical OR | cin_type = 'Invoice' OR cin_type = 'Contract' |
NOT | Logical negation | NOT (cin_type = 'Invoice' OR cin_type = 'Contract') |
( ) | Grouping | cin_type = 'SLA' AND (cin_createdBy = 'ADMIN' OR cin_createdBy = 'MANAGER') |
LIKE Wildcards and Rules
%(or*) matches any sequence of characters_matches exactly one character- A backslash escapes the next wildcard character. Because the backslash must itself be escaped inside an HXQL string, write it doubled:
LIKE '100\\%'matches the literal text100% LIKEis case-sensitive:cin_ingestProperties/BuyerOrg LIKE '%Evergreen%'matched 71 documents,LIKE '%evergreen%'matched 0
A single trailing wildcard fails on keyword fields. A pattern whose only wildcard is a % at the end (e.g. 'Evergreen%') is run as a phrase-prefix query, which only works on text fields. On keyword fields — ingest properties, sys_name, and others — it fails:
WHERE cin_ingestProperties/BuyerOrg LIKE 'Evergreen%'
-- 400: Error performing OpenSearch query: failed to create query: Can only use phrase prefix
-- queries on text fields - not on [cin_ingestProperties.BuyerOrg] which is of type [keyword]
Workarounds that were tested: a leading and trailing wildcard ('%Evergreen%', 71 results), an _ wildcard ('Evergreen Retail Cooperativ_', 51 results), or an exact = match. On text fields such as sys_title, LIKE 'CLI%' works (2702 results).
Security properties: Some fields that hold access-control data, such as sys_acl, sys_effectiveAcl and sys_effectivePermissions, cannot be used anywhere in a query (SELECT, WHERE, or ORDER BY). The query fails with 400 Disallowed system property: <name>, even for a caller with the hxpr.manage.everything permission. sys_racl and sys_secpol are not rejected. The same error is returned for other system fields that are not queryable.
Literals, Escaping, and Functions
| Literal | Example |
|---|---|
| String (single or double quotes) | 'Invoice', "Invoice" |
| Integer / decimal | 42, -3.5, 1E3 |
| Boolean (case-insensitive) | true, FALSE |
| Date / timestamp | DATE '2025-01-01', TIMESTAMP '2025-01-01T00:00:00Z' |
Match the stored type. Values inside type any fields keep the JSON type they were written with. In the tested environment, cin_ingestProperties/cinIsTld holds the string "True": cinIsTld = 'True' matched 418 documents, but the boolean literal cinIsTld = TRUE matched 0. Numbers stored as numbers (such as PageCount) match both = 3 and = '3' (45 documents each). Inspect a document with SELECT * to see how a value is stored. Real JSON booleans match both = FALSE and = 'false', but a capitalised string such as = 'False' against a boolean field fails with 400 ... Can't parse boolean value [False], expected [true] or [false].
Escaping
Inside a string, use a backslash to escape: \' (in single-quoted strings), \" (in double-quoted strings), \\, \t, \n, \r. Doubling quotes SQL-style ('') is not supported.
WHERE sys_title = 'O\'Brien contract'
WHERE sys_title = "O'Brien contract" -- or switch quote style
NOW()
NOW() is the only supported function. It returns the current UTC date-time, optionally shifted by an ISO 8601 period passed as a string. Any other function name fails with Unknown function.
-- Documents created in the last 7 days
SELECT * FROM SysContent
WHERE cin_dateCreated >= NOW('-P7D')
Named Parameters
A query can contain :name placeholders, which are bound from the parameters object of POST /api/query/advanced or a named query. A placeholder left without a value fails the query.
Sorting and Pagination
ORDER BY
Sort results within the HXQL query or via the sort field in the request body:
-- In HXQL
SELECT * FROM SysContent
WHERE cin_type = 'CLI Ingestion Type'
ORDER BY cin_dateCreated DESC
-- Or via the request body sort field ("field", "field ASC", or "field DESC")
"sort": ["cin_dateCreated DESC", "sys_title"]
ORDER BY accepts several fields, each with an optional ASC (default) or DESC. A non-empty sort in the request body replaces the query's ORDER BY; it is not combined with it.
Pagination
Use limit (default 10) and offset for basic pagination. limit + offset must not exceed 10,000; beyond that the request fails with 400. For deep pagination, put the order in the request's sort field (not an ORDER BY in the HXQL, which suppresses the cursor) and pass the nextSearchAfter token from the previous response as searchAfter. nextSearchAfter is only returned when the page is full. Very large pages can come back one document short without a cursor even though more results exist, so don't rely on a missing cursor alone: stop when a page returns no documents.
-- Page 1
{ "limit": 25, "offset": 0, "query": "SELECT * FROM SysContent", ... }
-- Page 2 (basic)
{ "limit": 25, "offset": 25, "query": "SELECT * FROM SysContent", ... }
-- Deep pagination (searchAfter = nextSearchAfter from previous response; offset is ignored)
{ "limit": 25, "searchAfter": "token-from-previous-response", ... }
Total Count
trackTotalCount defaults to true, so totalCount is returned unless you set it to false. trackTotalCountUpTo (default 10,000) limits how far the count goes; -1 removes the limit, which is expensive on large indexes:
{ "trackTotalCount": true, "trackTotalCountUpTo": 100 }
-- Response: "totalCount": 100, "totalCountIsTruncated": true (if more than 100 match)
Querying Context Engine Fields
After the Context Engine enriches a document, three fields are written (requires the CinContext mixin on the document):
cinctx_contextPropertyNames
A stringList (array) listing which enrichment actions were applied. Query to find documents that have a specific enrichment:
-- Find documents with text summaries
SELECT * FROM SysContent
WHERE cinctx_contextPropertyNames = 'textSummary'
-- Find any enriched documents
SELECT * FROM SysContent
WHERE cinctx_contextPropertyNames IS NOT NULL
The values depend on which enrichment actions ran. Values seen in the tested environment: textMetadataGeneration (the most common, on about 17,900 documents), and textSummary, textClassification, namedEntityText. Other enrichment actions may write other names (for example imageDescription, textEmbeddings, piiRecognition); this could not be confirmed against the tested data.
cinctx_contextProperties
A type any JSON blob containing the actual enrichment results. Use the slash path syntax in WHERE:
-- Find documents classified as a specific type
SELECT * FROM SysContent
WHERE cinctx_contextProperties/textClassification/result = 'Test Document'
-- Find documents mentioning a specific organization
SELECT * FROM SysContent
WHERE cinctx_contextProperties/namedEntityText/result/organizations IN ('Hyland Software')
-- Find documents mentioning a specific person
SELECT * FROM SysContent
WHERE cinctx_contextProperties/namedEntityText/result/persons IN ('Test User')
-- Find documents with summaries containing a keyword
SELECT * FROM SysContent
WHERE cinctx_contextProperties/textSummary/result LIKE '%testing%'
-- Find documents where a specific enrichment succeeded
SELECT * FROM SysContent
WHERE cinctx_contextProperties/textSummary/isSuccess = true
Enrichment Result Structure
Each enrichment action writes a key inside cinctx_contextProperties with this shape:
{
"textSummary": {
"isSuccess": true,
"result": "This document describes the terms of..."
},
"textClassification": {
"isSuccess": true,
"result": "Service Level Agreement"
},
"namedEntityText": {
"isSuccess": true,
"result": {
"organizations": ["Hyland Software", "Acme Corp"],
"persons": ["Jane Doe"],
"locations": ["Cleveland, Ohio"]
}
}
}
textMetadataGeneration has a different shape: a flat object whose keys are prefixed (e.g. contract:). In some environments the same prefixed keys (such as document:type or contract:number) are also stored directly under cinctx_contextProperties and listed in cinctx_contextPropertyNames, so check both places. Values are strings or arrays of strings, and a value that was not found is stored as an empty string. A trimmed example from the tested environment:
{
"textMetadataGeneration": {
"contract:type": "Amendment",
"contract:commercialCategory": "Amendment",
"contract:effectiveDate": "2024-04-08",
"contract:expirationDate": "",
"contract:engagementModel": "SaaS Subscription",
"contract:partyNames": ["HYLAND SOFTWARE, INC.", "..."],
"contract:servicesInScope": "Maintenance and Support",
"contract:currency": "",
"contract:value": ""
}
}
The same key can be a string on one document and an array on another (contract:servicesInScope is "Maintenance and Support" above and ["Support services", "Training services"] elsewhere). Conditions work on both.
Querying KG Processing States
cinctx_kgProcessingStates is a typed list (part of the CinContext mixin) where each entry tracks per-ontology Knowledge Graph processing with ontologyId, status, and state sub-fields. An entry from the tested environment:
{
"ontologyId": "c72b861d-cf97-4255-8d68-8259341403e2_8bedb854-0333-488a-9dbb-9ac61f0077e4",
"status": "SYNCED",
"state": { "filename": "..._kg_processing_state.json", "mimeType": "application/json", "length": 96858, ... }
}
ontologyIdis a composite of<graphDBId>_<ontologyId>: the graph database ID and the ontology ID joined by an underscore. To match one ontology across graph databases, useLIKE '%<ontologyId>'statusis a free-form string written by the processing service. Values seen in the tested environments includeSYNCED, the in-progressARCHITECTEDandMINED, and the failuresARCHITECT_FAILURE,MINER_FATAL_FAILUREandSYNC_FATAL_FAILURE; one environment also hadcompletedstateis a blob reference to the processing-state file
Reach into list entries with cinctx_kgProcessingStates/*/subField. For terms aggregation, the OpenSearch field path uses dot-notation: cinctx_kgProcessingStates.subField.
-- Find documents processed with a specific graph DB + ontology pair
SELECT * FROM SysContent
WHERE cinctx_kgProcessingStates/*/ontologyId = 'c72b861d-cf97-4255-8d68-8259341403e2_31944a57-cb89-406c-9f7d-96631d305f8f'
-- Find documents where KG processing synced
SELECT * FROM SysContent
WHERE cinctx_kgProcessingStates/*/status = 'SYNCED'
-- Find documents where KG processing failed
SELECT * FROM SysContent
WHERE cinctx_kgProcessingStates/*/status = 'SYNC_FATAL_FAILURE'
Note: Conditions on different sub-fields are not tied to the same list entry. cinctx_kgProcessingStates/*/ontologyId = 'a' AND cinctx_kgProcessingStates/*/status = 'failed' also matches a document where ontology a completed but another ontology failed.
Querying Ingest Properties
cin_ingestProperties is a type any field written during ingestion. Its structure depends on the connector or ingestion pipeline that pushed the document. Common patterns:
A trimmed cin_ingestProperties value from the tested environment (a contract ingested by a CLI pipeline):
{
"BuyerOrg": "Evergreen Retail Cooperative",
"SellerOrg": "Crestline Data Systems, Inc.",
"DocType": "SOW",
"Status": "Executed",
"Jurisdiction": "Ohio",
"EffectiveDate": "2024-08-04",
"PageCount": 3,
"Topics": "SOW;Milestones;Acceptance;Billing;Change Control",
"AmendmentId": "",
"integrationName": "CLI Ingestion Name",
"integrationDate": "2026-03-01T18:21:48.897Z",
...
}
Values keep their JSON type: PageCount is a number, dates are strings, and flags such as cinIsTld are the strings "True" / "False". These are keyword fields, so LIKE 'x%' fails; use '%x%' or =.
-- Filter by document type set during ingestion
SELECT * FROM SysContent
WHERE cin_ingestProperties/DocType = 'SLA'
-- Filter by a custom property
SELECT * FROM SysContent
WHERE cin_ingestProperties/BuyerOrg = 'BluePeak Insurance Group'
You can also use cin_ingestPropertyNames (a stringList) to see which ingest properties were set, similar to cinctx_contextPropertyNames. More variations are in the ingest properties gallery.
Terms Aggregation
The POST /api/query/termsAggregation endpoint lets you get distribution counts for a property across matching documents. This uses an HXQL query to define the document set, then aggregates on a specified field. The top-level limit is the number of buckets (default 10); searchTerm restricts the aggregation to documents whose property value contains that text, and returns only buckets whose key contains it (both case-insensitive):
{
"limit": 100,
"query": {
"limit": 100,
"offset": 0,
"query": "SELECT * FROM SysContent",
"repositoryId": "default",
"sort": ["sys_title"],
"syntax": "hxql",
"trackTotalCount": false,
"trackTotalCountUpTo": 100
},
"searchTerm": "",
"termsAggregationProperty": "cinctx_contextPropertyNames"
}
This returns buckets like:
{
"aggregationsBuckets": [
{ "key": "textMetadataGeneration", "docCount": 1000 }
],
"documents": [],
"count": 0,
...
}
documents is always empty. Unless the inner query sets "trackTotalCount": false, the response also contains "totalCount": 0 and "totalCountIsTruncated": true; ignore them, they don't describe the buckets. Counts are computed over a sample of up to 1,000 matching documents per index shard, so on large document sets they are approximate. The response above is a real example of this: it reports 1,000 documents and no other values, although about 17,900 documents have textMetadataGeneration and one document also has textSummary, textClassification, and namedEntityText.
Buckets are a sample. Rare values can be missing and counts are lower than the real totals. To measure coverage exactly, run one query per value and read totalCount (e.g. WHERE cinctx_contextPropertyNames = 'textSummary' with "trackTotalCountUpTo": -1). Narrowing the aggregation query (for example to one ingestion source) makes the sample more representative.
- Aggregate nested and list sub-fields with dot-notation:
cinctx_kgProcessingStates.statusworks, whilecinctx_kgProcessingStates/*/statusreturns no buckets - Numeric properties (such as
cin_ingestProperties.PageCount) return400... Can only use wildcard queries on keyword and text fields - not on [cin_ingestProperties.PageCount] which is of type [long]
SUM, COUNT, GROUP BY — What to Use Instead
HXQL has no aggregate functions or grouping. Queries that try them behave as follows:
SELECT COUNT(*) FROM SysContent→400At position 1:13 extraneous input '*' expecting {...}SELECT SUM(cin_ingestProperties/PageCount) FROM SysContentandSELECT COUNT(sys_id) FROM SysContentparse, but are treated as named-field SELECTs:200, no documents,"totalCount": 0. Nothing is summed or counted... GROUP BY cin_ingestProperties.Status→400mismatched input 'GROUP' expecting {<EOF>, 'WHERE', 'ORDER BY', ','}SELECT DISTINCT cin_ingestProperties.Status FROM SysContent→400extraneous input 'cin_ingestProperties.Status' expecting 'FROM'
| SQL you might write | Supported alternative | Exact? |
|---|---|---|
SELECT COUNT(*) ... WHERE ... | POST /api/query with the WHERE clause, "limit": 1, "trackTotalCountUpTo": -1; read totalCount | Yes |
SELECT x, COUNT(*) ... GROUP BY x | POST /api/query/termsAggregation with termsAggregationProperty: x | No — sample-based |
SELECT DISTINCT x | The bucket keys of a terms aggregation on x | Rare values may be missing |
SUM(x), AVG(x), MIN(x), MAX(x) | Page through the results with searchAfter and compute in your client. For MIN / MAX only, sort on x and read the first document | Yes |
COUNT: exact total
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Executed'",
"limit": 1,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{ "totalCount": 1083, "count": 1, ... }
With the default trackTotalCountUpTo (10,000) the count stops at 10,000 and "totalCountIsTruncated": true is returned; with "trackTotalCountUpTo": 100 the same query returned "totalCount": 100, "totalCountIsTruncated": true.
COUNT per value (ingest property)
{
"query": { "query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Executed'" },
"termsAggregationProperty": "cin_ingestProperties.DocType",
"limit": 5
}
-- Response (trimmed)
{
"aggregationsBuckets": [
{ "key": "Contract", "docCount": 315 },
{ "key": "SLA", "docCount": 218 },
{ "key": "SOW", "docCount": 186 },
{ "key": "Amendment", "docCount": 167 },
{ "key": "ApprovalMemo", "docCount": 66 }
],
"documents": [],
"count": 0
}
COUNT per value with searchTerm (ingest property)
{
"query": { "query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg'" },
"termsAggregationProperty": "cin_ingestProperties.BuyerOrg",
"limit": 5,
"searchTerm": "health"
}
-- Response (trimmed): searchTerm is case-insensitive
"aggregationsBuckets": [
{ "key": "Northfield Health Plan, Inc.", "docCount": 84 },
{ "key": "Northfield Health Plan", "docCount": 52 }
]
COUNT per value (context property)
{
"query": { "query": "SELECT * FROM SysContent WHERE cinctx_contextPropertyNames = 'textMetadataGeneration'" },
"termsAggregationProperty": "cinctx_contextProperties.textMetadataGeneration.contract:engagementModel",
"limit": 5
}
-- Response (trimmed): "" is the bucket for documents where no value was found
"aggregationsBuckets": [
{ "key": "", "docCount": 420 },
{ "key": "Professional Services", "docCount": 327 },
{ "key": "SaaS Subscription", "docCount": 132 },
{ "key": "Managed Services", "docCount": 45 },
{ "key": "Hybrid", "docCount": 25 }
]
COUNT per value (KG status)
{
"query": { "query": "SELECT * FROM SysContent WHERE cinctx_kgProcessingStates/*/ontologyId IS NOT NULL" },
"termsAggregationProperty": "cinctx_kgProcessingStates.status"
}
-- Response (trimmed)
"aggregationsBuckets": [
{ "key": "SYNCED", "docCount": 999 },
{ "key": "SYNC_FATAL_FAILURE", "docCount": 1 }
]
The termsAggregationProperty must use dot-notation: cinctx_kgProcessingStates/*/status returned an empty bucket list.
Buckets are approximate. In the tested environment, the Status buckets over the 1,255 documents with a BuyerOrg were Executed: 881 and Internal: 119, while the exact totalCount for Status = 'Executed' was 1083. Confirm any number you report with a totalCount query.
SUM and AVG: compute client-side
# Python sketch: post() sends a JSON body to POST /api/query and returns the parsed response
body = {"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/BuyerOrg = 'Evergreen Retail Cooperative'",
"limit": 20}
total = n = 0
while True:
page = post("/api/query", body)
for doc in page["documents"]:
total += doc["cin_ingestProperties"].get("PageCount", 0)
n += 1
if not page["documents"] or "nextSearchAfter" not in page: # stop on an empty page or a missing cursor
break
body["searchAfter"] = page["nextSearchAfter"]
# Live result: 51 documents in 3 pages, SUM(PageCount) = 264, AVG = 5.18
Convert values first when they are stored as strings. For example, cinctx_contextProperties/textMetadataGeneration/contract:value holds strings such as "5500" or "39415.41", or "" when no value was found.
Example Gallery: Ingest Properties
Every example below was run against a live Cloud Content Repository environment. Each shows the POST /api/query request body, a trimmed excerpt of the real response, and what it demonstrates. Counts are from that environment and will differ in yours; "trackTotalCountUpTo": -1 makes totalCount exact.
Equality
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/BuyerOrg = 'Evergreen Retail Cooperative'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 51,
"count": 2,
"documents": [
{"cin_ingestProperties": {"BuyerOrg": "Evergreen Retail Cooperative"}, ... },
{"cin_ingestProperties": {"BuyerOrg": "Evergreen Retail Cooperative"}, ... }
]
}
Exact match on a keyword value. Matching is case-sensitive.
AND across two properties
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Executed' AND cin_ingestProperties/DocType = 'SOW'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 201,
"count": 2,
"documents": [
{"cin_ingestProperties": {"Status": "Executed", "DocType": "SOW"}, ... },
{"cin_ingestProperties": {"Status": "Executed", "DocType": "SOW"}, ... }
]
}
Both conditions must hold.
IN: any of several values
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Jurisdiction IN ('Ohio', 'Texas')",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 150,
"count": 2,
"documents": [
{"cin_ingestProperties": {"Jurisdiction": "Ohio"}, ... },
{"cin_ingestProperties": {"Jurisdiction": "Texas"}, ... }
]
}
Matches documents whose value equals any listed value.
NOT IN, with IS NOT NULL
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Jurisdiction NOT IN ('Ohio', 'Texas') AND cin_ingestProperties/Jurisdiction IS NOT NULL",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1105,
"count": 2,
"documents": [
{"cin_ingestProperties": {"Jurisdiction": "Virginia"}, ... },
{"cin_ingestProperties": {"Jurisdiction": "Florida"}, ... }
]
}
NOT IN on its own also matches documents that do not have the property; adding IS NOT NULL keeps only documents that have it.
Not equal (<>)
{
"query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg' AND cin_ingestProperties/BuyerOrg <> 'Evergreen Retail Cooperative'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1204,
"count": 2,
"documents": [
{"cin_ingestProperties": {"BuyerOrg": "BluePeak Insurance Group"}, ... },
{"cin_ingestProperties": {"BuyerOrg": "Northfield Health Plan, Inc."}, ... }
]
}
Restricted to documents that have a BuyerOrg by testing cin_ingestPropertyNames.
LIKE: contains
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Topics LIKE '%Billing%'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 57,
"count": 2,
"documents": [
{"cin_ingestProperties": {"Topics": "SOW;Milestones;Acceptance;Billing;Change Control"}, ... },
{"cin_ingestProperties": {"Topics": "SOW;Milestones;Acceptance;Billing;Change Control"}, ... }
]
}
%x% works on keyword fields. Here Topics is a single ;-separated string, so LIKE finds a topic inside it.
LIKE with _ (one character)
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/BuyerOrg LIKE 'Evergreen Retail Cooperativ_'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 51,
"count": 2,
"documents": [
{"cin_ingestProperties": {"BuyerOrg": "Evergreen Retail Cooperative"}, ... },
{"cin_ingestProperties": {"BuyerOrg": "Evergreen Retail Cooperative"}, ... }
]
}
_ matches exactly one character. A pattern ending in a single % ('Evergreen%') fails with 400 on keyword fields; see LIKE rules.
NOT LIKE
{
"query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg' AND cin_ingestProperties/BuyerOrg NOT LIKE '%Evergreen%'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1184,
"count": 2,
"documents": [
{"cin_ingestProperties": {"BuyerOrg": "BluePeak Insurance Group"}, ... },
{"cin_ingestProperties": {"BuyerOrg": "Northfield Health Plan, Inc."}, ... }
]
}
Excludes values containing the text.
Numeric comparison
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/PageCount > 5",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 477,
"count": 2,
"documents": [
{"cin_ingestProperties": {"PageCount": 12}, ... },
{"cin_ingestProperties": {"PageCount": 6}, ... }
]
}
PageCount is stored as a number, so numeric comparison applies.
BETWEEN (inclusive)
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/PageCount BETWEEN 2 AND 4",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 614,
"count": 2,
"documents": [
{"cin_ingestProperties": {"PageCount": 3}, ... },
{"cin_ingestProperties": {"PageCount": 2}, ... }
]
}
Both bounds are included.
Date range on a string date
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/EffectiveDate >= '2024-01-01' AND cin_ingestProperties/EffectiveDate < '2025-01-01'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 590,
"count": 2,
"documents": [
{"cin_ingestProperties": {"EffectiveDate": "2024-08-04"}, ... },
{"cin_ingestProperties": {"EffectiveDate": "2024-12-08"}, ... }
]
}
EffectiveDate is stored as YYYY-MM-DD; compare with values in the same format.
Parentheses, OR, and NOT
{
"query": "SELECT * FROM SysContent WHERE (cin_ingestProperties/Status = 'Executed' OR cin_ingestProperties/Status = 'Internal') AND NOT cin_ingestProperties/DocType = 'SOW'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1054,
"count": 2,
"documents": [
{"cin_ingestProperties": {"Status": "Executed", "DocType": "Contract"}, ... },
{"cin_ingestProperties": {"Status": "Executed", "DocType": "SLA"}, ... }
]
}
OR inside parentheses, combined with a negated condition.
Empty-string value
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/AmendmentId = ''",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1079,
"count": 2,
"documents": [
{"cin_ingestProperties": {"AmendmentId": ""}, ... },
{"cin_ingestProperties": {"AmendmentId": ""}, ... }
]
}
Ingestion stored missing values as "", which = '' matches (IS NULL would not).
Documents that have a property (cin_ingestPropertyNames)
{
"query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1255,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
cin_ingestPropertyNames lists the ingest property names set on a document; = matches if the list contains the name.
Documents that have any of several properties
{
"query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames IN ('BuyerOrg', 'Meta')",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1257,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
IN on the names list.
Flag stored as a string
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/cinIsTld = 'True'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 418,
"count": 2,
"documents": [
{"cin_ingestProperties": {"cinIsTld": "True"}, ... },
{"cin_ingestProperties": {"cinIsTld": "True"}, ... }
]
}
cinIsTld holds the string "True"; = TRUE (boolean) matches 0 documents.
Relative dates with NOW()
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/integrationDate >= NOW('-P365D')",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 3502,
"count": 2,
"documents": [
{"cin_ingestProperties": {"integrationDate": "2026-05-19T01:28:50.100Z"}, ... },
{"cin_ingestProperties": {"integrationDate": "2026-05-19T01:16:25.247Z"}, ... }
]
}
NOW('-P365D') is the current time minus 365 days.
ORDER BY a nested property
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Executed' ORDER BY cin_ingestProperties/EffectiveDate DESC",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1083,
"count": 2,
"documents": [
{"cin_ingestProperties": {"EffectiveDate": "2027-08-15"}, ... },
{"cin_ingestProperties": {"EffectiveDate": "2027-08-15"}, ... }
]
}
Newest effective date first.
Example Gallery: Context Properties
These examples use the textMetadataGeneration enrichment, the most common one in the tested environment. See Querying Context Engine Fields for its structure.
Equality on an enrichment value
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:type = 'Amendment'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 3233,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:type": "Amendment"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:type": "Amendment"}}, ... }
]
}
Keys containing : are used as-is in the path syntax.
IN on an enrichment value
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:commercialCategory IN ('Renewal', 'Amendment')",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 6832,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:commercialCategory": "Amendment"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:commercialCategory": "Renewal"}}, ... }
]
}
Either category.
Array membership
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:partyNames = 'HYLAND SOFTWARE, INC.'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 593,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:partyNames": ["HYLAND SOFTWARE, INC.", "..."]}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:partyNames": ["HYLAND SOFTWARE, INC.", "..."]}}, ... }
]
}
contract:partyNames is an array; = matches if any element equals the value (other party names are omitted from the excerpt).
LIKE on an array
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:servicesInScope LIKE '%Training%'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1879,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:servicesInScope": ["Support services", "Training services"]}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:servicesInScope": ["Professional Services", "Implementation Services", "Managed Services", "Training", "Technical Support", "Consulting Services"]}}, ... }
]
}
Matches if any element contains the text. contract:servicesInScope is a string on some documents and an array on others; both are matched.
Date range on an enrichment value
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:effectiveDate BETWEEN '2024-01-01' AND '2024-12-31'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1059,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:effectiveDate": "2024-04-08"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:effectiveDate": "2024-02-24"}}, ... }
]
}
Dates generated by enrichment are strings in whatever format the enrichment produced, for example 2025-03-01, 03/01/2025 or January 30, 2023. String range comparisons such as >= are only reliable for YYYY-MM-DD values.
Excluding empty values in a range
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:expirationDate <> '' AND cinctx_contextProperties/textMetadataGeneration/contract:expirationDate < '2025-01-01'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 5812,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:expirationDate": "2024-01-03"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:expirationDate": "2020-08-14"}}, ... }
]
}
Values that were not found are stored as "", so exclude them explicitly before a < comparison.
Non-empty value
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:value <> '' AND cinctx_contextProperties/textMetadataGeneration/contract:value IS NOT NULL",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 10589,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:value": "5500"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:value": "39415.41"}}, ... }
]
}
contract:value is a string such as "5500", or "" when not found.
Enrichment present + enrichment value
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextPropertyNames = 'textMetadataGeneration' AND cinctx_contextProperties/textMetadataGeneration/contract:engagementModel = 'SaaS Subscription'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 4790,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:engagementModel": "SaaS Subscription"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:engagementModel": "SaaS Subscription"}}, ... }
]
}
cinctx_contextPropertyNames lists which enrichments ran.
Documents without any enrichment
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextPropertyNames IS NULL",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 36781,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
IS NULL matches documents where the field does not exist. Embedding records (title cohere.embed-multilingual-v3) are among the results; add a type or property filter to exclude them.
ORDER BY an enrichment value
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:type = 'Amendment' ORDER BY cinctx_contextProperties/textMetadataGeneration/contract:effectiveDate DESC",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 3233,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:effectiveDate": "2024-07-02"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:effectiveDate": "2024-06-30"}}, ... }
]
}
Sorted on contract:effectiveDate, newest first.
Several enrichment conditions with NOT
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:type = 'Amendment' AND cinctx_contextProperties/textMetadataGeneration/contract:partyNames = 'HYLAND SOFTWARE, INC.' AND NOT cinctx_contextProperties/textMetadataGeneration/contract:engagementModel = 'SaaS Subscription'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 41,
"count": 2,
"documents": [
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:type": "Amendment"}}, ... },
{"cinctx_contextProperties": {"textMetadataGeneration": {"contract:type": "Amendment"}}, ... }
]
}
Amendments with Hyland as a party that are not SaaS subscriptions.
Example Gallery: KG Processing States
Documents with any KG processing state
{
"query": "SELECT * FROM SysContent WHERE cinctx_kgProcessingStates/*/ontologyId IS NOT NULL",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1155,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
Path syntax.
Same query with path syntax
{
"query": "SELECT * FROM SysContent WHERE cinctx_kgProcessingStates/ontologyId IS NOT NULL",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1155,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
Returns the same documents as the path syntax.
One ontology, any graph database
{
"query": "SELECT * FROM SysContent WHERE cinctx_kgProcessingStates/*/ontologyId LIKE '%31944a57-cb89-406c-9f7d-96631d305f8f'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 588,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
ontologyId is <graphDBId>_<ontologyId>, so a leading % matches the ontology in every graph database.
Processing synced
{
"query": "SELECT * FROM SysContent WHERE cinctx_kgProcessingStates/*/status = 'SYNCED'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1153,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
Status values are written by the processing service; SYNCED is the common one.
Processing failed
{
"query": "SELECT * FROM SysContent WHERE cinctx_kgProcessingStates/*/status = 'SYNC_FATAL_FAILURE'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 1,
"count": 1,
"documents": [
{ ... }
]
}
SYNC_FATAL_FAILURE is one of several failure statuses. Values seen in the tested environment include ARCHITECT_FAILURE, MINER_FATAL_FAILURE and SYNC_FATAL_FAILURE for failures, ARCHITECTED and MINED while processing is in progress, and SYNCED when it is complete.
Enriched but not yet KG-processed
{
"query": "SELECT * FROM SysContent WHERE cinctx_contextPropertyNames IS NOT NULL AND cinctx_kgProcessingStates/*/ontologyId IS NULL",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 17865,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
Has context properties, but no KG processing state.
Ingest property + KG state
{
"query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg' AND cinctx_kgProcessingStates/*/status = 'SYNCED'",
"limit": 2,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
{
"totalCount": 561,
"count": 2,
"documents": [
{ ... },
{ ... }
]
}
Combines an ingest-property check with a KG status.
Common Mistakes
| Mistake | What Happens | Fix |
|---|---|---|
| Naming fields in SELECT | Returns 0 documents, no error | Use SELECT * instead |
Unknown type name (e.g., FROM documents) | The query fails with 400 Unknown type: documents | Use FROM SysContent or a real document type such as SysFile |
Using field:desc in the body sort | Treated as a field literally named field:desc, not as a descending sort | Use "field DESC" (with a space) |
Escaping quotes SQL-style ('O''Brien') | Parse error (400) | Use a backslash ('O\'Brien') or double quotes |
Paging past 10,000 with offset | 400: limit + offset should be <= 10000 | Use searchAfter with nextSearchAfter |
| Expecting immediate results after PATCH | Query returns 0 (indexing lag) | Call GET /api/query/waitForFullTextSearchIndexing first (its timeout is in seconds, default 5) |
Querying cinctx_ fields on docs without CinContext mixin | Fields don't exist on the document | Ensure document has the CinContext mixin type |
| Comparing a date in a different format than it is stored | Wrong or empty results | Compare in the stored format: system dates such as cin_dateCreated with ISO 8601 ('2025-01-01T00:00:00Z'), and string dates such as cin_ingestProperties/EffectiveDate with the same 'YYYY-MM-DD' form |
LIKE 'Prefix%' on an ingest property or other keyword field | 400 Error performing OpenSearch query: failed to create query: Can only use phrase prefix queries on text fields - not on [...] which is of type [keyword] | Use LIKE '%Prefix%', an _ wildcard, or =. See LIKE rules |
Expecting LIKE or ILIKE to ignore case | LIKE is case-sensitive; ILIKE returned 0 results on the tested fields | Match the stored case, or use terms aggregation with searchTerm (case-insensitive) to find the exact values first |
Using STARTSWITH as a string prefix | 0 results on non-path fields | Use STARTSWITH only on sys_path; use LIKE for strings |
= TRUE on a flag stored as a string | 0 results | Compare with the stored string, e.g. cin_ingestProperties/cinIsTld = 'True' |
COUNT(*), SUM(), GROUP BY, DISTINCT, LIMIT | 400 parse error, or (for SUM(x) / COUNT(x)) 200 with no documents | See SUM, COUNT, GROUP BY — What to Use Instead |
NOT IN without IS NOT NULL | Also matches documents that don't have the property (54,497 vs. 1,105 in the tested environment) | Add AND field IS NOT NULL |
Path syntax (/*/) in termsAggregationProperty | Empty bucket list | Use dot-notation, e.g. cinctx_kgProcessingStates.status |
Query Recipes
Copy-paste these patterns and adjust the values for your use case. Each was run against the tested environment.
Find all documents of a type
SELECT * FROM SysContent WHERE cin_type = 'CLI Ingestion Type'
Find documents created in a date range
SELECT * FROM SysContent
WHERE cin_dateCreated >= '2026-01-01T00:00:00Z'
AND cin_dateCreated <= '2026-12-31T23:59:59Z'
ORDER BY cin_dateCreated DESC
-- equivalent
SELECT * FROM SysContent
WHERE cin_dateCreated BETWEEN '2026-01-01T00:00:00Z' AND '2026-12-31T23:59:59Z'
Find a specific document by ID
SELECT * FROM SysContent WHERE sys_id = '966e293c-08e0-4ada-b28e-da4d04ac07fe__006fbbf7-0b8c-4a8c-9e88-92e525674c83'
Ingested documents in the tested environment usually have IDs of the form <sourceId>__<id>, where the part after __ is often but not always a UUID (for example 0198bb68-...__agents-001). Documents created through the Document API have a plain UUID, and so do embedding records, which can also appear in results such as IS NULL queries.
Find enriched documents with a specific classification
SELECT * FROM SysContent
WHERE cinctx_contextProperties/textClassification/result = 'Test Document'
Find documents mentioning a company
SELECT * FROM SysContent
WHERE cinctx_contextProperties/namedEntityText/result/organizations IN ('Hyland Software')
-- or, with metadata generation
SELECT * FROM SysContent
WHERE cinctx_contextProperties/textMetadataGeneration/contract:partyNames = 'HYLAND SOFTWARE, INC.'
Find documents with failed KG processing
SELECT * FROM SysContent
WHERE cinctx_kgProcessingStates/*/status IN ('ARCHITECT_FAILURE', 'MINER_FATAL_FAILURE', 'SYNC_FATAL_FAILURE')
Find documents that have not been enriched yet
SELECT * FROM SysContent
WHERE cinctx_contextPropertyNames IS NULL
Combined filter: type + date + creator
SELECT * FROM SysContent
WHERE cin_type = 'CLI Ingestion Type'
AND cin_createdBy = 'CLI Ingestion'
AND cin_dateCreated >= '2025-01-01T00:00:00Z'
ORDER BY cin_dateCreated DESC
Find documents by source system
SELECT * FROM SysContent
WHERE cin_sourceId = 'f2fc56d6-a421-4376-a715-a08446f2a319'
cin_sourceId is the ID of the source / system integration (a UUID in the tested environment). Run a terms aggregation on cin_sourceId to list the sources in yours.
Debug: inspect a single document's full state
-- See everything: mixins, ingest props, context enrichments, KG states
SELECT * FROM SysContent
WHERE sys_id = 'b93e73c1-79c4-4ab6-a7de-8d208dc7609c__00a4a00d-4d24-47f1-9c26-75b8d58e5530'
Parameterised Queries with POST /api/query/advanced
Execute a query built from structured predicates and named parameters. Accepts every field of POST /api/query (repositoryId, query, syntax, limit, offset, searchAfter, sort, trackTotalCount, trackTotalCountUpTo) plus the fields below. The predicates, the query's own WHERE clause, and the quick filter clauses are combined with AND. The response has the same shape as POST /api/query.
- HTTP
- curl
POST {{baseUrl}}/api/query/advanced
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
Content-Type: application/json
{
"repositoryId": "default",
"query": "SELECT * FROM SysFile",
"sort": ["sys_title"],
"parameters": {
"titleMatch": "My Other File%"
},
"whereClauseDefinition": {
"predicates": [
{
"searchParameter": "sys_title",
"operator": "LIKE",
"parameters": [ { "name": "titleMatch" } ]
}
]
},
"quickFilterClauses": ["sys_title != 'article'"]
}
curl -X POST "$BASE_URL/api/query/advanced" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.hxcs.v1+json" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"repositoryId": "default",
"query": "SELECT * FROM SysFile",
"sort": ["sys_title"],
"parameters": {
"titleMatch": "My Other File%"
},
"whereClauseDefinition": {
"predicates": [
{
"searchParameter": "sys_title",
"operator": "LIKE",
"parameters": [ { "name": "titleMatch" } ]
}
]
},
"quickFilterClauses": ["sys_title != 'article'"]
}
JSON
Advanced Query Fields
| Field | Type | Description |
|---|---|---|
parameters | object | Named values referenced by predicates, and by :name placeholders in the HXQL query |
whereClauseDefinition | object | Predicate definitions (below) |
whereClauseDefinition.query | string | Optional. An HXQL statement here replaces the request's query as the statement to select from. With syntax: "expr" it is instead a server-side boolean expression over the server's global properties, not over document fields: true adds no filter, false makes the query return nothing, and a document field such as cin_sourceId returns 400 No global property: cin_sourceId. |
whereClauseDefinition.syntax | string | "hxql" (default) or "expr" |
whereClauseDefinition.predicates[].searchParameter | string | Field to filter on |
whereClauseDefinition.predicates[].operator | string | Operator (see below), or ":paramName" to take the operator from parameters |
whereClauseDefinition.predicates[].parameters[] | array | { "name": "...", "defaultValue": ... }. name refers to a key of parameters (a leading : is allowed); defaultValue is used when it is not supplied. Omit name to give a fixed value. BETWEEN takes two entries (min, max) |
quickFilterClauses | string[] | Extra HXQL predicates, e.g. "sys_title != 'article'" |
Operators: =, <>, !=, <, >, <=, >=, LIKE, ILIKE, NOT LIKE, NOT ILIKE, BETWEEN, IN, NOT IN, STARTSWITH, EMPTY / ISEMPTY (field is the empty string), FULLTEXT / FULLTEXT ALL (full-text match), and IS NULL (value true means IS NULL, false means IS NOT NULL). Operator names are case-insensitive; any other value returns 400 Unsupported predicate operator.
- A predicate whose value is not supplied (missing,
null, or a blank string) is dropped rather than causing an error, so one definition can back a search form where only some fields are filled in IN/NOT INandSTARTSWITHaccept a list; a single value is treated as a one-element list. SeveralSTARTSWITHprefixes are combined withORBETWEENwith only one bound becomes>=(min only) or<=(max only)- Word-style operator names such as
eqorgtare not accepted:400Unsupported predicate operator: eq - The response's
queryfield shows the final HXQL statement the predicates were turned into, which is useful for debugging
Advanced Query Examples
Each example shows the request, and the query and totalCount from the live response.
Parameterised ingest filters: = and IN
{
"query": "SELECT * FROM SysContent",
"limit": 3,
"trackTotalCountUpTo": -1,
"parameters": {
"status": "Executed",
"buyers": ["Evergreen Retail Cooperative", "Rivergate Financial Group"]
},
"whereClauseDefinition": {
"predicates": [
{ "searchParameter": "cin_ingestProperties/Status", "operator": "=", "parameters": [ { "name": "status" } ] },
{ "searchParameter": "cin_ingestProperties/BuyerOrg", "operator": "IN", "parameters": [ { "name": "buyers" } ] }
]
}
}
-- Response (trimmed)
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Executed' AND cin_ingestProperties/BuyerOrg IN ('Evergreen Retail Cooperative', 'Rivergate Financial Group')",
"totalCount": 135
BETWEEN with two parameters
{
"query": "SELECT * FROM SysContent",
"limit": 3,
"trackTotalCountUpTo": -1,
"parameters": { "minPages": 2, "maxPages": 4 },
"whereClauseDefinition": {
"predicates": [
{ "searchParameter": "cin_ingestProperties/PageCount", "operator": "BETWEEN",
"parameters": [ { "name": "minPages" }, { "name": "maxPages" } ] }
]
}
}
-- Response (trimmed)
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/PageCount BETWEEN 2 AND 4",
"totalCount": 614
defaultValue, and a predicate that is dropped
{
"query": "SELECT * FROM SysContent",
"limit": 3,
"trackTotalCountUpTo": -1,
"parameters": {},
"whereClauseDefinition": {
"predicates": [
{ "searchParameter": "cin_ingestProperties/Status", "operator": "=",
"parameters": [ { "name": "status", "defaultValue": "Internal" } ] }
]
}
}
-- Response (trimmed): no "status" supplied, so the default is used
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Internal'",
"totalCount": 172
With a context-property search form, the same pattern works. Here three predicates are defined, but no value is given for status, so that predicate is dropped:
{
"query": "SELECT * FROM SysContent",
"limit": 3,
"trackTotalCountUpTo": -1,
"parameters": { "ctype": "Amendment", "party": "HYLAND SOFTWARE, INC." },
"whereClauseDefinition": {
"predicates": [
{ "searchParameter": "cinctx_contextProperties/textMetadataGeneration/contract:type", "operator": "=", "parameters": [ { "name": "ctype" } ] },
{ "searchParameter": "cinctx_contextProperties/textMetadataGeneration/contract:partyNames", "operator": "=", "parameters": [ { "name": "party" } ] },
{ "searchParameter": "cin_ingestProperties/Status", "operator": "=", "parameters": [ { "name": "status" } ] }
]
}
}
-- Response (trimmed)
"query": "SELECT * FROM SysContent WHERE cinctx_contextProperties/textMetadataGeneration/contract:type = 'Amendment' AND cinctx_contextProperties/textMetadataGeneration/contract:partyNames = 'HYLAND SOFTWARE, INC.'",
"totalCount": 253
LIKE parameter plus a quick filter
{
"query": "SELECT * FROM SysContent",
"limit": 3,
"trackTotalCountUpTo": -1,
"parameters": { "topic": "%Billing%" },
"whereClauseDefinition": {
"predicates": [
{ "searchParameter": "cin_ingestProperties/Topics", "operator": "LIKE", "parameters": [ { "name": "topic" } ] }
]
},
"quickFilterClauses": ["cin_ingestProperties/Status = 'Executed'"]
}
-- Response (trimmed)
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Topics LIKE '%Billing%' AND cin_ingestProperties/Status = 'Executed'",
"totalCount": 57
Operator taken from a parameter
{
"query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg'",
"limit": 3,
"trackTotalCountUpTo": -1,
"parameters": { "op": "<>", "bo": "Evergreen Retail Cooperative" },
"whereClauseDefinition": {
"predicates": [
{ "searchParameter": "cin_ingestProperties/BuyerOrg", "operator": ":op", "parameters": [ { "name": "bo" } ] }
]
}
}
-- Response (trimmed)
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/BuyerOrg <> 'Evergreen Retail Cooperative' AND cin_ingestPropertyNames = 'BuyerOrg'",
"totalCount": 1204
:name placeholders in the HXQL query
{
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = :st AND cin_ingestProperties/PageCount >= :minPages",
"parameters": { "st": "Executed", "minPages": 10 },
"limit": 1,
"trackTotalCountUpTo": -1
}
-- Response (trimmed)
"query": "SELECT * FROM SysContent WHERE cin_ingestProperties/Status = 'Executed' AND cin_ingestProperties/PageCount >= 10",
"totalCount": 364
The same placeholder sent to POST /api/query (which has no parameters) fails with 400 No value for query parameter :st.
Run Named Queries
Named queries are query definitions configured on the server. List them, inspect a definition, then run it with your parameter values.
List named queries
GET /api/query/named returns the names of the named queries configured on the server.
- HTTP
- curl
GET {{baseUrl}}/api/query/named
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
curl -X GET "$BASE_URL/api/query/named" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.hxcs.v1+json"
{
"namedQueries": ["advanced_document_content", "advanced_folder_listing", "advanced_search",
"default_document_suggestion", "document_content", "folder_listing",
"simple_search", "tree_children"]
}
The list above is what a live environment returned; the set is configured on the server.
Get a named query definition
Return a named query definition: name, aliases, whereClauseDefinition, sort, and quickFilters (each { name, clause, sort }). Fields the definition does not set are omitted. Returns 404 (404 NOT_FOUND "Named query not found.") if no such named query exists.
-- GET /api/query/named/advanced_search (live response)
{
"name": "advanced_search",
"whereClauseDefinition": {
"query": "SELECT * FROM SysContent WHERE sys_parentId IS NOT NULL AND SORTED_COLUMN IS NOT NULL AND sysver_isVersion = 0 AND sys_mixinTypes <> 'SysHiddenInNavigation'",
"predicates": [
{ "searchParameter": "sys_fulltext", "operator": "FULLTEXT", "parameters": [ { "name": "fulltext", "defaultValue": null } ] },
{ "searchParameter": "sys_created", "operator": "BETWEEN", "parameters": [ { "name": "created_min", "defaultValue": null }, { "name": "created_max", "defaultValue": null } ] },
{ "searchParameter": "sys_modified", "operator": "BETWEEN", "parameters": [ { "name": "modified_min", "defaultValue": null }, { "name": "modified_max", "defaultValue": null } ] },
{ "searchParameter": "sys_path", "operator": "STARTSWITH", "parameters": [ { "name": "path", "defaultValue": null } ] }
]
},
"quickFilters": [
{ "name": "noFolder", "clause": "sys_mixinTypes <> 'SysFolderish'" },
{ "name": "mostRecent", "sort": ["sys_modified DESC"] }
]
}
Run a named query
Execute a named query. The response has the same shape as POST /api/query. Returns 404 (No named query with name ...) if the named query does not exist.
- HTTP
- curl
POST {{baseUrl}}/api/query/named
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
Content-Type: application/json
{ "queryName": "advanced_search", "parameters": { "path": "/my-folder" }, "selectedQuickFilters": ["noFolder"], "limit": 5 }
curl -X POST "$BASE_URL/api/query/named" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.hxcs.v1+json" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{ "queryName": "advanced_search", "parameters": { "path": "/my-folder" }, "selectedQuickFilters": ["noFolder"], "limit": 5 }
JSON
{
"queryName": "advanced_search",
"parameters": { "path": "/my-folder" },
"selectedQuickFilters": ["noFolder"],
"limit": 5
}
-- Response (trimmed): the executed query
"query": "SELECT * FROM SysContent WHERE sys_path STARTSWITH '/my-folder' AND sys_parentId IS NOT NULL AND sys_id IS NOT NULL AND sysver_isVersion = 0 AND sys_mixinTypes <> 'SysHiddenInNavigation' AND sys_mixinTypes <> 'SysFolderish'"
A full-text search through simple_search:
{ "queryName": "simple_search", "parameters": { "fulltext": "renewal" }, "limit": 1, "trackTotalCountUpTo": -1 }
-- Response (trimmed)
"query": "SELECT * FROM SysContent WHERE sys_fulltext = 'renewal' AND sys_parentId IS NOT NULL AND sys_id IS NOT NULL AND sysver_isVersion = 0 AND sys_mixinTypes <> 'SysHiddenInNavigation'",
"totalCount": 146
| Field | Type | Description |
|---|---|---|
queryName | string | Name (or alias) of the named query |
parameters | object | Values for the definition's predicate parameters |
selectedQuickFilters | string[] | Names of the definition's quick filters to apply |
sort | string[] | Overrides the sort; otherwise the selected quick filters' sort, then the definition's sort, is used |
repositoryId, limit, offset, searchAfter, trackTotalCount, trackTotalCountUpTo | As for POST /api/query |
Get Value Distributions with POST /api/query/termsAggregation
Aggregate documents by a specific field's terms. Use this to discover the distribution of values for a given property across your document set.
- HTTP
- curl
POST {{baseUrl}}/api/query/termsAggregation
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
Content-Type: application/json
{
"query": { "query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg'" },
"termsAggregationProperty": "cin_ingestProperties.Status"
}
curl -X POST "$BASE_URL/api/query/termsAggregation" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.hxcs.v1+json" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"query": { "query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg'" },
"termsAggregationProperty": "cin_ingestProperties.Status"
}
JSON
Aggregation Request Body
{
"query": {
"query": "SELECT * FROM SysContent",
"syntax": "hxql"
},
"termsAggregationProperty": "cinctx_contextPropertyNames",
"limit": 10,
"searchTerm": "text"
}
Aggregation Fields
| Field | Type | Required | Description |
|---|---|---|---|
query | object | Yes | Query selecting the documents to aggregate over (same structure as the POST /api/query body). An empty object {} selects all documents |
termsAggregationProperty | string | Yes | Property to aggregate on |
limit | integer | No | Max number of buckets (default 10) |
searchTerm | string | No | Only aggregate documents whose property value contains this text, and only return buckets whose key contains it (case-insensitive). On multi-value properties this also drops the document's other values from the buckets. |
A missing query or termsAggregationProperty returns 400 Invalid input.
Aggregation Response
-- Request
{
"query": { "query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg'" },
"termsAggregationProperty": "cin_ingestProperties.Status"
}
-- Response (live)
{
"repositoryId": null,
"query": "SELECT * FROM SysContent WHERE cin_ingestPropertyNames = 'BuyerOrg'",
"limit": 10,
"offset": 0,
"documents": [],
"totalCount": 0,
"aggregationsBuckets": [
{ "key": "Executed", "docCount": 881 },
{ "key": "Internal", "docCount": 119 }
],
"count": 0
}
documents is always empty and totalCount is 0; only the buckets are computed. Counts are computed over a sample of up to 1,000 matching documents per index shard, so they are approximate on large document sets. In the example, 1,255 documents match the query, so the buckets do not cover all of them.
- Use dot-notation for nested and list sub-fields:
cinctx_kgProcessingStates.statusreturned buckets (SYNCED,SYNC_FATAL_FAILURE); the path formcinctx_kgProcessingStates/*/statusreturned an emptyaggregationsBucketslist - Numeric properties cannot be aggregated:
cin_ingestProperties.PageCountreturned400Error performing OpenSearch query: Can only use wildcard queries on keyword and text fields - not on [cin_ingestProperties.PageCount] which is of type [long] - Keys containing
:work, e.g.cinctx_contextProperties/textMetadataGeneration/contract:engagementModel. Empty-string values appear as a bucket with"key": ""
More count-per-value examples are in Counting and aggregating.
Wait for Indexing After Ingestion
Wait for full-text search indexing to complete. This is useful after ingesting documents to ensure they are searchable before running queries.
- HTTP
- curl
GET {{baseUrl}}/api/query/waitForFullTextSearchIndexing?timeout=10
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json
curl -X GET "$BASE_URL/api/query/waitForFullTextSearchIndexing?timeout=10" \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/vnd.hxcs.v1+json"
Wait Parameters
| Parameter | In | Type | Description |
|---|---|---|---|
refresh | query | boolean | Refresh the index (default true) |
timeout | query | integer | Timeout in seconds (default 5) |
waitForAudit | query | boolean | Deprecated and unused |
HXCS-REPOSITORY | header | string | Repository ID (default "default") |
Wait Response
{ "indexing": "done" }
Returns "not done" if indexing did not complete within the timeout.
Next Steps
- Semantic and Hybrid Search: find chunks by vector similarity
- Downloading Content: fetch the source files of your query results
- API Reference: every Query API endpoint and schema
- Error Reference: error formats and status codes