Summaries and indexes

Blobify publishes derived JSON so a frontend can enumerate content, find one entry by a known value, or page through an ordered feed without scanning every content document.

Summary shards

Summary shards are the broad listing layer. Each draft or published state has one manifest and a set of per-model immutable shard files.

textcode
spaces/main/summaries/published/index.json
spaces/main/summaries/published/article/s000.1783796607793.json
spaces/main/summaries/draft/index.json
spaces/main/summaries/draft/article/s000.1783796607793.json

The manifest uses format version 2 and maps each logical shard name to its current versioned object key:

jsoncode
{
  "formatVersion": 2,
  "state": "published",
  "models": {
    "article": {
      "total": 2,
      "shardCount": 1,
      "shards": {
        "s000": "s000.1783796607793.json"
      },
      "version": 1783796607793
    }
  }
}

Always fetch index.json first, then fetch the file names in the model's shards map. Shard files are immutable. Clients must not guess the current version from a timestamp or build s000.{version}.json themselves.

Shard count is adaptive. Blobify uses 1, 16, or 64 shards based on the size and current tier of the dataset, so consumers must follow the manifest instead of assuming a fixed count.

summaryFields controls which content fields appear. summaryMetadata controls configurable system metadata in published summaries. Draft summaries also include the metadata required by the dashboard, including publishedLocales, publish and update status, and the draft-only updatedBy audit event. Published summaries never expose the editor actor.

The dashboard's content list reads the draft summary manifest and the immutable shard keys it names. Published sites normally read the published state.

Field indexes

Field indexes support exact lookups such as slug to content ID. Configure them with fieldIndexes on a model.

jsoncode
{
  "fieldIndexes": [
    { "field": "slug", "normalizer": "slug-v1", "unique": true }
  ]
}

Non-translatable and translatable fields use different logical paths:

textcode
spaces/main/lookups/published/article/sku/s076.json
spaces/main/lookups/published/article/slug/en/s076.json

The shard is chosen from the normalized field value, not the content ID. slug-v1 trims and lowercases its input before lookup. exact-v1 keeps exact-value semantics.

The generated client's getOne(...) reads the relevant value shard and then loads the content document. API upsert uses the same index path, with a content-document scan as a correctness fallback while derived output is still converging.

Field indexes maintain internal reverse-pointer shards under lookups/{state}/{model}/_reverse/ so the worker can remove stale values after an edit. Direct clients read the value shards, not those reverse pointers.

Unique fields

Saving a model automatically derives a required field index for each text or slug field marked unique: true. Translatable fields are unique per locale. Non-translatable fields are unique globally.

List indexes

List indexes provide ordered, locale-specific feeds. A model config chooses a field or metadata sort source and an ascending or descending order.

jsoncode
{
  "listIndexes": [
    {
      "id": "latest",
      "source": { "kind": "metadata", "name": "lastPublished" },
      "order": "desc",
      "state": "published"
    }
  ]
}

List index v2 uses one manifest and generational page files:

textcode
spaces/main/list-indexes/published/article/latest/en/index.json
spaces/main/list-indexes/published/article/latest/en/g3/p00000.json
spaces/main/list-indexes/published/article/latest/en/g3/p00001-v4.json

The manifest is the ordered source of truth:

jsoncode
{
  "formatVersion": 2,
  "model": "article",
  "indexId": "latest",
  "state": "published",
  "locale": "en",
  "gen": 3,
  "total": 2011,
  "pages": [
    { "key": "g3/p00000.json", "count": 1024 },
    { "key": "g3/p00001-v4.json", "count": 987 }
  ]
}

pages[].key is relative to the locale directory and must be treated as opaque. Storage-page counts vary, and the manifest count is authoritative. There is no model pageSize setting for these storage pages, no reverse-shard directory, and no nextPage cursor chain between page files.

The generated client's listByIndex(model, indexId, locale, { page, pageSize }) computes UI pagination across one or more storage pages and returns { items, total, totalPages, page, nextPage }. That runtime pageSize is independent of storage page sizing.

Rebuild derived output

Schema saves do not rebuild existing derived files. After changing summary fields, field indexes, list indexes, display fields, or unique flags on an existing model, run the narrowest rebuild that covers the change.

ScopeEndpoint
All spacesPOST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-summaries
All spacesPOST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-field-indexes
All spacesPOST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-list-indexes
All spacesPOST /v1/orgs/{orgId}/schemas/models/{model}/rebuild-all
One spacePOST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-summaries
One spacePOST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-field-indexes
One spacePOST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-list-indexes
One spacePOST /v1/orgs/{orgId}/content/{spaceId}/{model}/rebuild-all

Each endpoint accepts optional ?state=draft or ?state=published. With no state, it rebuilds both. A valid request returns 202 Accepted after enqueueing work. The queue worker performs the rebuild asynchronously.

Content prefixes

If a space sets contentPrefix, Blobify rewrites summary, lookup, and list-index keys under the physical prefix:

textcode
spaces/main/_blobify/site_a1/summaries/published/index.json
spaces/main/_blobify/site_a1/lookups/published/article/slug/en/s076.json
spaces/main/_blobify/site_a1/list-indexes/published/article/latest/en/index.json

The dashboard and generated client handle this automatically.

Pick the right output

  • Use a content document when you know the content ID and need full fields.
  • Use summaries for broad enumeration and build-time lists.
  • Use a field index for an exact lookup by a configured value.
  • Use a list index when ordering and scalable UI pagination matter.

Summary fields, field indexes, and list indexes are independent model settings. Including slug in a summary does not create a field index, and adding a field index does not add that field to summaries.