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.related({
|
|
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
|