feat(8.0): upsert + FindParams.includeVectors + removeMany adaptive chunking

Three additive ergonomics from the API-simplification audit (no behavior change
to existing call sites):

- AddParams.upsert: create-or-update in one call. With a custom id, an existing
  entity is MERGED via the update path (merges metadata, re-embeds changed data,
  bumps _rev, PRESERVES createdAt) instead of the destructive full overwrite a
  plain add() does. Mutually exclusive with ifAbsent (throws if both set);
  ignored when no id is supplied. Wired into add(), addMany (per-item flag
  propagation), and the transact add op (routes to planTxUpdate). Kills the
  get()-then-add() round-trip for idempotent writes.
- FindParams.includeVectors: mirror of GetOptions.includeVectors — find() returns
  stored vectors when set; default stays empty (the perf contract is preserved).
  Honored on both the query and metadata-only where paths, and in db.find().
- removeMany adaptive chunking: replaced the hardcoded chunkSize=10 with
  params.chunkSize ?? storageConfig.maxBatchSize, matching addMany/relateMany —
  one storage-adaptive batch policy across all *Many methods (no thrash on
  high-latency backends).

Tests: tests/unit/brainy/upsert.test.ts (insert/merge/createdAt-preserved/
re-embed/ifAbsent-conflict/no-id/addMany/transact), find-include-vectors.test.ts
(true/default/where-path), batch-operations.test.ts (removeMany >10 items).
This commit is contained in:
David Snelling 2026-06-20 16:34:20 -07:00
parent 1bc709d31b
commit 4cc2088aed
6 changed files with 481 additions and 12 deletions

View file

@ -371,12 +371,12 @@ export class Db<T = any> {
entity = await this.host.entityFromRecord(
id,
{ metadata: resolved.metadata, vector: resolved.vector },
false
params.includeVectors ?? false
)
} else if (resolved.source === 'current') {
// Defensive: changed ids always resolve to a record or absent, but
// a 'current' answer is still served correctly from the live path.
entity = await this.host.get(id)
entity = await this.host.get(id, { includeVectors: params.includeVectors ?? false })
}
if (entity && entityMatchesFind(entity as Entity, params as FindParams)) {
merged.push(resultFromEntity(entity))