fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
/**
|
2026-06-17 13:11:41 -07:00
|
|
|
* Integration test for metadata explosion fix
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
*
|
2026-06-17 13:11:41 -07:00
|
|
|
* Validates that vector embeddings (and array-index-as-object-key payloads) are
|
|
|
|
|
* NEVER indexed as metadata fields, while legitimate small arrays and scalar
|
|
|
|
|
* fields ARE registered for `where` filtering.
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
*
|
2026-06-17 13:11:41 -07:00
|
|
|
* Original bug class: 825,924 chunk files created for 1,144 entities (721 files
|
|
|
|
|
* per entity) because each of the 384 vector dimensions was being indexed as its
|
|
|
|
|
* own numeric metadata field.
|
|
|
|
|
*
|
|
|
|
|
* 8.0 architecture note: the per-vector-dimension chunk explosion is structurally
|
|
|
|
|
* impossible now — the metadata index is a column store + sparse field index
|
|
|
|
|
* (`_column_index/<field>/…` and `_system/idx/**`), not one `__chunk__*` file per
|
|
|
|
|
* field-value. So this suite no longer counts `_system/__chunk__*` files (there
|
|
|
|
|
* are none). Instead it asserts the real invariant directly via the public
|
|
|
|
|
* `getAvailableFields()` surface: no numeric (vector-dimension) field names, no
|
|
|
|
|
* `vector`/`embedding` field, but the legitimate semantic fields ARE indexed.
|
|
|
|
|
*
|
|
|
|
|
* Field placement (8.0 contract — see AddParams JSDoc): `data` is the content that
|
|
|
|
|
* gets embedded; `metadata` holds the structured, queryable fields used in `where`
|
|
|
|
|
* filters. Queryable fields therefore live under `metadata` here.
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest'
|
|
|
|
|
import { Brainy } from '../../src/brainy.js'
|
|
|
|
|
import { NounType } from '../../src/types/graphTypes.js'
|
2026-06-17 13:11:41 -07:00
|
|
|
import { existsSync, rmSync } from 'fs'
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
|
|
|
|
|
describe('Metadata Vector Exclusion Fix', () => {
|
|
|
|
|
let brainy: Brainy
|
|
|
|
|
const testDir = '/tmp/brainy-metadata-vector-test'
|
|
|
|
|
|
|
|
|
|
beforeEach(async () => {
|
|
|
|
|
// Clean test directory
|
|
|
|
|
if (existsSync(testDir)) {
|
|
|
|
|
rmSync(testDir, { recursive: true, force: true })
|
|
|
|
|
}
|
|
|
|
|
|
feat(8.0)!: flip requireSubtype default to true (BRAINY-8.0-SUBTYPE-CONTRACT § C-1)
Brainy 8.0 makes subtype required by default on every public write path
(`add`, `addMany`, `update`, `relate`, `relateMany`, `updateRelation`,
import). Per the locked C-1 contract, every entity and relation gets a
non-empty subtype string by the time the storage layer sees it.
OPT-OUT REMAINS FULLY SUPPORTED
The runtime flag is still consumer-controlled. Three opt-out paths
cover migration / legacy fixtures / typed escape:
- `new Brainy({ requireSubtype: false })` — last-resort: turn off the
contract entirely. Recommended only for migration windows or test
fixtures that legitimately can't supply a subtype.
- `new Brainy({ requireSubtype: { except: [NounType.Thing, ...] } })` —
per-type allowlist: strict everywhere except the listed types.
- `brain.requireSubtype(type, options)` — per-type registration with
optional vocabulary. Composes with the brain-wide flag.
Default is now `true`. Opt-out is explicit and documented; nothing
silently degrades.
TEST SWEEP
Bulk-applied `requireSubtype: false` to every `new Brainy({...})` call
site across 120 test files. Three sed patterns covered the shapes:
- `new Brainy({` → `new Brainy({ requireSubtype: false,`
- `new Brainy<T>({` → `new Brainy<T>({ requireSubtype: false,`
- `new Brainy()` → `new Brainy({ requireSubtype: false })`
tests/helpers/test-factory.ts → createTestConfig() defaults
`requireSubtype: false` so test files using the helper inherit the
opt-out without per-site edits.
The test sites that DO exercise subtype semantics (the
subtype-and-facets suite, the strict-mode-self-test suite, the verb-
subtype-and-enforcement suite, etc.) already pass real subtypes — they
were the 7.30.x acceptance tests for this contract. Those tests
continue to pass unchanged.
CHANGES
src/brainy.ts
- normalizeConfig() — `requireSubtype` default `false` → `true`.
Comment refreshed to document the three opt-out paths.
tests/* (120 files)
- Bulk-edited brain construction sites. No functional test changes; the
opt-out preserves the test author's original intent.
tests/helpers/test-factory.ts
- createTestConfig() base config gains `requireSubtype: false`.
NO-OP for consumers who were already passing subtype on every write.
For consumers who weren't, the upgrade path is one of the three opt-out
forms above. Migration recipe documented in 8.0 release notes (next
commit).
VERIFICATION
- npx tsc --noEmit: clean
- npm test: 1408 / 1409 (same pre-existing race-condition outstanding;
no other regressions from the flip)
2026-06-09 14:58:25 -07:00
|
|
|
brainy = new Brainy({ requireSubtype: false,
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
storage: { type: 'filesystem', path: testDir },
|
|
|
|
|
ai: { provider: 'mock' }
|
|
|
|
|
})
|
|
|
|
|
await brainy.init()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
afterEach(async () => {
|
|
|
|
|
if (brainy) {
|
|
|
|
|
await brainy.clear()
|
|
|
|
|
await brainy.close()
|
|
|
|
|
}
|
|
|
|
|
if (existsSync(testDir)) {
|
|
|
|
|
rmSync(testDir, { recursive: true, force: true })
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
it('should NOT index vector embeddings as metadata fields', async () => {
|
|
|
|
|
// Add entity with queryable metadata + a small array (tags SHOULD be indexed)
|
|
|
|
|
await brainy.add({
|
|
|
|
|
type: NounType.Person,
|
|
|
|
|
data: 'Alice the developer',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Alice',
|
|
|
|
|
email: 'alice@example.com',
|
|
|
|
|
tags: ['developer', 'typescript'] // Small array - SHOULD be indexed
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// The indexed-field set is the canonical record of what the metadata index
|
|
|
|
|
// actually tracks. The vector explosion bug manifested as hundreds of numeric
|
|
|
|
|
// field names (one per dimension) appearing here.
|
|
|
|
|
const fields = await brainy.getAvailableFields()
|
|
|
|
|
|
|
|
|
|
// CRITICAL: no purely-numeric field names (vector dimension indices like
|
|
|
|
|
// "0", "1", "54716") and no nested numeric dimension keys.
|
|
|
|
|
const numericFields = fields.filter(f => /(^|\.)\d+$/.test(f))
|
|
|
|
|
expect(numericFields).toEqual([])
|
|
|
|
|
|
|
|
|
|
// CRITICAL: the raw vector must never be indexed as a metadata field.
|
|
|
|
|
expect(fields).not.toContain('vector')
|
|
|
|
|
expect(fields).not.toContain('embedding')
|
|
|
|
|
expect(fields).not.toContain('embeddings')
|
|
|
|
|
|
|
|
|
|
// The legitimate semantic fields ARE indexed.
|
|
|
|
|
expect(fields).toContain('name')
|
|
|
|
|
expect(fields).toContain('email')
|
|
|
|
|
expect(fields).toContain('tags')
|
|
|
|
|
|
|
|
|
|
// Field count stays sane (semantic fields + Brainy's own system/VFS fields),
|
|
|
|
|
// nowhere near the hundreds a per-dimension explosion would produce.
|
|
|
|
|
expect(fields.length).toBeLessThan(50)
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
it('should NOT index objects with numeric keys (array-as-object guard)', async () => {
|
|
|
|
|
// Add entity with an object that has numeric keys (simulates a vector
|
|
|
|
|
// accidentally serialized as { "0": 0.1, "1": 0.2, ... }).
|
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
|
|
|
await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Person,
|
|
|
|
|
data: 'NumericTest entity',
|
|
|
|
|
metadata: {
|
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
|
|
|
name: 'NumericTest',
|
|
|
|
|
numericObject: {
|
|
|
|
|
'0': 0.1,
|
|
|
|
|
'1': 0.2,
|
|
|
|
|
'2': 0.3,
|
|
|
|
|
'100': 1.0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
const fields = await brainy.getAvailableFields()
|
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// CRITICAL: numeric keys (whether top-level or nested under a field like
|
|
|
|
|
// "numericObject.0") must NOT become indexed fields. This is the guard that
|
|
|
|
|
// prevents vectors-as-objects from exploding the index.
|
|
|
|
|
const numericFields = fields.filter(f => /(^|\.)\d+$/.test(f))
|
|
|
|
|
expect(numericFields).toEqual([])
|
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// The legitimate scalar field IS indexed.
|
|
|
|
|
expect(fields).toContain('name')
|
fix: v3.50.2 emergency hotfix - exclude numeric field names from metadata indexing
Critical fix for incomplete v3.50.1 release.
Problem: v3.50.1 prevented vector fields by name ('vector', 'embedding')
but missed vectors stored as objects with numeric keys: {0: 0.1, 1: 0.2, ...}
Studio team diagnostics showed:
- 212,531 chunk files with NUMERIC field names
- Examples: "field": "54716", "field": "100000", "field": "100001"
- 424,837 total files (expected ~1,200)
Root Cause: Vectors converted to objects with numeric keys were still
being indexed because field name check only caught semantic names.
Fix Applied (src/utils/metadataIndex.ts:1106):
- Added regex check: if (/^\d+$/.test(key)) continue
- Skips ANY purely numeric field name (array indices as object keys)
- Catches: "0", "1", "2", "100", "54716", "100000", etc.
Test Coverage:
- Added new test: "should NOT index objects with numeric keys (v3.50.2 fix)"
- Verifies NO chunk files have numeric field names
- All 8 integration tests passing
Impact:
- Prevents 212K+ chunk files from being created
- Reduces file count from 424K to ~1,200 (354x reduction)
- Fixes server hangs during initialization
- Completes the metadata explosion fix started in v3.50.1
2025-10-16 16:31:06 -07:00
|
|
|
})
|
|
|
|
|
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
it('should still index small arrays (tags, categories)', async () => {
|
2026-06-17 13:11:41 -07:00
|
|
|
// Add entity with a small (<= 10 element) array of tags.
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Person,
|
|
|
|
|
data: 'Bob the engineer',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Bob',
|
|
|
|
|
tags: ['javascript', 'react', 'nodejs']
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// Small arrays must be registered as a multi-value field (not skipped, not
|
|
|
|
|
// exploded). 'tags' should appear in the indexed-field set.
|
|
|
|
|
const fields = await brainy.getAvailableFields()
|
|
|
|
|
expect(fields).toContain('tags')
|
|
|
|
|
|
|
|
|
|
// Behavioral check: filtering by a tag value should return the entity. The
|
|
|
|
|
// array was ['javascript', 'react', 'nodejs'] — matching ANY member must work.
|
|
|
|
|
//
|
|
|
|
|
// REAL LIBRARY BUG (left failing on purpose): the ColumnStore ingest path in
|
|
|
|
|
// src/utils/metadataIndex.ts (addToIndex, ~line 1407) does
|
|
|
|
|
// `fieldsMap[field] = value` for every extracted pair, so for a multi-value
|
|
|
|
|
// field it keeps only the LAST array element ('nodejs') and drops the rest.
|
|
|
|
|
// Only the '__words__' field gets the array-accumulation treatment. As a
|
|
|
|
|
// result `where: { tags: 'react' }` (and 'javascript') returns [], while
|
|
|
|
|
// `where: { tags: 'nodejs' }` returns 1. Correct input → wrong output, so
|
|
|
|
|
// this assertion stays as-is to keep the bug visible.
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
const results = await brainy.find({
|
|
|
|
|
where: { tags: 'react' }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(results.length).toBeGreaterThan(0)
|
|
|
|
|
expect(results[0].entity.metadata?.name).toBe('Bob')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should skip indexing large arrays (>10 elements)', async () => {
|
2026-06-17 13:11:41 -07:00
|
|
|
// Add entity with a large array (not a vector, just bulk data).
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
const largeArray = Array.from({ length: 100 }, (_, i) => `item${i}`)
|
|
|
|
|
|
|
|
|
|
await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Document,
|
|
|
|
|
data: 'Doc with large array',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Doc with large array',
|
|
|
|
|
items: largeArray
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// Large arrays (> 10 elements) are deliberately skipped to avoid indexing
|
|
|
|
|
// bulk/vector-like payloads: 'items' must NOT appear, and the 100 elements
|
|
|
|
|
// must NOT have produced 100 indexed fields.
|
|
|
|
|
const fields = await brainy.getAvailableFields()
|
|
|
|
|
expect(fields).not.toContain('items')
|
|
|
|
|
const numericFields = fields.filter(f => /(^|\.)\d+$/.test(f))
|
|
|
|
|
expect(numericFields).toEqual([])
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// The scalar 'name' field IS indexed.
|
|
|
|
|
expect(fields).toContain('name')
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should preserve HNSW vector search functionality', async () => {
|
|
|
|
|
// Add entities with semantic content
|
|
|
|
|
const id1 = await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Concept,
|
|
|
|
|
data: 'AI algorithms that learn from data',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Machine Learning',
|
|
|
|
|
description: 'AI algorithms that learn from data'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const id2 = await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Concept,
|
|
|
|
|
data: 'Neural networks with multiple layers',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Deep Learning',
|
|
|
|
|
description: 'Neural networks with multiple layers'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// Vectors are metadata-only by default; request them explicitly to verify the
|
|
|
|
|
// embedding pipeline actually produced a stored vector for each entity.
|
|
|
|
|
const entity1 = await brainy.get(id1, { includeVectors: true })
|
|
|
|
|
const entity2 = await brainy.get(id2, { includeVectors: true })
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
|
|
|
|
|
expect(entity1).toBeDefined()
|
|
|
|
|
expect(entity2).toBeDefined()
|
2026-06-17 13:11:41 -07:00
|
|
|
expect(Array.isArray(entity1?.vector)).toBe(true)
|
|
|
|
|
expect(Array.isArray(entity2?.vector)).toBe(true)
|
|
|
|
|
expect(entity1!.vector!.length).toBe(384)
|
|
|
|
|
expect(entity2!.vector!.length).toBe(384)
|
|
|
|
|
|
|
|
|
|
// Self-retrieval confirms the HNSW index is wired: querying an entity by its
|
|
|
|
|
// OWN content text returns it (deterministic embedder → cosine 1.0).
|
|
|
|
|
const selfHits = await brainy.find({ query: 'AI algorithms that learn from data', limit: 5 })
|
|
|
|
|
expect(selfHits.length).toBeGreaterThan(0)
|
|
|
|
|
expect(selfHits.some(r => r.id === id1)).toBe(true)
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should preserve metadata field filtering', async () => {
|
2026-06-17 13:11:41 -07:00
|
|
|
// Add entities with various queryable metadata
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Person,
|
|
|
|
|
data: 'Charlie the engineer',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Charlie',
|
|
|
|
|
email: 'charlie@example.com',
|
|
|
|
|
role: 'engineer'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Person,
|
|
|
|
|
data: 'Dana the designer',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Dana',
|
|
|
|
|
email: 'dana@example.com',
|
|
|
|
|
role: 'designer'
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// Verify scalar metadata filtering works
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
const engineers = await brainy.find({
|
|
|
|
|
where: { role: 'engineer' }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(engineers.length).toBe(1)
|
|
|
|
|
expect(engineers[0].entity.metadata?.name).toBe('Charlie')
|
|
|
|
|
|
|
|
|
|
const designers = await brainy.find({
|
|
|
|
|
where: { role: 'designer' }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(designers.length).toBe(1)
|
|
|
|
|
expect(designers[0].entity.metadata?.name).toBe('Dana')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('should handle nested object metadata correctly', async () => {
|
|
|
|
|
// Add entity with nested metadata
|
|
|
|
|
await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Person,
|
|
|
|
|
data: 'Eve in New York',
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: 'Eve',
|
|
|
|
|
address: {
|
|
|
|
|
city: 'New York',
|
|
|
|
|
state: 'NY'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// Nested fields are flattened to dot-notation field names; filter by the
|
|
|
|
|
// flattened path.
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
const results = await brainy.find({
|
|
|
|
|
where: { 'address.city': 'New York' }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(results.length).toBeGreaterThan(0)
|
|
|
|
|
expect(results[0].entity.metadata?.name).toBe('Eve')
|
|
|
|
|
})
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
it('should NOT create exponential index fields for multiple entities', async () => {
|
|
|
|
|
// Add 10 entities (each with a vector embedding + a few metadata fields)
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
for (let i = 0; i < 10; i++) {
|
|
|
|
|
await brainy.add({
|
2026-06-17 13:11:41 -07:00
|
|
|
type: NounType.Person,
|
|
|
|
|
data: `Person ${i}`,
|
|
|
|
|
metadata: {
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
name: `Person ${i}`,
|
|
|
|
|
email: `person${i}@example.com`,
|
|
|
|
|
tags: ['user']
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-17 13:11:41 -07:00
|
|
|
// The indexed-field set is keyed by field NAME, not by entity — so it must
|
|
|
|
|
// stay bounded regardless of entity count. The original bug turned this into
|
|
|
|
|
// ~721 fields per entity (one per vector dimension); here it must remain a
|
|
|
|
|
// small handful of semantic fields plus Brainy's own system/VFS fields.
|
|
|
|
|
const fields = await brainy.getAvailableFields()
|
|
|
|
|
const numericFields = fields.filter(f => /(^|\.)\d+$/.test(f))
|
|
|
|
|
expect(numericFields).toEqual([])
|
|
|
|
|
expect(fields).toContain('name')
|
|
|
|
|
expect(fields).toContain('email')
|
|
|
|
|
expect(fields).toContain('tags')
|
|
|
|
|
// Nowhere near the thousands a per-dimension explosion would produce.
|
|
|
|
|
expect(fields.length).toBeLessThan(50)
|
fix: metadata explosion bug - 69K files reduced to ~1K
Critical fix for metadata indexing that was creating 60+ chunk files per entity.
Root cause: Vector embeddings (384-dimensional arrays) were being indexed in
metadata, causing each dimension to create a separate chunk file with numeric
field names ("0", "1", "2", etc.).
Changes:
- Modified extractIndexableFields() to exclude vector/embedding fields
- Added NEVER_INDEX set: ['vector', 'embedding', 'embeddings', 'connections']
- Added safety check to skip arrays > 10 elements
- Preserves small array indexing (tags, categories, roles)
Impact:
- Reduces metadata files from 69,429 → ~1,200 (58x reduction)
- Fixes server initialization hangs
- Fixes metadata batch loading stalling at batch 23
- Fixes VFS getDescendants() hanging with large datasets
- Fixes Graph View UI not loading
Test Results:
- 7/7 integration tests passing
- Verified: 6 chunk files for 10 entities (was 7,210 before fix)
- 611/622 unit tests passing
Files Modified:
- src/utils/metadataIndex.ts - Core fix
- src/coreTypes.ts - HNSWVerb type enforcement with VerbType enum
- src/storage/adapters/* - Include core relational fields in HNSWVerb
- src/storage/adapters/baseStorageAdapter.ts - Type enforcement (HNSWNoun, GraphVerb)
- tests/integration/metadata-vector-exclusion.test.ts - Comprehensive test coverage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 16:10:31 -07:00
|
|
|
})
|
|
|
|
|
})
|