Brings verbs to first-class parity with nouns. The 7.29.0 subtype primitive
shipped for entities only; this release ships the symmetric verb mirror plus
the enforcement layer for ensuring every entity AND every relationship has
both type AND subtype.
Layer V1 — verb subtype mirror
- HNSWVerbWithMetadata.subtype + STANDARD_VERB_FIELDS set + resolveVerbField()
- Relation<T>.subtype, RelateParams<T>.subtype, UpdateRelationParams<T> extended,
GetRelationsParams.subtype, GraphConstraints.subtype (for find connected)
- relate() persists subtype on verbMetadata + GraphVerb + transaction ops
- getRelations({ type, subtype }) fast-path filter with set membership
- find({ connected: { via, subtype, depth } }) traversal filter (depth-1 on
the JS path; explicit error on depth > 1 pointing at Cortex native)
- verbsToRelations + storage destructure sites surface subtype to top-level
- All three graph-index fast-path queries (getVerbsBySource/ByTarget) enrich
with subtype from metadata
Layer V2 — updateRelation() closes a pre-7.30 gap
- New first-class verb update method (parallel to update() for nouns)
- Changes subtype/type/weight/confidence/data/metadata in place
- Re-indexes in graph adjacency when verb type changes; id preserved
- validateUpdateRelationParams enforces id + at-least-one-field-to-update
Layer V3 — verb subtype storage rollup
- verbSubtypeCountsByType: Map<number, Map<string, number>> on BaseStorage
- verbSubtypeByIdCache for self-heal during update/delete
- incrementVerbSubtypeCount + decrementVerbSubtypeCount maintain state
- loadVerbSubtypeStatistics + saveVerbSubtypeStatistics persist to
_system/verb-subtype-statistics.json (mirrors noun-side shape)
- rebuildVerbSubtypeCounts for poison recovery / explicit repair
- getVerbSubtypeCountsByType accessor for the public counts API
- Wired into init() / flushCounts() / saveVerbMetadata / deleteVerbMetadata
Layer V4 — verb counts API + relationshipSubtypesOf
- brain.counts.byRelationshipSubtype(verb, subtype?) — O(1) breakdown or point
- brain.counts.topRelationshipSubtypes(verb, n) — top N by count
- brain.relationshipSubtypesOf(verb) — sorted distinct subtypes
Layer V5 — migrateField extended to verbs
- New entityKind?: 'noun' | 'verb' | 'both' option (default 'noun')
- Mirror verb iteration via storage.getVerbs() with same path semantics
- verbToRelationLike + buildRelationMigrationUpdate helpers project the
storage verb shape onto the Entity<T>-shaped surface readPath understands
- Routes through new updateRelation() for the verb-side rewrite
Enforcement (opt-in in 7.30, default in 8.0)
- brain.requireSubtype(type, options) — unified API for NounType OR VerbType.
Registers per-type rules with optional values whitelist; composes with the
brain-wide flag.
- new Brainy({ requireSubtype: true }) — brain-wide strict mode. Every public
write path validates the pairing guarantee.
- { except: [NounType.Thing, ...] } form for catch-all type exemptions
- Atomic-fail semantics on addMany / relateMany — pre-validate every item
before any storage write, throw on first failure with item index
- Per-type rules + brain-wide flag both throw with descriptive messages
- VFS infrastructure bypass via metadata.isVFSEntity / isVFS markers so
brain's own VFS writes don't get rejected when strict mode is on
VFS labeling — concrete subtypes for infrastructure entities
- VFS root: NounType.Collection + subtype: 'vfs-root' (was bare Collection)
- VFS directories: subtype: 'vfs-directory'
- VFS files: subtype: 'vfs-file' (NounType still mime-based)
- VFS containment edges: VerbType.Contains + subtype: 'vfs-contains'
- Lets consumers cleanly enumerate VFS state via find({ subtype: 'vfs-file' })
and distinguish Brainy's VFS Collections from user-created Collections
Docs
- docs/guides/subtypes-and-facets.md extended with Layer V (Verbs) section +
Enforcement section. New full reference at the bottom split into Layer 1
(nouns), Layer V (verbs), Layer 2 (facets), Layer 3 (migration), Enforcement.
- docs/api/README.md adds updateRelation(), getRelations({ subtype }), the
three verb-side counts methods, requireSubtype(), and the brain-wide
constructor option. relate() params include subtype.
- docs/DATA_MODEL.md adds a Subtype-for-VerbType section + STANDARD_VERB_FIELDS
- docs/architecture/finite-type-system.md extends Principle 1a to verbs
- docs/QUERY_OPERATORS.md adds a verb-subtype filter section covering
getRelations and find({connected, subtype}) traversal
- README.md "Subtypes" section now shows both noun + verb in one example +
the enforcement APIs
- RELEASES.md v7.30.0 entry with the full noun/verb capability parity matrix
Tests
- tests/integration/verb-subtype-and-enforcement.test.ts — 30 new tests
covering V1 round-trips, V1 set membership, updateRelation in place,
updateRelation preservation, V2 counts breakdown + point + topN + distinct,
V2 decrements on unrelate, V2 re-routes on updateRelation, V3 depth-1
traversal filter, V3 depth>1 explicit error, V4 verb migration, V4 both
entity kinds, V4 readBoth preservation, V5 per-type required rejection,
V5 vocabulary rejection, V5 on-vocab acceptance, V5 verb-side enforcement,
V5 addMany atomic-fail, V5 relateMany atomic-fail, V5 update enforcement,
V5 updateRelation enforcement, V5 brain-wide strict mode, V5 except clause.
Verification
- Unit suite: 1468/1468 passing
- Noun subtype integration (7.29 carryover): 26/26 passing
- Verb subtype + enforcement integration: 30/30 passing
- Type-check: clean
- Build: clean
- Public closed-source reference audit: clean
Internal 8.0 spec
- .strategy/BRAINY-8.0-SUBTYPE-CONTRACT.md (gitignored, not in npm artifact)
documents the contract upgrade Cortex 3.0 implements against: required-by-
default subtype, SubtypeRegistry typing hook, native simplification,
multi-hop traversal native fast path, brain.fillSubtypes() migration helper.
Coordinated via PLATFORM-HANDOFF rows CTX-SUBTYPE-PARITY-V2 (7.30 parallel
work) and CTX-SUBTYPE-8.0-CONTRACT (8.0 spec).
271 lines
10 KiB
Markdown
271 lines
10 KiB
Markdown
# Data Model
|
|
|
|
> How Brainy stores entities and relationships, and the critical distinction between `data` and `metadata`.
|
|
|
|
---
|
|
|
|
## Entity (Noun)
|
|
|
|
An entity is the fundamental data unit in Brainy. Every entity has:
|
|
|
|
| Field | Type | Indexed | Description |
|
|
|-------|------|---------|-------------|
|
|
| `id` | `string` | Primary key | UUID v4 (auto-generated or custom) |
|
|
| `data` | `any` | **HNSW vector index** | Content used for semantic/hybrid search. Strings auto-embed. |
|
|
| `metadata` | `object` | **MetadataIndex** | Structured queryable fields (tags, dates, flags, etc.) |
|
|
| `type` | `NounType` | MetadataIndex (as `noun`) | Entity type classification |
|
|
| `vector` | `number[]` | HNSW | 384-dim embedding (auto-computed from `data` or user-provided) |
|
|
| `confidence` | `number` | MetadataIndex | Type classification confidence (0-1) |
|
|
| `weight` | `number` | MetadataIndex | Entity importance/salience (0-1) |
|
|
| `service` | `string` | MetadataIndex | Multi-tenancy identifier |
|
|
| `createdAt` | `number` | MetadataIndex | Creation timestamp (ms since epoch) |
|
|
| `updatedAt` | `number` | MetadataIndex | Last update timestamp (ms since epoch) |
|
|
| `createdBy` | `object` | MetadataIndex | Source augmentation info |
|
|
|
|
### Example
|
|
|
|
```typescript
|
|
const id = await brain.add({
|
|
data: 'John Smith is a software engineer at Acme Corp', // → embedded into vector
|
|
type: NounType.Person,
|
|
metadata: { // → indexed, queryable via where filters
|
|
role: 'engineer',
|
|
department: 'backend',
|
|
yearsExperience: 8
|
|
},
|
|
confidence: 0.95,
|
|
weight: 0.7
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Relationship (Verb)
|
|
|
|
A relationship is a typed, directed edge connecting two entities.
|
|
|
|
| Field | Type | Indexed | Description |
|
|
|-------|------|---------|-------------|
|
|
| `id` | `string` | Primary key | UUID v4 (auto-generated) |
|
|
| `from` | `string` | **GraphAdjacencyIndex** | Source entity ID |
|
|
| `to` | `string` | **GraphAdjacencyIndex** | Target entity ID |
|
|
| `type` | `VerbType` | GraphAdjacencyIndex (as `verb`) | Relationship type classification |
|
|
| `data` | `any` | — | Opaque content (overrides auto-computed vector if provided) |
|
|
| `metadata` | `object` | — | Structured fields on the edge |
|
|
| `weight` | `number` | — | Connection strength (0-1, default: 1.0) |
|
|
| `confidence` | `number` | — | Relationship certainty (0-1) |
|
|
| `evidence` | `RelationEvidence` | — | Why this relationship was detected |
|
|
| `createdAt` | `number` | — | Creation timestamp (ms since epoch) |
|
|
| `updatedAt` | `number` | — | Last update timestamp (ms since epoch) |
|
|
| `service` | `string` | — | Multi-tenancy identifier |
|
|
|
|
### Example
|
|
|
|
```typescript
|
|
const relId = await brain.relate({
|
|
from: personId,
|
|
to: projectId,
|
|
type: VerbType.WorksOn,
|
|
data: 'Lead engineer on the AI module', // Optional: content for this edge
|
|
metadata: { // Optional: queryable edge fields
|
|
role: 'lead',
|
|
startDate: '2024-01-15'
|
|
},
|
|
weight: 0.9
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Data vs Metadata
|
|
|
|
This is the most important concept in Brainy's storage model:
|
|
|
|
### `data` — Content for Semantic Search
|
|
|
|
- Embedded into a 384-dimensional vector via the WASM embedding engine
|
|
- Searchable via **semantic similarity** (HNSW vector index) and **hybrid text+semantic** search
|
|
- Queried by passing `query` to `find()`:
|
|
```typescript
|
|
brain.find({ query: 'machine learning algorithms' })
|
|
```
|
|
- **NOT** indexed by MetadataIndex — you cannot use `where` filters on `data`
|
|
- Stored opaquely: strings, objects, numbers — anything goes
|
|
|
|
### `metadata` — Structured Queryable Fields
|
|
|
|
- Indexed by MetadataIndex with O(1) lookups per field
|
|
- Queryable via `where` filters using [BFO operators](./QUERY_OPERATORS.md):
|
|
```typescript
|
|
brain.find({
|
|
where: {
|
|
department: 'engineering',
|
|
yearsExperience: { greaterThan: 5 },
|
|
tags: { contains: 'senior' }
|
|
}
|
|
})
|
|
```
|
|
- **NOT** used for vector/semantic search
|
|
- Must be a flat or lightly nested object
|
|
|
|
### Quick Reference
|
|
|
|
| | `data` | `metadata` |
|
|
|---|---|---|
|
|
| **Purpose** | Content for embedding / semantic search | Structured fields for filtering |
|
|
| **Searched by** | `find({ query })` — vector similarity, hybrid text+semantic | `find({ where })` — exact, range, set operators |
|
|
| **Indexed by** | HNSW vector index | MetadataIndex |
|
|
| **Queryable with operators?** | No | Yes (`equals`, `greaterThan`, `oneOf`, etc.) |
|
|
| **Auto-embedded?** | Yes (strings → 384-dim vectors) | No |
|
|
| **Typical content** | Text descriptions, document content | Tags, dates, status flags, categories, numeric fields |
|
|
|
|
### Common Pattern
|
|
|
|
```typescript
|
|
// Add an article
|
|
await brain.add({
|
|
data: 'A deep dive into transformer architectures and attention mechanisms',
|
|
type: NounType.Document,
|
|
metadata: {
|
|
title: 'Transformer Deep Dive',
|
|
author: 'Dr. Chen',
|
|
publishedYear: 2024,
|
|
tags: ['AI', 'transformers', 'NLP'],
|
|
status: 'published'
|
|
}
|
|
})
|
|
|
|
// Search by content (semantic — searches data)
|
|
const results = await brain.find({ query: 'neural network attention' })
|
|
|
|
// Filter by fields (exact — queries metadata)
|
|
const recent = await brain.find({
|
|
where: {
|
|
publishedYear: { greaterThan: 2023 },
|
|
status: 'published'
|
|
}
|
|
})
|
|
|
|
// Combine both (Triple Intelligence)
|
|
const precise = await brain.find({
|
|
query: 'attention mechanisms', // Semantic search on data
|
|
where: { author: 'Dr. Chen' }, // Metadata filter
|
|
connected: { from: authorId, depth: 1 } // Graph traversal
|
|
})
|
|
```
|
|
|
|
---
|
|
|
|
## Storage Field Naming
|
|
|
|
Internally, Brainy uses different field names in storage vs the public API:
|
|
|
|
| Public API (Entity/Relation) | Storage (metadata object) | Notes |
|
|
|------------------------------|--------------------------|-------|
|
|
| `type` | `noun` | Entity type stored as `noun` |
|
|
| `from` | `sourceId` | Relationship source |
|
|
| `to` | `targetId` | Relationship target |
|
|
| `type` (on Relation) | `verb` | Relationship type stored as `verb` |
|
|
|
|
When querying with `find()`, you can use:
|
|
- `type` parameter (convenience alias, equivalent to `where.noun`)
|
|
- `where.noun` directly
|
|
|
|
```typescript
|
|
// These are equivalent:
|
|
brain.find({ type: NounType.Person })
|
|
brain.find({ where: { noun: NounType.Person } })
|
|
```
|
|
|
|
---
|
|
|
|
## Standard Metadata Fields
|
|
|
|
When you add an entity, Brainy stores these standard fields in the metadata object alongside your custom fields:
|
|
|
|
| Field | Set By | Description |
|
|
|-------|--------|-------------|
|
|
| `noun` | System | Entity type (NounType enum value) |
|
|
| `subtype` | User | Per-NounType sub-classification (e.g. `'employee'`, `'invoice'`, `'milestone'`). Flat string, no hierarchy. Indexed on the fast path and rolled into per-NounType statistics. |
|
|
| `data` | System | The raw `data` value (stored opaquely) |
|
|
| `createdAt` | System | Creation timestamp |
|
|
| `updatedAt` | System | Last update timestamp |
|
|
| `confidence` | User | Type classification confidence |
|
|
| `weight` | User | Entity importance |
|
|
| `service` | User | Multi-tenancy identifier |
|
|
| `createdBy` | User/System | Source augmentation |
|
|
|
|
On read, these standard fields are extracted to top-level Entity properties. The `metadata` field on the returned Entity contains **only your custom fields**.
|
|
|
|
### Subtype — sub-classification within a NounType
|
|
|
|
`type` (NounType) is a stable 42-value enum. `subtype` is the consumer-chosen string vocabulary *within* a type:
|
|
|
|
```typescript
|
|
// A Person who is an employee:
|
|
await brain.add({
|
|
data: 'Avery Brooks — runs the AI lab',
|
|
type: NounType.Person,
|
|
subtype: 'employee',
|
|
metadata: { department: 'ai-lab' }
|
|
})
|
|
|
|
// A Document that is an invoice:
|
|
await brain.add({
|
|
data: 'INV-2026-001',
|
|
type: NounType.Document,
|
|
subtype: 'invoice',
|
|
metadata: { amount: 1500 }
|
|
})
|
|
```
|
|
|
|
`subtype` lives at the **top level** — NOT inside `metadata`, NOT inside `data`. That's how `find({ type, subtype })` routes through the standard-field fast path (column-store hit) instead of the metadata fallback. See **[Subtypes & Facets](./guides/subtypes-and-facets.md)** for the full guide including `trackField()` and `migrateField()`.
|
|
|
|
### Subtype — sub-classification within a VerbType (7.30+)
|
|
|
|
Relationships are first-class citizens too. Every verb (`VerbType`) gets the same `subtype` primitive — a `ReportsTo` relationship might carry `subtype: 'direct'` vs `'dotted-line'`; a `RelatedTo` edge might carry `'spouse'` / `'sibling'` / `'colleague'`. Same shape as the noun side: flat string, no hierarchy, top-level standard field on `HNSWVerbWithMetadata` and on the public `Relation<T>`:
|
|
|
|
```typescript
|
|
await brain.relate({
|
|
from: ceoId,
|
|
to: vpId,
|
|
type: VerbType.ReportsTo,
|
|
subtype: 'direct', // top-level standard field
|
|
metadata: { since: '2025-Q1' } // user-custom fields stay in metadata
|
|
})
|
|
```
|
|
|
|
Fast-path filter on the verb side:
|
|
|
|
```typescript
|
|
const direct = await brain.getRelations({
|
|
from: ceoId,
|
|
type: VerbType.ReportsTo,
|
|
subtype: 'direct'
|
|
})
|
|
```
|
|
|
|
The verb-side rollup at `_system/verb-subtype-statistics.json` mirrors the noun-side `_system/subtype-statistics.json` — same shape, same self-heal machinery. Per-VerbType-per-subtype counts are O(1) via `brain.counts.byRelationshipSubtype()`.
|
|
|
|
Verbs and nouns now have full capability parity — every API on the noun side has a verb-side mirror, including the new `brain.updateRelation()` (which closed a pre-7.30 gap where relationships had no update path).
|
|
|
|
### Standard verb fields
|
|
|
|
The verb-side equivalent of `STANDARD_ENTITY_FIELDS` is `STANDARD_VERB_FIELDS`, exported from `src/coreTypes.ts`. Verb-specific standard fields:
|
|
|
|
| Field | Description |
|
|
|---|---|
|
|
| `verb` | The VerbType enum value |
|
|
| `sourceId` / `targetId` | The two endpoints of the relationship |
|
|
| `subtype` | Sub-classification within the VerbType (7.30+) |
|
|
| `confidence`, `weight`, `createdAt`, `updatedAt`, `service`, `createdBy`, `data` | Same semantics as the noun-side standard fields |
|
|
|
|
The companion `resolveVerbField(verb, field)` helper resolves field paths the same way `resolveEntityField` does for nouns: standard fields first, metadata fallback for everything else.
|
|
|
|
---
|
|
|
|
## See Also
|
|
|
|
- [API Reference](./api/README.md) — Complete API documentation
|
|
- [Query Operators](./QUERY_OPERATORS.md) — All BFO operators with examples
|
|
- [Find System](./FIND_SYSTEM.md) — Natural language find() details
|