Skip to content

v7.29.0 β€” subtype primitive + trackField + migrateField

Choose a tag to compare

@dpsifr dpsifr released this 05 Jun 00:26
· 28 commits to main since this release

Affected products: anyone modeling entities with per-product sub-classification β€” every
consumer that's been reaching for metadata.kind, a custom metadata.subtype field, a
data.kind shape inside the payload, or similar ad-hoc conventions. Additive; drop-in from
7.28.x. No deprecations.

New β€” subtype promoted to a top-level standard field

subtype?: string is now a first-class standard field on every entity, alongside type /
confidence / weight. Use it as the platform-standard primitive for sub-classifying entities
within a NounType (Person β†’ 'employee' / 'customer'; Document β†’ 'invoice' / 'contract';
Event β†’ 'milestone' / 'meeting'):

await brain.add({
  data: 'Avery Brooks β€” runs the AI lab',
  type: NounType.Person,
  subtype: 'employee',                  // top-level write param
  metadata: { department: 'ai-lab' }
})

// Fast-path filter β€” column-store hit, not metadata fallback:
const employees = await brain.find({ type: NounType.Person, subtype: 'employee' })

// Set membership:
const internal = await brain.find({
  type: NounType.Person,
  subtype: ['employee', 'contractor']
})

Flat string, no hierarchy β€” your vocabulary, your choice. Brainy stores and counts, never validates.

New β€” O(1) subtype counts via the persisted rollup

A new _system/subtype-statistics.json rollup is maintained incrementally as entities are added,
updated, and deleted β€” mirroring the existing nounCountsByType machinery with the same self-heal
behavior on poison detection. Counts are O(1) at billion scale:

brain.counts.bySubtype(NounType.Person)
// β†’ { employee: 12, customer: 847, vendor: 34 }

brain.counts.bySubtype(NounType.Person, 'employee')   // O(1) point count
// β†’ 12

brain.counts.topSubtypes(NounType.Person, 3)
// β†’ [['customer', 847], ['employee', 12], ['vendor', 34]]

brain.subtypesOf(NounType.Person)
// β†’ ['customer', 'employee', 'vendor']

New β€” brain.trackField() for other metadata facets

For facets that aren't the primary sub-classification (status, source, role, paradigm),
trackField registers a field for cardinality + per-NounType breakdown stats without promoting
each one to a top-level slot. Piggybacks on the existing aggregation engine, so backfill-on-define
applies β€” registering on a populated brain scans existing entities on the first query:

brain.trackField('status', { perType: true })

await brain.counts.byField('status')
// β†’ { todo: 12, doing: 3, done: 47 }

await brain.counts.byField('status', { type: NounType.Task })
// β†’ { todo: 8, doing: 2, done: 30 }

// Opt-in vocabulary validation:
brain.trackField('priority', { values: ['low', 'medium', 'high'] })
// brain.add({ ..., metadata: { priority: 'urgent' } }) β†’ throws

New β€” generic brain.migrateField() for one-shot rewrites

Streams every entity and copies a value from one field path to another. Supports top-level
standard fields, metadata.*, and data.* paths; idempotent; with optional readBoth: true
deprecation window that preserves the source field alongside the new one:

// Phase 1: dual-populate (legacy readers still work)
await brain.migrateField({
  from: 'metadata.kind',
  to: 'subtype',
  readBoth: true
})

// ...readers migrate to subtype at their own pace...

// Phase 2: clear the source field
await brain.migrateField({ from: 'metadata.kind', to: 'subtype' })
// β†’ { scanned: 1500, migrated: 1500, skipped: 0, errors: [] }

Supports batchSize and onProgress for large brains.

Aggregation composition

subtype is a standard-field group-by dimension out of the box β€” no where: wrapper, no
metadata-fallback overhead:

await brain.find({
  aggregate: {
    groupBy: ['type', 'subtype'],
    metrics: { count: { op: 'COUNT' } }
  }
})
// [
//   { groupKey: { type: 'person',   subtype: 'customer' }, count: 847 },
//   { groupKey: { type: 'person',   subtype: 'employee' }, count: 12 },
//   { groupKey: { type: 'document', subtype: 'invoice'  }, count: 2103 },
//   ...
// ]

Cortex compatibility

Subtype works under Cortex out of the box via auto-field-indexing. Cortex will land its own
native-side query-planner recognition + subtypeCountsByType native rollup in the next Cortex
release for full parity with type's fast path. Not a Brainy blocker β€” subtype reads/writes
function correctly with current Cortex.

Docs

Full guide: docs/guides/subtypes-and-facets.md. Subtype is documented as a core primitive
throughout README.md, docs/DATA_MODEL.md, docs/architecture/finite-type-system.md,
docs/api/README.md, and docs/QUERY_OPERATORS.md.