NyaruCollection

public struct NyaruCollection<T> : Sendable where T : Decodable, T : Encodable, T : Sendable

A typed, Sendable handle to one collection within a NyaruDB database.

NyaruCollection is a thin facade that encodes and decodes the generic document type T and delegates all storage operations to a shared CollectionCore actor. Handles are cheap value types — copy them freely.

Writing documents. Use insert, update, upsert, patch, and delete:

try await collection.insert(user)
try await collection.patch(id: userId, changes: ["name": "New Name"])

Reading documents. Use get(id:) for point lookups, all() for everything, stream() for bounded-memory iteration, and find() for fluent queries with predicates, sorting, and pagination.

Maintenance. Call compact() opportunistically (e.g. on app backgrounding) to reclaim space from tombstoned records.

Writes

  • insert(_:) Asynchronous

    Inserts a new document into the collection.

    The document must contain the configured idField. If another document with the same id already exists, a duplicateID error is thrown.

    Throws

    NyaruError.duplicateID if the id already exists, NyaruError.encodingFailed if encoding fails.

    Declaration

    Swift

    public func insert(_ document: T) async throws

    Parameters

    document

    The document to insert.

  • insert(contentsOf:) Asynchronous

    Inserts a batch of documents atomically.

    All ids are validated before anything is written — if any id already exists in the collection or is duplicated within the batch, no documents are inserted and duplicateID is thrown.

    Throws

    NyaruError.duplicateID if any id conflicts.

    Declaration

    Swift

    public func insert(contentsOf documents: [T]) async throws

    Parameters

    documents

    An array of documents to insert.

  • insertBatch(_:) Asynchronous

    Accumulates insert operations in memory and flushes them as a single batch when the body completes, producing one index merge pass instead of one per call.

    Insertions inside the body are synchronous — no await required:

    try await collection.insertBatch { batch in
      for chunk in incomingChunks { batch.insert(contentsOf: chunk) }
    }
    

    If the body throws, nothing is written to disk. This is a write buffer, not a transaction: update, delete, and patch are not buffered or rolled back — call them outside the batch.

    Throws

    Rethrows from body or from the final batch write.

    Declaration

    Swift

    public func insertBatch(
      _ body: (NyaruInsertBatch<T>) async throws -> Void
    ) async throws

    Parameters

    body

    Closure receiving a NyaruInsertBatch that accumulates documents via its synchronous insert methods.

  • update(_:) Asynchronous

    Replaces the document with the same id.

    The full document is replaced (not merged). If no document with the given id exists, documentNotFound is thrown. To insert-or-replace, use upsert instead.

    Throws

    NyaruError.documentNotFound if the id does not exist.

    Declaration

    Swift

    public func update(_ document: T) async throws

    Parameters

    document

    The updated document.

  • upsert(_:) Asynchronous

    Replaces the document with the same id, or inserts it if absent.

    Unlike update, this never throws documentNotFound — if no document exists for the id, the document is inserted.

    Declaration

    Swift

    public func upsert(_ document: T) async throws

    Parameters

    document

    The document to upsert.

  • delete(id:) Asynchronous

    Deletes a document by its id.

    The record is tombstoned in the shard file (space is reclaimed during compaction). The index entries are also removed.

    Declaration

    Swift

    @discardableResult
    public func delete(id: FieldValueConvertible) async throws -> Bool

    Parameters

    id

    The document id (any FieldValueConvertible type).

    Return Value

    true if a document was removed, false if no document with that id existed.

  • delete(ids:) Asynchronous

    Deletes multiple documents by id in a single batched pass: one storage hop per shard and one index sweep per field, instead of a full read/remove cycle per document.

    Unknown ids are skipped (they do not count toward the result and do not throw).

    Declaration

    Swift

    @discardableResult
    public func delete(ids: [FieldValueConvertible]) async throws -> Int

    Parameters

    ids

    The document ids to delete.

    Return Value

    The number of documents actually deleted.

Partial Update

  • patch(id:changes:) Asynchronous

    Partially updates a document by applying top-level field changes without decoding the full document type.

    This is useful when you want to change a few fields without fetching, decoding, modifying, re-encoding, and re-inserting the entire document. The merged document is validated against the type T before anything is written to disk.

    Restrictions:

    • Only top-level fields can be patched (nested paths like "address.city" are rejected).
    • The document id field cannot be changed through patch.
    • The merged result must still decode as T, otherwise the patch is rejected and no data is written.

    Throws

    NyaruError.documentNotFound if the id does not exist, NyaruError.decodingFailed if the merged document no longer decodes as T, NyaruError.unsupportedOperation for nested paths or id changes.

    Declaration

    Swift

    public func patch(id: FieldValueConvertible, changes: [String : FieldValue]) async throws

    Parameters

    id

    The document id.

    changes

    A dictionary of field names to new FieldValues.

Reads

  • get(id:) Asynchronous

    Performs a point lookup by document id through the primary index.

    Declaration

    Swift

    public func get(id: FieldValueConvertible) async throws -> T?

    Parameters

    id

    The document id.

    Return Value

    The decoded document, or nil if no document with that id exists.

  • count() Asynchronous

    Returns the total number of live documents in the collection.

    Throws

    NyaruError.databaseClosed if the database was closed.

    Declaration

    Swift

    public func count() async throws -> Int

    Return Value

    The live document count (excludes tombstoned records).

  • all() Asynchronous

    Returns every document in the collection.

    For large collections this materialises the entire dataset in memory. Consider using stream() for bounded-memory iteration.

    Throws

    NyaruError.decodingFailed if any document fails to decode.

    Declaration

    Swift

    public func all() async throws -> [T]

    Return Value

    An array of all decoded documents.

  • Returns a pull-based async sequence over all documents.

    Each next() call serves from an in-memory batch and only touches the collection actor when the batch is exhausted. Memory is bounded by one batch (batchSize elements), making this suitable for iterating large collections without materialising everything at once.

    Declaration

    Swift

    public func stream(batchSize: Int = 64) -> NyaruDocumentStream<T>

    Parameters

    batchSize

    Maximum documents fetched per storage read (default 64).

    Return Value

    A NyaruDocumentStream<T> (conforms to AsyncSequence).

  • Returns a fluent query builder for this collection.

    let results = try await collection.find()
        .where("age", isGreaterThan: 18)
        .sort(by: "name")
        .limit(10)
        .execute()
    

    Declaration

    Swift

    public func find() -> QueryBuilder<T>

    Return Value

    An immutable QueryBuilder<T>.

Maintenance

  • compact() Asynchronous

    Rewrites every shard file to remove tombstoned records and rebuilds all indexes.

    Call this opportunistically, such as when the app transitions to the background or after a significant number of deletions. There is no automatic background timer — compaction is always explicit.

    Throws

    I/O errors from shard or index operations.

    Declaration

    Swift

    public func compact() async throws
  • needsCompaction() Asynchronous

    Whether any shard’s tombstone ratio exceeds the configured DatabaseOptions.maxFragmentation threshold.

    Use this to decide when to call compact() — for example, on app backgrounding:

    if try await collection.needsCompaction() {
      try await collection.compact()
    }
    

    Throws

    NyaruError.databaseClosed if the database was closed.

    Declaration

    Swift

    public func needsCompaction() async throws -> Bool

    Return Value

    true if compaction would reclaim meaningful space.

  • stats() Asynchronous

    Returns a snapshot of collection statistics.

    The returned CollectionStats includes the document count, shard count, total on-disk size, index entry counts, and the fragmentation ratio.

    Throws

    NyaruError.databaseClosed if the database was closed.

    Declaration

    Swift

    public func stats() async throws -> CollectionStats

    Return Value

    Current collection statistics.