feat: add neural extraction APIs with NounType taxonomy
Add brain.extract() and brain.extractConcepts() methods that use NeuralEntityExtractor with embeddings and sophisticated NounType taxonomy (30+ entity types) for semantic entity and concept extraction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
27cc699555
commit
dd50d89ad6
41 changed files with 3807 additions and 7391 deletions
727
docs/vfs/PROJECTION_STRATEGY_API.md
Normal file
727
docs/vfs/PROJECTION_STRATEGY_API.md
Normal file
|
|
@ -0,0 +1,727 @@
|
|||
# Projection Strategy API
|
||||
|
||||
## Creating Custom Semantic Dimensions
|
||||
|
||||
Projection strategies allow you to create custom ways to organize and access files in Semantic VFS. This guide shows you how to build your own.
|
||||
|
||||
---
|
||||
|
||||
## What is a Projection Strategy?
|
||||
|
||||
A **projection strategy** maps a semantic dimension (like "priority" or "language") to actual file entities using Brainy queries.
|
||||
|
||||
**Example:**
|
||||
```typescript
|
||||
/by-priority/high → Files with metadata.priority = 'high'
|
||||
/by-language/typescript → Files with .ts extension
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Interface Definition
|
||||
|
||||
Every projection must implement the `ProjectionStrategy` interface:
|
||||
|
||||
```typescript
|
||||
export interface ProjectionStrategy {
|
||||
/**
|
||||
* Unique name for this dimension
|
||||
* Used in paths like: /by-{name}/...
|
||||
*/
|
||||
readonly name: string
|
||||
|
||||
/**
|
||||
* Convert dimension value to Brainy FindParams
|
||||
* This is for documentation/debugging (not always used)
|
||||
*
|
||||
* @param value - The dimension value (e.g., 'high' for priority)
|
||||
* @param subpath - Optional file filter within dimension
|
||||
*/
|
||||
toQuery(value: any, subpath?: string): FindParams
|
||||
|
||||
/**
|
||||
* Resolve dimension value to entity IDs
|
||||
* This is the MAIN method that does the work
|
||||
*
|
||||
* @param brain - Brainy instance (use brain.find, brain.similar, etc.)
|
||||
* @param vfs - VirtualFileSystem instance
|
||||
* @param value - The dimension value to resolve
|
||||
* @returns Array of entity IDs matching this dimension
|
||||
*/
|
||||
resolve(brain: Brainy, vfs: VirtualFileSystem, value: any): Promise<string[]>
|
||||
|
||||
/**
|
||||
* OPTIONAL: List all items in this dimension
|
||||
* Used for directory listings like: readdir('/by-priority')
|
||||
*
|
||||
* @param brain - Brainy instance
|
||||
* @param vfs - VirtualFileSystem instance
|
||||
* @param limit - Max results to return
|
||||
*/
|
||||
list?(brain: Brainy, vfs: VirtualFileSystem, limit?: number): Promise<VFSEntity[]>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start: Priority Projection
|
||||
|
||||
Let's build a projection that organizes files by priority (high, medium, low):
|
||||
|
||||
### Step 1: Create the Strategy Class
|
||||
|
||||
```typescript
|
||||
import { BaseProjectionStrategy } from '@soulcraft/brainy/vfs/semantic'
|
||||
import { Brainy } from '@soulcraft/brainy'
|
||||
import { VirtualFileSystem, VFSEntity } from '@soulcraft/brainy/vfs'
|
||||
|
||||
export class PriorityProjection extends BaseProjectionStrategy {
|
||||
readonly name = 'priority'
|
||||
|
||||
/**
|
||||
* Convert priority value to FindParams
|
||||
*/
|
||||
toQuery(priority: string, subpath?: string) {
|
||||
const query = {
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
priority: priority // Match metadata.priority field
|
||||
},
|
||||
limit: 1000
|
||||
}
|
||||
|
||||
// Filter by filename if subpath provided
|
||||
if (subpath) {
|
||||
query.where = {
|
||||
...query.where,
|
||||
anyOf: [
|
||||
{ name: subpath },
|
||||
{ path: { endsWith: subpath } }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return query
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve priority to entity IDs
|
||||
*/
|
||||
async resolve(brain: Brainy, vfs: VirtualFileSystem, priority: string): Promise<string[]> {
|
||||
// Query Brainy for files with this priority
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
priority: priority
|
||||
},
|
||||
limit: 1000
|
||||
})
|
||||
|
||||
// Extract entity IDs using helper from base class
|
||||
return this.extractIds(results)
|
||||
}
|
||||
|
||||
/**
|
||||
* List all files that have priority metadata
|
||||
*/
|
||||
async list(brain: Brainy, vfs: VirtualFileSystem, limit = 100): Promise<VFSEntity[]> {
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
priority: { exists: true }
|
||||
},
|
||||
limit
|
||||
})
|
||||
|
||||
return results.map(r => r.entity as VFSEntity)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Register the Strategy
|
||||
|
||||
```typescript
|
||||
import { Brainy } from '@soulcraft/brainy'
|
||||
import { PriorityProjection } from './PriorityProjection'
|
||||
|
||||
const brain = new Brainy()
|
||||
await brain.init()
|
||||
|
||||
const vfs = brain.vfs()
|
||||
await vfs.init()
|
||||
|
||||
// Register custom projection
|
||||
// TODO: This will be exposed as public API
|
||||
// For now, access via internal property
|
||||
vfs['projectionRegistry'].register(new PriorityProjection())
|
||||
```
|
||||
|
||||
### Step 3: Use It!
|
||||
|
||||
```typescript
|
||||
// Write files with priority metadata
|
||||
await vfs.writeFile('/src/critical-fix.ts', code, {
|
||||
metadata: { priority: 'high' }
|
||||
})
|
||||
|
||||
await vfs.writeFile('/src/nice-to-have.ts', code, {
|
||||
metadata: { priority: 'low' }
|
||||
})
|
||||
|
||||
// Access by priority
|
||||
const highPriority = await vfs.readdir('/by-priority/high')
|
||||
console.log(highPriority) // ['critical-fix.ts']
|
||||
|
||||
const lowPriority = await vfs.readdir('/by-priority/low')
|
||||
console.log(lowPriority) // ['nice-to-have.ts']
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Base Class Helpers
|
||||
|
||||
`BaseProjectionStrategy` provides utility methods:
|
||||
|
||||
### `extractIds(results: Result[]): string[]`
|
||||
Extracts entity IDs from Brainy query results:
|
||||
|
||||
```typescript
|
||||
const results = await brain.find({ where: { ... } })
|
||||
return this.extractIds(results) // ['id1', 'id2', ...]
|
||||
```
|
||||
|
||||
### `filterFiles(brain: Brainy, ids: string[]): Promise<string[]>`
|
||||
Filters to only file entities (removes directories):
|
||||
|
||||
```typescript
|
||||
const allIds = await this.traverseGraph(...)
|
||||
return await this.filterFiles(brain, allIds) // Only files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Examples
|
||||
|
||||
### Example 1: Language Projection
|
||||
|
||||
Organize files by programming language:
|
||||
|
||||
```typescript
|
||||
export class LanguageProjection extends BaseProjectionStrategy {
|
||||
readonly name = 'language'
|
||||
|
||||
// Map extensions to languages
|
||||
private languageMap = {
|
||||
ts: 'typescript',
|
||||
js: 'javascript',
|
||||
py: 'python',
|
||||
go: 'go',
|
||||
rs: 'rust'
|
||||
}
|
||||
|
||||
toQuery(language: string, subpath?: string) {
|
||||
// Find extension for this language
|
||||
const ext = Object.entries(this.languageMap)
|
||||
.find(([_, lang]) => lang === language)?.[0]
|
||||
|
||||
return {
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
extension: ext
|
||||
},
|
||||
limit: 1000
|
||||
}
|
||||
}
|
||||
|
||||
async resolve(brain: Brainy, vfs: VirtualFileSystem, language: string): Promise<string[]> {
|
||||
const ext = Object.entries(this.languageMap)
|
||||
.find(([_, lang]) => lang === language)?.[0]
|
||||
|
||||
if (!ext) return []
|
||||
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
extension: ext
|
||||
},
|
||||
limit: 5000
|
||||
})
|
||||
|
||||
return this.extractIds(results)
|
||||
}
|
||||
|
||||
async list(brain: Brainy, vfs: VirtualFileSystem, limit = 100): Promise<VFSEntity[]> {
|
||||
// Return sample files from each language
|
||||
const results = await brain.find({
|
||||
where: { vfsType: 'file' },
|
||||
limit
|
||||
})
|
||||
|
||||
return results.map(r => r.entity as VFSEntity)
|
||||
}
|
||||
}
|
||||
|
||||
// Usage:
|
||||
// /by-language/typescript → All .ts files
|
||||
// /by-language/python → All .py files
|
||||
```
|
||||
|
||||
### Example 2: Size Projection
|
||||
|
||||
Organize files by size category:
|
||||
|
||||
```typescript
|
||||
export class SizeProjection extends BaseProjectionStrategy {
|
||||
readonly name = 'size'
|
||||
|
||||
// Size categories in bytes
|
||||
private readonly categories = {
|
||||
tiny: [0, 1024], // < 1 KB
|
||||
small: [1024, 102400], // 1-100 KB
|
||||
medium: [102400, 1048576], // 100 KB - 1 MB
|
||||
large: [1048576, Infinity] // > 1 MB
|
||||
}
|
||||
|
||||
toQuery(category: string, subpath?: string) {
|
||||
const [min, max] = this.categories[category] || [0, Infinity]
|
||||
|
||||
return {
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
size: {
|
||||
greaterEqual: min,
|
||||
lessThan: max
|
||||
}
|
||||
},
|
||||
limit: 1000
|
||||
}
|
||||
}
|
||||
|
||||
async resolve(brain: Brainy, vfs: VirtualFileSystem, category: string): Promise<string[]> {
|
||||
const [min, max] = this.categories[category]
|
||||
if (!min && min !== 0) return []
|
||||
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
size: {
|
||||
greaterEqual: min,
|
||||
lessThan: max
|
||||
}
|
||||
},
|
||||
limit: 1000
|
||||
})
|
||||
|
||||
return this.extractIds(results)
|
||||
}
|
||||
|
||||
async list(brain: Brainy, vfs: VirtualFileSystem, limit = 100): Promise<VFSEntity[]> {
|
||||
// Return files sorted by size
|
||||
const results = await brain.find({
|
||||
where: { vfsType: 'file' },
|
||||
limit
|
||||
})
|
||||
|
||||
return results
|
||||
.map(r => r.entity as VFSEntity)
|
||||
.sort((a, b) => (b.metadata.size || 0) - (a.metadata.size || 0))
|
||||
}
|
||||
}
|
||||
|
||||
// Usage:
|
||||
// /by-size/tiny → Files < 1 KB
|
||||
// /by-size/large → Files > 1 MB
|
||||
```
|
||||
|
||||
### Example 3: Status Projection (Custom Logic)
|
||||
|
||||
Organize files by review status with custom logic:
|
||||
|
||||
```typescript
|
||||
export class StatusProjection extends BaseProjectionStrategy {
|
||||
readonly name = 'status'
|
||||
|
||||
toQuery(status: string, subpath?: string) {
|
||||
return {
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
reviewStatus: status
|
||||
},
|
||||
limit: 1000
|
||||
}
|
||||
}
|
||||
|
||||
async resolve(brain: Brainy, vfs: VirtualFileSystem, status: string): Promise<string[]> {
|
||||
// Custom logic: "needs-review" means modified in last 24h without review
|
||||
if (status === 'needs-review') {
|
||||
const oneDayAgo = Date.now() - (24 * 60 * 60 * 1000)
|
||||
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
modified: { greaterEqual: oneDayAgo },
|
||||
reviewStatus: { missing: true } // No review status set
|
||||
},
|
||||
limit: 1000
|
||||
})
|
||||
|
||||
return this.extractIds(results)
|
||||
}
|
||||
|
||||
// Standard status query
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
reviewStatus: status
|
||||
},
|
||||
limit: 1000
|
||||
})
|
||||
|
||||
return this.extractIds(results)
|
||||
}
|
||||
|
||||
async list(brain: Brainy, vfs: VirtualFileSystem, limit = 100): Promise<VFSEntity[]> {
|
||||
// Return files with any review status
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
vfsType: 'file',
|
||||
anyOf: [
|
||||
{ reviewStatus: { exists: true } },
|
||||
{ modified: { greaterEqual: Date.now() - 86400000 } }
|
||||
]
|
||||
},
|
||||
limit
|
||||
})
|
||||
|
||||
return results.map(r => r.entity as VFSEntity)
|
||||
}
|
||||
}
|
||||
|
||||
// Usage:
|
||||
// /by-status/needs-review → Files modified in last 24h without review
|
||||
// /by-status/approved → Approved files
|
||||
// /by-status/rejected → Rejected files
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Using Brainy Field Operators (BFO)
|
||||
|
||||
Projection strategies use **Brainy Field Operators** (BFO), not MongoDB-style operators:
|
||||
|
||||
### Comparison Operators
|
||||
```typescript
|
||||
// ❌ MongoDB style (WRONG)
|
||||
{ size: { $gte: 1000, $lte: 5000 } }
|
||||
|
||||
// ✅ BFO style (CORRECT)
|
||||
{ size: { greaterEqual: 1000, lessEqual: 5000 } }
|
||||
```
|
||||
|
||||
### Logical Operators
|
||||
```typescript
|
||||
// ❌ MongoDB style (WRONG)
|
||||
{ $or: [{ name: 'foo' }, { name: 'bar' }] }
|
||||
|
||||
// ✅ BFO style (CORRECT)
|
||||
{ anyOf: [{ name: 'foo' }, { name: 'bar' }] }
|
||||
```
|
||||
|
||||
### Existence Operators
|
||||
```typescript
|
||||
// ❌ MongoDB style (WRONG)
|
||||
{ tags: { $exists: true } }
|
||||
|
||||
// ✅ BFO style (CORRECT)
|
||||
{ tags: { exists: true } }
|
||||
```
|
||||
|
||||
### String Operators
|
||||
```typescript
|
||||
// ❌ MongoDB style (WRONG)
|
||||
{ path: { $regex: /\.ts$/ } }
|
||||
|
||||
// ✅ BFO style (CORRECT)
|
||||
{ path: { endsWith: '.ts' } }
|
||||
```
|
||||
|
||||
### Full BFO Operator Reference
|
||||
|
||||
```typescript
|
||||
// Comparison
|
||||
{ field: value } // Exact match
|
||||
{ field: { greaterThan: 10 } } // >
|
||||
{ field: { greaterEqual: 10 } } // >=
|
||||
{ field: { lessThan: 10 } } // <
|
||||
{ field: { lessEqual: 10 } } // <=
|
||||
{ field: { not: value } } // !=
|
||||
|
||||
// Logical
|
||||
{ anyOf: [{ a: 1 }, { b: 2 }] } // OR
|
||||
{ allOf: [{ a: 1 }, { b: 2 }] } // AND
|
||||
|
||||
// Existence
|
||||
{ field: { exists: true } } // Field exists
|
||||
{ field: { missing: true } } // Field doesn't exist
|
||||
|
||||
// String
|
||||
{ field: { startsWith: 'prefix' } } // Starts with
|
||||
{ field: { endsWith: 'suffix' } } // Ends with
|
||||
{ field: { matches: 'pattern' } } // Regex match
|
||||
|
||||
// Array
|
||||
{ array: { contains: 'item' } } // Array contains item
|
||||
{ array: { hasAll: ['a', 'b'] } } // Has all items
|
||||
{ array: { oneOf: ['a', 'b', 'c'] } } // Value in list
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Guidelines
|
||||
|
||||
### 1. Use Indexes
|
||||
All metadata fields are automatically indexed. Use direct equality or range queries for best performance:
|
||||
|
||||
```typescript
|
||||
// ✅ Fast: Direct index lookup (O(log n))
|
||||
{ priority: 'high' }
|
||||
{ size: { greaterEqual: 1000 } }
|
||||
|
||||
// ⚠️ Slower: Must scan results
|
||||
{ path: { matches: /complex-regex/ } }
|
||||
```
|
||||
|
||||
### 2. Limit Results
|
||||
Always set reasonable limits:
|
||||
|
||||
```typescript
|
||||
async resolve(brain, vfs, value) {
|
||||
const results = await brain.find({
|
||||
where: { ... },
|
||||
limit: 1000 // Prevent unbounded queries
|
||||
})
|
||||
return this.extractIds(results)
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Avoid Post-Filtering When Possible
|
||||
If you need post-filtering, consider flattening data:
|
||||
|
||||
```typescript
|
||||
// ❌ Slow: Fetch 5000, filter in memory
|
||||
const all = await brain.find({ where: { type: 'file' }, limit: 5000 })
|
||||
return all.filter(item => item.metadata.nested.value === target)
|
||||
|
||||
// ✅ Fast: Flatten during write, query directly
|
||||
// Store: metadata.nested_value = target
|
||||
const results = await brain.find({
|
||||
where: { nested_value: target },
|
||||
limit: 1000
|
||||
})
|
||||
```
|
||||
|
||||
### 4. Cache Expensive Operations
|
||||
Use the projection's resolve cache:
|
||||
|
||||
```typescript
|
||||
// Automatic caching in SemanticPathResolver
|
||||
// Results cached for 5 minutes by default
|
||||
// No manual caching needed!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Projections
|
||||
|
||||
### Unit Test Example
|
||||
|
||||
```typescript
|
||||
import { describe, it, expect, beforeAll } from 'vitest'
|
||||
import { Brainy } from '@soulcraft/brainy'
|
||||
import { PriorityProjection } from './PriorityProjection'
|
||||
|
||||
describe('PriorityProjection', () => {
|
||||
let brain: Brainy
|
||||
let vfs: any
|
||||
let projection: PriorityProjection
|
||||
|
||||
beforeAll(async () => {
|
||||
brain = new Brainy()
|
||||
await brain.init()
|
||||
vfs = brain.vfs()
|
||||
await vfs.init()
|
||||
projection = new PriorityProjection()
|
||||
})
|
||||
|
||||
it('should resolve high priority files', async () => {
|
||||
// Create test files
|
||||
await vfs.writeFile('/test1.ts', 'code', {
|
||||
metadata: { priority: 'high' }
|
||||
})
|
||||
await vfs.writeFile('/test2.ts', 'code', {
|
||||
metadata: { priority: 'low' }
|
||||
})
|
||||
|
||||
// Resolve high priority
|
||||
const ids = await projection.resolve(brain, vfs, 'high')
|
||||
|
||||
expect(ids).toHaveLength(1)
|
||||
|
||||
const entity = await brain.get(ids[0])
|
||||
expect(entity.metadata.priority).toBe('high')
|
||||
})
|
||||
|
||||
it('should list all files with priority', async () => {
|
||||
const entities = await projection.list(brain, vfs, 100)
|
||||
|
||||
expect(entities.length).toBeGreaterThan(0)
|
||||
expect(entities.every(e => e.metadata.priority)).toBe(true)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. ✅ Name projections clearly
|
||||
```typescript
|
||||
// ✅ Good
|
||||
readonly name = 'priority' // /by-priority/high
|
||||
readonly name = 'language' // /by-language/typescript
|
||||
|
||||
// ❌ Bad
|
||||
readonly name = 'proj1' // /by-proj1/??? unclear
|
||||
```
|
||||
|
||||
### 2. ✅ Document expected metadata
|
||||
```typescript
|
||||
/**
|
||||
* Priority Projection
|
||||
*
|
||||
* Requires metadata fields:
|
||||
* - priority: string ('high' | 'medium' | 'low')
|
||||
*
|
||||
* Usage:
|
||||
* /by-priority/high
|
||||
*/
|
||||
export class PriorityProjection extends BaseProjectionStrategy {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### 3. ✅ Handle missing data gracefully
|
||||
```typescript
|
||||
async resolve(brain, vfs, value) {
|
||||
const results = await brain.find({
|
||||
where: { priority: value },
|
||||
limit: 1000
|
||||
})
|
||||
|
||||
// Return empty array if no results, don't throw
|
||||
return this.extractIds(results) // [] if empty
|
||||
}
|
||||
```
|
||||
|
||||
### 4. ✅ Validate input
|
||||
```typescript
|
||||
async resolve(brain, vfs, priority: string) {
|
||||
// Validate priority value
|
||||
const valid = ['high', 'medium', 'low']
|
||||
if (!valid.includes(priority)) {
|
||||
return [] // Or throw error
|
||||
}
|
||||
|
||||
// Continue with query...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern 1: Enum-Based Projection
|
||||
For fixed sets of values (status, priority, type):
|
||||
|
||||
```typescript
|
||||
private readonly validValues = ['draft', 'review', 'approved']
|
||||
|
||||
async resolve(brain, vfs, status: string) {
|
||||
if (!this.validValues.includes(status)) return []
|
||||
// ... query
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Range-Based Projection
|
||||
For numeric or time ranges:
|
||||
|
||||
```typescript
|
||||
private readonly ranges = {
|
||||
recent: Date.now() - 86400000, // Last 24h
|
||||
week: Date.now() - 7 * 86400000, // Last week
|
||||
month: Date.now() - 30 * 86400000 // Last month
|
||||
}
|
||||
|
||||
async resolve(brain, vfs, period: string) {
|
||||
const since = this.ranges[period]
|
||||
if (!since) return []
|
||||
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
modified: { greaterEqual: since }
|
||||
}
|
||||
})
|
||||
return this.extractIds(results)
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: Computed Projection
|
||||
Combine multiple criteria:
|
||||
|
||||
```typescript
|
||||
async resolve(brain, vfs, value: string) {
|
||||
// "stale" = not modified in 30 days AND no recent access
|
||||
if (value === 'stale') {
|
||||
const thirtyDaysAgo = Date.now() - 30 * 86400000
|
||||
|
||||
const results = await brain.find({
|
||||
where: {
|
||||
allOf: [
|
||||
{ modified: { lessThan: thirtyDaysAgo } },
|
||||
{ accessed: { lessThan: thirtyDaysAgo } }
|
||||
]
|
||||
}
|
||||
})
|
||||
return this.extractIds(results)
|
||||
}
|
||||
|
||||
// Regular query for other values...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Projection returns empty results
|
||||
1. Check metadata exists: `console.log(entity.metadata)`
|
||||
2. Verify query syntax: Use BFO operators, not MongoDB
|
||||
3. Check limits: Increase limit if needed
|
||||
|
||||
### Slow performance
|
||||
1. Check if field is indexed: All metadata fields are auto-indexed
|
||||
2. Avoid post-filtering: Flatten complex structures
|
||||
3. Use appropriate limits: Don't fetch more than needed
|
||||
|
||||
### Type errors
|
||||
1. Import correct types: `import { Brainy, VirtualFileSystem } from '@soulcraft/brainy'`
|
||||
2. Use `as VFSEntity` when mapping results
|
||||
3. Check BaseProjectionStrategy import
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Semantic VFS Guide](./SEMANTIC_VFS.md) - Using semantic paths
|
||||
- [Performance Tuning](./PERFORMANCE_TUNING.md) - Optimization guide
|
||||
- [VFS Core API](./VFS_CORE.md) - Base VFS operations
|
||||
Loading…
Add table
Add a link
Reference in a new issue