openapi: 3.0.3
info:
  title: SonnetDB Document API
  version: 0.0.0-dev
  description: >
    Private JSON API for SonnetDB document collections. This contract is
    intentionally SonnetDB-native and does not implement MongoDB wire protocol
    or BSON command compatibility.
paths:
  /v1/db/{db}/documents/{collection}:
    post:
      summary: Create a document collection.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentCollectionCreateRequest'
      responses:
        '200':
          description: Collection already exists.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentCollectionOperationResponse'
        '201':
          description: Collection created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentCollectionOperationResponse'
    delete:
      summary: Drop a document collection.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      responses:
        '200':
          description: Collection dropped.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentCollectionOperationResponse'
        '404':
          description: Collection does not exist.
  /v1/db/{db}/documents/{collection}/insert-one:
    post:
      summary: Insert one document.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentWriteItem'
      responses:
        '200':
          description: Write result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '400':
          description: Validation failure or invalid document.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '409':
          description: Duplicate key or write conflict.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
  /v1/db/{db}/documents/{collection}/validator:
    put:
      summary: Set or replace collection validator.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentValidator'
      responses:
        '200':
          description: Validator updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentValidatorResponse'
        '400':
          description: Invalid validator.
    delete:
      summary: Drop collection validator.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      responses:
        '200':
          description: Validator dropped.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentValidatorResponse'
        '404':
          description: Validator or collection does not exist.
  /v1/db/{db}/documents/{collection}/insert-many:
    post:
      summary: Insert many documents.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentInsertManyRequest'
      responses:
        '200':
          description: Write result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '207':
          description: Unordered batch committed valid items and returned item errors.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '400':
          description: Ordered batch validation failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '409':
          description: Ordered batch duplicate key or write conflict.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
  /v1/db/{db}/documents/{collection}/bulk-write:
    post:
      summary: Execute a mixed insert, replace, update, delete, and upsert batch.
      description: >
        Successful operations commit as one collection-local atomic KV batch.
        Ordered failures commit no document changes; unordered failures commit
        the valid subset atomically. requestId results are retained for 24 hours.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentBulkWriteRequest'
      responses:
        '200':
          description: Batch committed or a prior idempotent result was replayed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentBulkWriteResponse'
        '207':
          description: Unordered batch committed its valid subset and returned item errors.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentBulkWriteResponse'
        '400':
          description: Empty request or ordered validation failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentBulkWriteResponse'
        '409':
          description: Duplicate key, write conflict, or idempotency conflict.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentBulkWriteResponse'
        '413':
          description: Operation count, canonical payload, or atomic storage batch exceeds its budget; no document mutations are committed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentBulkWriteResponse'
  /v1/db/{db}/documents/{collection}/find:
    post:
      summary: Find documents with filter, projection, sort, limit, skip, and cursor pagination.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentFindRequest'
      responses:
        '200':
          description: Matching documents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentFindResponse'
  /v1/db/{db}/documents/{collection}/find-one:
    post:
      summary: Find one document by id.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentFindRequest'
      responses:
        '200':
          description: Single document lookup result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentFindOneResponse'
  /v1/db/{db}/documents/{collection}/update-one:
    post:
      summary: Replace one existing document.
      description: Whole-document replacement or operator update.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentUpdateOneRequest'
      responses:
        '200':
          description: Write result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
  /v1/db/{db}/documents/{collection}/find-one-and-update:
    post:
      summary: Atomically find and update one document.
      description: Returns the before or after image from the same single-document mutation.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentFindOneAndUpdateRequest'
      responses:
        '200':
          description: Match, upsert, and selected before/after image.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentFindOneAndUpdateResponse'
        '400':
          description: Invalid filter, update, upsert, or returnDocument value.
  /v1/db/{db}/documents/{collection}/update-many:
    post:
      summary: Replace many existing documents.
      description: Whole-document replacement batch or operator update. Missing ids are skipped for replacement batches.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentUpdateManyRequest'
      responses:
        '200':
          description: Write result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '207':
          description: Unordered batch committed valid items and returned item errors.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '400':
          description: Ordered batch validation failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '409':
          description: Ordered batch duplicate key or write conflict.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
  /v1/db/{db}/documents/{collection}/delete-one:
    post:
      summary: Delete one document by id.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentDeleteOneRequest'
      responses:
        '200':
          description: Write result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '207':
          description: Unordered batch committed valid items and returned item errors.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
        '400':
          description: Ordered batch validation failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
  /v1/db/{db}/documents/{collection}/delete-many:
    post:
      summary: Delete many documents by id.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentDeleteManyRequest'
      responses:
        '200':
          description: Write result.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentWriteResponse'
  /v1/db/{db}/documents/{collection}/count:
    post:
      summary: Count documents.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentCountRequest'
      responses:
        '200':
          description: Document count.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentCountResponse'
  /v1/db/{db}/documents/{collection}/distinct:
    post:
      summary: Return distinct scalar values for a JSON path.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentDistinctRequest'
      responses:
        '200':
          description: Distinct values.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentDistinctResponse'
  /v1/db/{db}/documents/{collection}/aggregate:
    post:
      summary: Run a document aggregation pipeline.
      description: >
        Supports SonnetDB-native JSON pipeline stages equivalent to $match,
        $project, $group, $sort, $limit, $skip, $unwind, $count, and $distinct.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentAggregateRequest'
      responses:
        '200':
          description: Aggregated documents.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentAggregateResponse'
  /v1/db/{db}/documents/{collection}/update-preview:
    post:
      summary: Preview partial update results without writing.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentUpdatePreviewRequest'
      responses:
        '200':
          description: Before/after documents generated by the production update executor.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentUpdatePreviewResponse'
  /v1/db/{db}/documents/{collection}/indexes:
    post:
      summary: Create a path, multikey, wildcard, unique, sparse, partial, or TTL document index.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentIndexCreateRequest'
      responses:
        '201':
          description: Index created and populated.
  /v1/db/{db}/documents/{collection}/indexes/{index}:
    delete:
      summary: Drop a document JSON path index.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
        - name: index
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Index dropped.
        '404':
          description: Index not found.
  /v1/db/{db}/documents/{collection}/indexes/validate:
    post:
      summary: Validate document index entries against primary documents.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      responses:
        '200':
          description: Read-only consistency report.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentIndexConsistencyResponse'
  /v1/db/{db}/documents/{collection}/change-feed:
    post:
      summary: Read the persistent collection change feed.
      parameters:
        - $ref: '#/components/parameters/db'
        - $ref: '#/components/parameters/collection'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DocumentChangeFeedRequest'
      responses:
        '200':
          description: Change batch and bound resume token.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DocumentChangeFeedResponse'
        '410':
          description: Resume position is outside the retained event window.
components:
  parameters:
    db:
      name: db
      in: path
      required: true
      schema:
        type: string
    collection:
      name: collection
      in: path
      required: true
      schema:
        type: string
  schemas:
    DocumentCollectionCreateRequest:
      type: object
      properties:
        ifNotExists:
          type: boolean
          default: true
        validator:
          $ref: '#/components/schemas/DocumentValidator'
          nullable: true
    DocumentCollectionOperationResponse:
      type: object
      properties:
        collection:
          type: string
        status:
          type: string
    DocumentWriteItem:
      type: object
      required: [id, document]
      properties:
        id:
          type: string
        document:
          type: object
          additionalProperties: true
    DocumentInsertManyRequest:
      type: object
      required: [documents]
      properties:
        documents:
          type: array
          items:
            $ref: '#/components/schemas/DocumentWriteItem'
        ordered:
          type: boolean
          default: true
          description: true rejects and rolls back the whole batch on first error; false commits valid items and returns item errors.
    DocumentFindRequest:
      type: object
      properties:
        id:
          type: string
          description: Legacy shortcut for _id equality. Can be combined with filter.
        ids:
          type: array
          description: Legacy shortcut for _id IN. Can be combined with filter.
          items:
            type: string
        filter:
          $ref: '#/components/schemas/DocumentFilter'
        projection:
          type: array
          items:
            $ref: '#/components/schemas/DocumentProjection'
        sort:
          type: array
          items:
            $ref: '#/components/schemas/DocumentSort'
        limit:
          type: integer
          minimum: 1
          maximum: 1000
          description: Values above 1000 are clamped by the server in this first version.
        skip:
          type: integer
          minimum: 0
          default: 0
        continuationToken:
          type: string
          nullable: true
          description: Token returned by a previous find response. Cannot be combined with skip.
        collation:
          type: string
          enum: [ordinal, ordinal_ignore_case]
          default: ordinal
          description: Basic string comparison mode for filters and sorting.
    DocumentFilter:
      type: object
      description: Recursive filter AST. Leaf paths accept _id/id, document/json, or JSON path such as $.metrics.temp.
      properties:
        path:
          type: string
          example: $.site
        op:
          type: string
          enum: [eq, ne, gt, gte, lt, lte, in, nin, exists, contains, elemMatch, regex, type, size, all]
          default: eq
        value:
          nullable: true
          description: Scalar, array, object, or null value used by the operator.
        and:
          type: array
          items:
            $ref: '#/components/schemas/DocumentFilter'
        or:
          type: array
          items:
            $ref: '#/components/schemas/DocumentFilter'
        not:
          $ref: '#/components/schemas/DocumentFilter'
        elemMatch:
          $ref: '#/components/schemas/DocumentFilter'
          description: Element-level filter used only when op is elemMatch.
        regexOptions:
          type: string
          pattern: '^[icmsx]*$'
          description: Bounded regular-expression options. Regex case handling is independent of collation.
    DocumentProjection:
      type: object
      properties:
        name:
          type: string
          description: Output property name. If omitted, the server derives a name from path.
        path:
          type: string
          description: _id/id, document/json, or JSON path.
          example: $.metrics.temp
    DocumentSort:
      type: object
      required: [path]
      properties:
        path:
          type: string
          description: _id/id, document/json, or JSON path.
          example: $.score
        descending:
          type: boolean
          default: false
    DocumentItemResponse:
      type: object
      properties:
        id:
          type: string
        document:
          type: object
          additionalProperties: true
        version:
          type: integer
          format: int64
    DocumentFindResponse:
      type: object
      properties:
        collection:
          type: string
        documents:
          type: array
          items:
            $ref: '#/components/schemas/DocumentItemResponse'
        count:
          type: integer
        limit:
          type: integer
          nullable: true
        skip:
          type: integer
        continuationToken:
          type: string
          nullable: true
          description: Token for the next page. Null means there are no more documents.
        hasMore:
          type: boolean
        batchSize:
          type: integer
          nullable: true
        snapshotVersion:
          type: integer
          format: int64
          nullable: true
          description: Read-only snapshot boundary captured by the cursor.
        cursorExpiresAtUtc:
          type: string
          format: date-time
          nullable: true
    DocumentFindOneResponse:
      type: object
      properties:
        collection:
          type: string
        found:
          type: boolean
        document:
          allOf:
            - $ref: '#/components/schemas/DocumentItemResponse'
          nullable: true
    DocumentUpdate:
      type: object
      description: SonnetDB-native partial update operators keyed by JSON path.
      properties:
        set: { type: object, additionalProperties: true }
        unset: { type: object, additionalProperties: true }
        inc: { type: object, additionalProperties: true }
        min: { type: object, additionalProperties: true }
        max: { type: object, additionalProperties: true }
        rename: { type: object, additionalProperties: { type: string } }
        push: { type: object, additionalProperties: true }
        pull: { type: object, additionalProperties: true }
        addToSet: { type: object, additionalProperties: true }
        currentDate: { type: object, additionalProperties: true }
        mul: { type: object, additionalProperties: true }
        pop:
          type: object
          description: JSON path values must be -1 (first element) or 1 (last element).
          additionalProperties:
            type: integer
            enum: [-1, 1]
    DocumentUpdateOneRequest:
      type: object
      properties:
        id: { type: string, nullable: true }
        document: { nullable: true }
        filter:
          $ref: '#/components/schemas/DocumentFilter'
        update:
          $ref: '#/components/schemas/DocumentUpdate'
        upsert: { type: boolean, default: false }
        upsertId: { type: string, nullable: true }
    DocumentUpdateManyRequest:
      type: object
      properties:
        documents:
          type: array
          items:
            $ref: '#/components/schemas/DocumentWriteItem'
        filter:
          $ref: '#/components/schemas/DocumentFilter'
        update:
          $ref: '#/components/schemas/DocumentUpdate'
        upsert: { type: boolean, default: false }
        upsertId: { type: string, nullable: true }
        ordered: { type: boolean, default: true }
    DocumentFindOneAndUpdateRequest:
      type: object
      required: [update]
      properties:
        id: { type: string, nullable: true }
        filter:
          $ref: '#/components/schemas/DocumentFilter'
        update:
          $ref: '#/components/schemas/DocumentUpdate'
        upsert: { type: boolean, default: false }
        upsertId: { type: string, nullable: true }
        returnDocument:
          type: string
          enum: [before, after]
          default: before
    DocumentFindOneAndUpdateResponse:
      type: object
      properties:
        collection: { type: string }
        found: { type: boolean }
        document:
          allOf:
            - $ref: '#/components/schemas/DocumentItemResponse'
          nullable: true
        inserted: { type: integer }
        matched: { type: integer }
        modified: { type: integer }
        errors:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/DocumentWriteError'
    DocumentDeleteOneRequest:
      type: object
      required: [id]
      properties:
        id:
          type: string
    DocumentDeleteManyRequest:
      type: object
      required: [ids]
      properties:
        ids:
          type: array
          items:
            type: string
        ordered:
          type: boolean
          default: true
          description: true rejects and rolls back the whole batch on first error; false commits valid IDs and returns item errors.
    DocumentWriteError:
      type: object
      properties:
        index:
          type: integer
          description: Zero-based item index in the original batch.
        id:
          type: string
          nullable: true
        code:
          type: string
          enum: [duplicate_key, validation_failed, write_conflict, document_too_large, batch_too_large, idempotency_conflict, not_attempted]
        message:
          type: string
        severity:
          type: string
          enum: [error, warning]
          default: error
    DocumentWriteResponse:
      type: object
      properties:
        collection:
          type: string
        inserted:
          type: integer
        matched:
          type: integer
        modified:
          type: integer
        deleted:
          type: integer
        errors:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/DocumentWriteError'
    DocumentBulkWriteOperation:
      type: object
      description: Fields that are not meaningful for the selected operation type are rejected with validation_failed.
      required: [type]
      properties:
        type:
          type: string
          enum: [insertOne, replaceOne, updateOne, updateMany, deleteOne, deleteMany]
        id: { type: string, nullable: true }
        document: { nullable: true }
        filter:
          $ref: '#/components/schemas/DocumentFilter'
        update:
          $ref: '#/components/schemas/DocumentUpdate'
        upsert: { type: boolean, default: false }
        upsertId: { type: string, nullable: true }
        expectedVersion: { type: integer, format: int64, minimum: 0, nullable: true }
    DocumentBulkWriteRequest:
      type: object
      required: [operations]
      properties:
        operations:
          type: array
          minItems: 1
          maxItems: 1000
          items:
            $ref: '#/components/schemas/DocumentBulkWriteOperation'
        ordered: { type: boolean, default: true }
        requestId:
          type: string
          maxLength: 256
          nullable: true
          description: Collection-local idempotency key retained for 24 hours.
    DocumentBulkWriteItemResponse:
      type: object
      properties:
        index: { type: integer, minimum: 0 }
        operation: { type: string }
        id: { type: string, nullable: true }
        status: { type: string, enum: [succeeded, no_op, failed, not_attempted] }
        inserted: { type: integer }
        matched: { type: integer }
        modified: { type: integer }
        deleted: { type: integer }
        upsertedId: { type: string, nullable: true }
        error:
          allOf:
            - $ref: '#/components/schemas/DocumentWriteError'
          nullable: true
    DocumentBulkWriteResponse:
      type: object
      properties:
        collection: { type: string }
        inserted: { type: integer }
        matched: { type: integer }
        modified: { type: integer }
        deleted: { type: integer }
        items:
          type: array
          items:
            $ref: '#/components/schemas/DocumentBulkWriteItemResponse'
        errors:
          type: array
          nullable: true
          items:
            $ref: '#/components/schemas/DocumentWriteError'
        requestId: { type: string, nullable: true }
        replayed: { type: boolean }
        committed: { type: boolean }
    DocumentValidator:
      type: object
      required: [rules]
      properties:
        validationAction:
          type: string
          enum: [error, warn]
          default: error
        rules:
          type: array
          minItems: 1
          items:
            $ref: '#/components/schemas/DocumentValidatorRule'
    DocumentValidatorRule:
      type: object
      required: [path]
      properties:
        path:
          type: string
          example: $.site
        required:
          type: boolean
          default: false
        type:
          type: string
          enum: [string, number, integer, boolean, object, array, 'null']
        types:
          type: array
          items:
            type: string
            enum: [string, number, integer, boolean, object, array, 'null']
        minimum:
          type: number
          format: double
        maximum:
          type: number
          format: double
        enum:
          type: array
          items: {}
        pattern:
          type: string
          description: Regular expression applied to string values.
    DocumentValidatorResponse:
      type: object
      properties:
        collection:
          type: string
        status:
          type: string
          enum: [updated, dropped, missing]
        validator:
          $ref: '#/components/schemas/DocumentValidator'
          nullable: true
    DocumentCountRequest:
      type: object
      properties:
        ids:
          type: array
          items:
            type: string
    DocumentCountResponse:
      type: object
      properties:
        collection:
          type: string
        count:
          type: integer
          format: int64
    DocumentDistinctRequest:
      type: object
      required: [path]
      properties:
        path:
          type: string
          example: $.site
        ids:
          type: array
          items:
            type: string
        limit:
          type: integer
          minimum: 1
          maximum: 1000
    JsonElementValue:
      type: object
      properties:
        kind:
          type: integer
          description: '0 = Null, 1 = String, 2 = Integer, 3 = Double, 4 = Boolean.'
          enum: [0, 1, 2, 3, 4]
        stringValue:
          type: string
          nullable: true
        integerValue:
          type: integer
          format: int64
          nullable: true
        doubleValue:
          type: number
          format: double
          nullable: true
        booleanValue:
          type: boolean
          nullable: true
    DocumentDistinctResponse:
      type: object
      properties:
        collection:
          type: string
        path:
          type: string
        values:
          type: array
          items:
            $ref: '#/components/schemas/JsonElementValue'
    DocumentAggregateRequest:
      type: object
      required: [pipeline]
      properties:
        pipeline:
          type: array
          items:
            $ref: '#/components/schemas/DocumentAggregateStage'
    DocumentAggregateStage:
      type: object
      description: Exactly one stage discriminator must be present. computedFields may accompany $project.
      properties:
        $match:
          $ref: '#/components/schemas/DocumentFilter'
        $project:
          type: array
          items:
            $ref: '#/components/schemas/DocumentProjection'
        $group:
          $ref: '#/components/schemas/DocumentAggregateGroup'
        $sort:
          type: array
          items:
            $ref: '#/components/schemas/DocumentSort'
        $limit:
          type: integer
          minimum: 1
        $skip:
          type: integer
          minimum: 0
        $unwind:
          $ref: '#/components/schemas/DocumentAggregateUnwind'
        $count:
          type: string
          default: count
        $distinct:
          $ref: '#/components/schemas/DocumentAggregateDistinct'
        computedFields:
          type: array
          description: Computed fields evaluated by the same $project stage.
          items:
            $ref: '#/components/schemas/DocumentAggregateComputedField'
    DocumentAggregateComputedField:
      type: object
      required: [name, expression]
      properties:
        name:
          type: string
        expression:
          $ref: '#/components/schemas/DocumentAggregateExpression'
    DocumentAggregateExpression:
      type: object
      required: [op]
      description: >
        SonnetDB-native expression AST. field requires path; literal uses value;
        arithmetic and if_null require two arguments; cond requires three;
        concat accepts one or more arguments.
      properties:
        op:
          type: string
          enum: [field, literal, add, subtract, multiply, divide, concat, if_null, cond]
        path:
          type: string
          nullable: true
        value:
          nullable: true
        arguments:
          type: array
          maxItems: 64
          items:
            $ref: '#/components/schemas/DocumentAggregateExpression'
    DocumentAggregateGroup:
      type: object
      properties:
        keys:
          type: array
          items:
            $ref: '#/components/schemas/DocumentAggregateGroupKey'
        accumulators:
          type: array
          items:
            $ref: '#/components/schemas/DocumentAggregateAccumulator'
    DocumentAggregateGroupKey:
      type: object
      required: [name]
      properties:
        name:
          type: string
        path:
          type: string
          nullable: true
          example: $.site
        expression:
          $ref: '#/components/schemas/DocumentAggregateExpression'
    DocumentAggregateAccumulator:
      type: object
      required: [name, op]
      properties:
        name:
          type: string
        op:
          type: string
          enum: [count, sum, avg, average, min, max, first, last, distinct, push, addToSet]
        path:
          type: string
          nullable: true
          example: $.score
        expression:
          $ref: '#/components/schemas/DocumentAggregateExpression'
    DocumentAggregateUnwind:
      type: object
      required: [path]
      properties:
        path:
          type: string
          example: $.tags
        name:
          type: string
          nullable: true
        preserveNullAndEmptyArrays:
          type: boolean
          default: false
        includeArrayIndex:
          type: string
          nullable: true
    DocumentAggregateDistinct:
      type: object
      required: [path]
      properties:
        path:
          type: string
          example: $.site
        name:
          type: string
          default: value
        limit:
          type: integer
          minimum: 1
    DocumentAggregateResponse:
      type: object
      properties:
        collection:
          type: string
        documents:
          type: array
          items:
            type: object
            additionalProperties: true
        count:
          type: integer
    DocumentUpdatePreviewRequest:
      type: object
      required: [update]
      properties:
        filter:
          $ref: '#/components/schemas/DocumentFilter'
        update:
          $ref: '#/components/schemas/DocumentUpdate'
        many:
          type: boolean
          default: false
        limit:
          type: integer
          minimum: 1
          maximum: 100
          default: 20
        upsert:
          type: boolean
          default: false
        upsertId:
          type: string
          nullable: true
    DocumentUpdatePreviewResponse:
      type: object
      properties:
        collection:
          type: string
        matched:
          type: integer
        changed:
          type: integer
        documents:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              version: { type: integer, format: int64 }
              before: { nullable: true }
              after: {}
              isUpsert: { type: boolean }
              changed: { type: boolean }
    DocumentIndexCreateRequest:
      type: object
      required: [name, paths]
      properties:
        name: { type: string }
        paths:
          type: array
          minItems: 1
          items: { type: string }
        isUnique: { type: boolean, default: false }
        isSparse: { type: boolean, default: false }
        partialFilter:
          type: object
          nullable: true
          properties:
            path: { type: string }
            operator: { type: string, enum: [exists, eq, ne, gt, gte, lt, lte] }
            valueScalar: { type: string, nullable: true }
        ttlPath: { type: string, nullable: true }
        ttlSeconds: { type: integer, format: int64, minimum: 1, nullable: true }
        kind:
          type: string
          enum: [path, wildcard]
          default: path
          description: wildcard requires exactly one subtree root path and cannot be unique or TTL.
    DocumentIndexConsistencyResponse:
      type: object
      properties:
        collection: { type: string }
        documentCount: { type: integer }
        isConsistent: { type: boolean }
        indexes:
          type: array
          items:
            type: object
            properties:
              index: { type: string }
              isConsistent: { type: boolean }
              expectedEntries: { type: integer }
              actualEntries: { type: integer }
              missingEntries: { type: integer }
              orphanEntries: { type: integer }
    DocumentChangeFeedRequest:
      type: object
      properties:
        resumeToken: { type: string, nullable: true }
        startAt: { type: string, enum: [now, beginning], default: now }
        limit: { type: integer, minimum: 1, maximum: 1000, default: 100 }
        operations:
          type: array
          items: { type: string, enum: [insert, update, delete] }
        documentId: { type: string, nullable: true }
    DocumentChangeFeedResponse:
      type: object
      properties:
        collection: { type: string }
        changes:
          type: array
          items:
            type: object
            properties:
              sequence: { type: integer, format: int64 }
              occurredAtUtc: { type: string, format: date-time }
              operation: { type: string, enum: [insert, update, delete] }
              documentId: { type: string }
              documentVersion: { type: integer, format: int64 }
              before: { nullable: true }
              after: { nullable: true }
              payloadTruncated: { type: boolean }
        resumeToken: { type: string }
        hasMore: { type: boolean }
        latestSequence: { type: integer, format: int64 }
        oldestAvailableSequence: { type: integer, format: int64, nullable: true }
        resumeTokenExpiresAtUtc: { type: string, format: date-time }
