Skip to main content

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:

  1. Run a query with POST /api/query and page through the results
  2. Write HXQL: SELECT, WHERE, operators, literals, sorting and pagination
  3. Filter on ingest properties, context properties, and KG processing states
  4. Count and aggregate documents
  5. Build parameterised queries with POST /api/query/advanced
  6. Run server-defined named queries
  7. Get value distributions with POST /api/query/termsAggregation
  8. Wait for indexing to finish after ingestion

Run a Query​

Send an HXQL statement in the query field of POST /api/query.

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
}

Parameters​

FieldTypeRequiredDescription
repositoryIdstringNoRepository to search (default "default")
querystringNoQuery in the syntax given by syntax. If blank, defaults to SELECT * FROM SysContent
syntaxstringNo"hxql" (default): a full HXQL statement. "expr": an expression used as a filter, e.g. content.sys_title == "foo"
limitintegerNoMax documents to return (default 10). Values above 10,000 are capped at 10,000
offsetintegerNoNumber of documents to skip (default 0). limit + offset must be ≤ 10,000, otherwise 400
searchAfterstringNoOpaque 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
sortstring[]NoSort fields, each optionally followed by " ASC" or " DESC" (e.g. "sys_title DESC"). When present, replaces any ORDER BY in the query
trackTotalCountbooleanNoWhether to compute totalCount (default true)
trackTotalCountUpTointegerNoMax 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.

ParameterInTypeDescription
syncquerybooleanDefault 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..."
}
FieldDescription
queryThe query as executed (including any sort and bound parameters)
syntaxOnly present when it is not hxql
documentsThe matching documents
countNumber of documents in this page
totalCountTotal matching documents. Omitted when trackTotalCount is false (the response then contains "trackTotalCount": false)
totalCountIsTruncatedOnly present (as true) when the count stopped at trackTotalCountUpTo
nextSearchAfterCursor 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.
Common behavior
  • 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 returns 400.
  • If the search index is unreachable, the search endpoints return 503 with a Retry-After header (seconds). The request can be retried.
HXQL Notes
  • Use SELECT *. On the default (OpenSearch) path, naming fields in SELECT (e.g. SELECT sys_title FROM SysContent) returns an empty documents list and "totalCount": 0, with no error. This applies to every field, not only type any fields
  • Type any fields (cin_ingestProperties, cinctx_contextProperties) CAN be used in WHERE clauses with the slash path syntax: WHERE cin_ingestProperties/Status = 'Executed', WHERE cinctx_contextProperties/textMetadataGeneration/contract:type = 'Amendment'
  • On list fields, both = and IN() match when any element matches: WHERE cin_ingestPropertyNames IN ('BuyerOrg', 'Meta')
  • HXQL has no SUM, COUNT, GROUP BY, DISTINCT, or LIMIT. 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
}
note

"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
caution

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:

FieldMixin TypeWhat It Holds
cin_ingestPropertiesCinRemoteMetadata written during ingestion (DocType, BuyerOrg, etc.)
cinctx_contextPropertiesCinContextContext 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.

warning

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

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')
note

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​

OperatorDescriptionExample
=Equalscin_type = 'Invoice'
<>, !=Not equalscin_type <> 'Draft'
>, >=, <, <=Comparisoncin_dateCreated >= '2025-01-01T00:00:00Z'
[NOT] BETWEEN x AND yInclusive rangecin_dateCreated BETWEEN '2025-01-01T00:00:00Z' AND '2025-12-31T23:59:59Z'
[NOT] LIKECase-sensitive pattern match (see LIKE rules below)cin_ingestProperties/BuyerOrg LIKE '%Evergreen%'
[NOT] ILIKECase-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 errorsys_title ILIKE 'cli%' → 0 results
STARTSWITHPath-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 valuescinctx_contextPropertyNames IN ('textSummary', 'piiRecognition')
IS NULLField does not existcinctx_contextPropertyNames IS NULL
IS NOT NULLField existscinctx_contextPropertyNames IS NOT NULL
sys_fulltext = '...'Full-text search across the document. Terms are combined with AND; use OR between terms for eithersys_fulltext = 'contract renewal'
ANDLogical ANDcin_type = 'SLA' AND cin_createdBy = 'ADMIN'
ORLogical ORcin_type = 'Invoice' OR cin_type = 'Contract'
NOTLogical negationNOT (cin_type = 'Invoice' OR cin_type = 'Contract')
( )Groupingcin_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 text 100%
  • LIKE is case-sensitive: cin_ingestProperties/BuyerOrg LIKE '%Evergreen%' matched 71 documents, LIKE '%evergreen%' matched 0
caution

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).

warning

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​

LiteralExample
String (single or double quotes)'Invoice', "Invoice"
Integer / decimal42, -3.5, 1E3
Boolean (case-insensitive)true, FALSE
Date / timestampDATE '2025-01-01', TIMESTAMP '2025-01-01T00:00:00Z'
warning

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, ... }
}
  • ontologyId is a composite of <graphDBId>_<ontologyId>: the graph database ID and the ontology ID joined by an underscore. To match one ontology across graph databases, use LIKE '%<ontologyId>'
  • status is a free-form string written by the processing service. Values seen in the tested environments include SYNCED, the in-progress ARCHITECTED and MINED, and the failures ARCHITECT_FAILURE, MINER_FATAL_FAILURE and SYNC_FATAL_FAILURE; one environment also had completed
  • state is 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

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.

warning

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.status works, while cinctx_kgProcessingStates/*/status returns no buckets
  • Numeric properties (such as cin_ingestProperties.PageCount) return 400 ... 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​

caution

HXQL has no aggregate functions or grouping. Queries that try them behave as follows:

  • SELECT COUNT(*) FROM SysContent → 400 At position 1:13 extraneous input '*' expecting {...}
  • SELECT SUM(cin_ingestProperties/PageCount) FROM SysContent and SELECT COUNT(sys_id) FROM SysContent parse, but are treated as named-field SELECTs: 200, no documents, "totalCount": 0. Nothing is summed or counted
  • ... GROUP BY cin_ingestProperties.Status → 400 mismatched input 'GROUP' expecting {<EOF>, 'WHERE', 'ORDER BY', ','}
  • SELECT DISTINCT cin_ingestProperties.Status FROM SysContent → 400 extraneous input 'cin_ingestProperties.Status' expecting 'FROM'
SQL you might writeSupported alternativeExact?
SELECT COUNT(*) ... WHERE ...POST /api/query with the WHERE clause, "limit": 1, "trackTotalCountUpTo": -1; read totalCountYes
SELECT x, COUNT(*) ... GROUP BY xPOST /api/query/termsAggregation with termsAggregationProperty: xNo — sample-based
SELECT DISTINCT xThe bucket keys of a terms aggregation on xRare 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 documentYes

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.

warning

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.


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.


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.


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​

MistakeWhat HappensFix
Naming fields in SELECTReturns 0 documents, no errorUse SELECT * instead
Unknown type name (e.g., FROM documents)The query fails with 400 Unknown type: documentsUse FROM SysContent or a real document type such as SysFile
Using field:desc in the body sortTreated as a field literally named field:desc, not as a descending sortUse "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 offset400: limit + offset should be <= 10000Use searchAfter with nextSearchAfter
Expecting immediate results after PATCHQuery returns 0 (indexing lag)Call GET /api/query/waitForFullTextSearchIndexing first (its timeout is in seconds, default 5)
Querying cinctx_ fields on docs without CinContext mixinFields don't exist on the documentEnsure document has the CinContext mixin type
Comparing a date in a different format than it is storedWrong or empty resultsCompare 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 field400 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 caseLIKE is case-sensitive; ILIKE returned 0 results on the tested fieldsMatch the stored case, or use terms aggregation with searchTerm (case-insensitive) to find the exact values first
Using STARTSWITH as a string prefix0 results on non-path fieldsUse STARTSWITH only on sys_path; use LIKE for strings
= TRUE on a flag stored as a string0 resultsCompare with the stored string, e.g. cin_ingestProperties/cinIsTld = 'True'
COUNT(*), SUM(), GROUP BY, DISTINCT, LIMIT400 parse error, or (for SUM(x) / COUNT(x)) 200 with no documentsSee SUM, COUNT, GROUP BY — What to Use Instead
NOT IN without IS NOT NULLAlso 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 termsAggregationPropertyEmpty bucket listUse 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.

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'"]
}

Advanced Query Fields​

FieldTypeDescription
parametersobjectNamed values referenced by predicates, and by :name placeholders in the HXQL query
whereClauseDefinitionobjectPredicate definitions (below)
whereClauseDefinition.querystringOptional. 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.syntaxstring"hxql" (default) or "expr"
whereClauseDefinition.predicates[].searchParameterstringField to filter on
whereClauseDefinition.predicates[].operatorstringOperator (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)
quickFilterClausesstring[]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.

Predicate behavior
  • 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 IN and STARTSWITH accept a list; a single value is treated as a one-element list. Several STARTSWITH prefixes are combined with OR
  • BETWEEN with only one bound becomes >= (min only) or <= (max only)
  • Word-style operator names such as eq or gt are not accepted: 400 Unsupported predicate operator: eq
  • The response's query field 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.

GET {{baseUrl}}/api/query/named
Authorization: Bearer {{accessToken}}
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.

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 }
{
"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
FieldTypeDescription
queryNamestringName (or alias) of the named query
parametersobjectValues for the definition's predicate parameters
selectedQuickFiltersstring[]Names of the definition's quick filters to apply
sortstring[]Overrides the sort; otherwise the selected quick filters' sort, then the definition's sort, is used
repositoryId, limit, offset, searchAfter, trackTotalCount, trackTotalCountUpToAs 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.

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"
}

Aggregation Request Body​

{
"query": {
"query": "SELECT * FROM SysContent",
"syntax": "hxql"
},
"termsAggregationProperty": "cinctx_contextPropertyNames",
"limit": 10,
"searchTerm": "text"
}

Aggregation Fields​

FieldTypeRequiredDescription
queryobjectYesQuery selecting the documents to aggregate over (same structure as the POST /api/query body). An empty object {} selects all documents
termsAggregationPropertystringYesProperty to aggregate on
limitintegerNoMax number of buckets (default 10)
searchTermstringNoOnly 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.

Property rules
  • Use dot-notation for nested and list sub-fields: cinctx_kgProcessingStates.status returned buckets (SYNCED, SYNC_FATAL_FAILURE); the path form cinctx_kgProcessingStates/*/status returned an empty aggregationsBuckets list
  • Numeric properties cannot be aggregated: cin_ingestProperties.PageCount returned 400 Error 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.

GET {{baseUrl}}/api/query/waitForFullTextSearchIndexing?timeout=10
Authorization: Bearer {{accessToken}}
Accept: application/vnd.hxcs.v1+json

Wait Parameters​

ParameterInTypeDescription
refreshquerybooleanRefresh the index (default true)
timeoutqueryintegerTimeout in seconds (default 5)
waitForAuditquerybooleanDeprecated and unused
HXCS-REPOSITORYheaderstringRepository ID (default "default")

Wait Response​

{ "indexing": "done" }

Returns "not done" if indexing did not complete within the timeout.


Next Steps​