Rest API Design for OpenSearch Integration

Rest API Design for OpenSearch Integration

Portal Search: REST API Reference

Author: Bryan Fauble


Overview

Portal Search provides REST endpoints for creating OpenSearch-backed search indexes over Synapse table-like entities and executing full-text search queries against them.

Resources

The API is organized around four standalone configuration resources under /repo/v1/search/* and one SearchIndex Synapse Entity managed via the standard entity controller under /repo/v1/entity/*.

All resources are logically scoped to an Organization (an existing Synapse concept — CRUD and ACL management at /schema/organizations/*), but note that SearchIndex does not have its own organizationId field; it is scoped via the standard entity parentId (Project or Folder).

Resource

Purpose

Resource

Purpose

SynonymSet

Reusable synonym rules (e.g., "NF1" = "neurofibromatosis 1"). Cannot be deleted while referenced by a SearchConfiguration.

ColumnAnalyzerOverride

Per-column text analysis overrides. Cannot be deleted while referenced by a SearchConfiguration.

TextAnalyzer

A standalone reusable resource that defines how text is analyzed (tokenizer, filters, synonym awareness). Referenced by ColumnAnalyzerOverrideEntry (indexAnalyzerId/searchAnalyzerId) and SearchConfiguration (defaultAnalyzerId). System analyzers (IDs 1-6) are bootstrapped on startup; user-defined start at ID 1000+.

SearchConfiguration

Bundles synonym sets and column overrides. References a default TextAnalyzer (defaultAnalyzerId) for columns without explicit overrides. Can be associated with projects or folders via the Project Settings framework for inheritance.

SearchIndex (Entity)

A Synapse Entity (concreteType: "org.sagebionetworks.repo.model.table.search.SearchIndex") that ties a Synapse table-like entity (via definingSQL) to an OpenSearch index, optionally referencing a SearchConfiguration. The source entity is parsed from definingSQL at runtime. CRUD is via /repo/v1/entity/*; only search operations use /repo/v1/search/*.

Resources compose as:

SearchIndex → SearchConfiguration → {SynonymSets[], ColumnAnalyzerOverrides[]}.

TextAnalyzer is a standalone resource referenced by ColumnAnalyzerOverrideEntry.indexAnalyzerId/searchAnalyzerId and SearchConfiguration.defaultAnalyzerId.

Design Principles

  • Organization-scoped authorization for configuration. The four configuration resources (SynonymSet, ColumnAnalyzerOverride, TextAnalyzer, SearchConfiguration) are Organization-scoped and identified by organizationId. They are publicly readable; mutations require Organization ACL permissions. SearchIndex is a standard Synapse Entity without its own organizationId field; it is scoped by parentId (Project or Folder), and its ACL follows normal entity rules.

  • Secure search access. Search queries and autocomplete requests are not fully public. Callers must have:

    • READ on the SearchIndex entity and

    • Sufficient permissions to read from the underlying table referenced by definingSQL (mirrors the MaterializedView authorization pattern).

  • Build-once semantics with rebuild on update. AOSS indexes are point-in-time snapshots. There are no incremental updates. The lifecycle worker builds (or rebuilds) indexes on both CREATE and UPDATE of SearchIndex entities.

  • Single-entity constraint. definingSQL must reference exactly one entity. Multi-entity JOINs are rejected with 400 Bad Request.

  • Shared resource protection. SynonymSets and ColumnAnalyzerOverrides cannot be deleted while referenced by any SearchConfiguration.

  • Name uniqueness.

    • For SynonymSet, ColumnAnalyzerOverride, and SearchConfiguration, names are unique per Organization: UNIQUE(organizationId, name).

    • For TextAnalyzer, system analyzer names are globally unique; user-defined analyzer names are unique per Organization.

    • For SearchIndex (as a Synapse Entity), name uniqueness follows the standard entity rules: unique within its parent container (parentId).

  • Single polling point. Search queries run as async jobs. If an index is still building, the worker automatically retries — the client only polls the async result endpoint.


Quick Start

Step 1: Create Supporting Resources

Create a synonym set in your Organization:

POST /repo/v1/search/synonym/set
{ "organizationId": "42", "name": "NF Disease Terms", "description": "Synonym mappings for neurofibromatosis-related disease terminology", "rules": [ { "ruleType": "EQUIVALENT", "terms": ["NF1", "neurofibromatosis 1", "von Recklinghausen disease"] }, { "ruleType": "EQUIVALENT", "terms": ["schwannoma", "vestibular schwannoma", "acoustic neuroma"] } ] }

Create a search configuration referencing the synonym set:

POST /repo/v1/search/configuration
{ "organizationId": "42", "name": "NF Portal Config", "synonymSetIds": ["501"], "defaultAnalyzerId": "1", "columnAnalyzerOverrideIds": [] }

Step 2: Create a Search Index Entity

SearchIndex is a Synapse Entity, not a standalone /search/index resource. Creation goes through the standard entity controller. Use the SearchIndex concrete type and provide a parentId (a Project or Folder):

POST /repo/v1/entity
{ "concreteType": "org.sagebionetworks.repo.model.table.search.SearchIndex", "parentId": "syn123", "name": "Studies Search", "definingSQL": "SELECT studyName, summary, diseaseFocus, species, assay FROM syn52694652", "searchConfigurationId": "301" }

Sample Response (201 Created):

{ "id": "syn101", "concreteType": "org.sagebionetworks.repo.model.table.search.SearchIndex", "parentId": "syn123", "name": "Studies Search", "definingSQL": "SELECT studyName, summary, diseaseFocus, species, assay FROM syn52694652", "searchConfigurationId": "301", "versionNumber": 1, "etag": "aaa-bbb-ccc", "createdBy": "3350396", "createdOn": "2026-02-18T12:00:00.000Z", "modifiedBy": "3350396", "modifiedOn": "2026-02-18T12:00:00.000Z" }

Comments