Concepts
August 28, 2026 ยท View on GitHub
Arranger is a versatile, model-agnostic data discovery API for OpenSearch and Elasticsearch. It turns an index configuration into a working search API and MCP server for AI agent access. A React component library is available for building search UIs on top of it. This page defines the domain terms used throughout the Arranger documentation and codebase.
Catalogues and configuration
A catalogue is one searchable dataset in Arranger. It maps to a single Elasticsearch index and carries its own set of JSON configuration files:
base.json: the Elasticsearch index name and basic settingsextended.json: display names, types, and visibility for each fieldfacets.json: which fields to expose as filterable facet panelstable.json: which fields to show as columns in the data table
A running Arranger server can host one or more catalogues, each in its own subdirectory under the configuration root directory.
A catalogue is identified two different ways, and they are not interchangeable:
catalogueId: the actual unique identifier. Defaults to the config subdirectory name, overridable viacatalogIdinbase.json. This is what routes requests (/{catalogueId}/graphqlin multicatalogue mode) and what catalogues are keyed by internally; a server enforces this uniqueness structurally, by using it as a lookup key.documentType: the GraphQL root query field name for that catalogue's data. Since each catalogue gets its own independent GraphQL schema, nothing stops two different catalogues on the same server from configuring the samedocumentType. Do not treatdocumentTypeas a unique identifier, and do not build your owndocumentType-to-catalogueIdlookup that assumes a single match: check for more than one, and fail rather than silently picking one, since nothing server-side prevents the collision.
The server itself follows this same rule where it accepts either value in a path (/{catalogueId}/graphql, /introspection/:catalogueId): a real catalogueId always matches first, and a documentType is only accepted when it names exactly one catalogue. A documentType shared by several catalogues gets a 409 with every matching catalogueId, not a silent, arbitrary pick. See Introspection API for the exact response shape.
Facets, buckets, and aggregations
When users explore a dataset they navigate it through facets: panels in the search UI, one per filterable field, each showing the distinct values present in the current result set.
Each option within a facet is a bucket: a distinct value paired with its document count. For a "Disease type" facet, the buckets might look like:
| Value | Count |
|---|---|
| Leukemia | 42 |
| Lymphoma | 31 |
| Sarcoma | 18 |
Behind each facet is an Elasticsearch aggregation: the query Arranger sends to ES to compute those buckets. Aggregations are the technical backend; facets are their user-facing representation.
Filters, filter clauses, and SQONs
When a user selects one or more facet options, their selection is encoded as a SQON (Serializable Query Object Notation), Arranger's query language. A SQON is a tree structure made up of boolean combinators (and, or, not) and filter clauses.
A filter clause is a single field-level condition:
{
"op": "in",
"content": {
"fieldName": "disease_type",
"value": ["Leukemia", "Lymphoma"]
}
}
Within a filter clause, the content object identifies which field the condition applies to. For most operators this is a single fieldName property (a string): the name of one index field. The wildcard text-search operator instead uses fieldNames (a string array), because it matches a single value against multiple fields simultaneously. A planned fuzzy operator will take the same fieldNames shape; it is not implemented yet.
fieldName and fieldNames are names for properties of the SQON schema, not references to a field as a catalogue-configuration concept. Do not abbreviate either to field in code, comments, parameters, or documentation: field is ambiguous, while fieldName and fieldNames are unambiguous.
A SQON wraps one or more filter clauses under a combinator:
{
"op": "and",
"content": [
{ "op": "in", "content": { "fieldName": "disease_type", "value": ["Leukemia"] } },
{ "op": ">=", "content": { "fieldName": "age_at_diagnosis", "value": [18] } }
]
}
The word filter is used two ways: as a verb ("users filter the dataset") and as a noun that can mean either a single filter clause or a full SQON. When the distinction matters, use "filter clause" for one condition and "SQON" for the full query object.
Vocabulary reference
| Term | Meaning |
|---|---|
| model-agnostic | Arranger does not assume a specific data model or schema. Any correctly structured OpenSearch or Elasticsearch index can be used as a catalogue. |
| catalogue | One searchable dataset in Arranger, backed by an Elasticsearch index with its own configuration directory. Canadian spelling. |
catalogueId | The unique identifier for a catalogue; defaults to its config subdirectory name. Routes requests in multicatalogue mode. Not the same as documentType; see "Catalogues and configuration" above. |
documentType | The GraphQL root query field name for a catalogue's data. Not guaranteed unique across catalogues on the same server; never use it as a unique identifier in place of catalogueId. |
| configuration | The JSON files defining a catalogue (base, extended, facets, table). Use "configuration" in prose; config is acceptable in code identifiers. |
| directory | A filesystem directory. Not "folder". |
| aggregation | The Elasticsearch operation that computes buckets for a facet. |
| facet | A filterable field shown as a UI panel with selectable options (buckets). |
| bucket | One option within a facet: a distinct value and its document count. |
| SQON | The full structured query object passed to Arranger, built from one or more filter clauses. |
| filter clause | One field-level condition within a SQON (a single {op, content} leaf node). |
| filter | (verb) To narrow a dataset by selecting facet options. (noun) A SQON, or informally a single filter clause. |
fieldName | The string property in a filter clause's content that names the single index field the condition applies to. Used by most operators. Never abbreviate to field. |
fieldNames | The string-array property used instead of fieldName by the multi-field wildcard text operator (and by the planned, not-yet-implemented fuzzy). Matches one value against all listed fields simultaneously. Never abbreviate to fields or field. |
| settings | Elasticsearch's own term for index-level configuration (the ES settings API). Use "configuration" for Arranger-level concepts; keep "settings" when mirroring ES language. |