248 lines
6.7 KiB
Markdown
248 lines
6.7 KiB
Markdown
|
|
# Query Operators (BFO)
|
||
|
|
|
||
|
|
> Brainy Field Operators — the complete reference for `where` filters in `find()`.
|
||
|
|
|
||
|
|
All operators work with `find({ where: { ... } })` and filter on **metadata fields** (not `data`).
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Equality
|
||
|
|
|
||
|
|
| Operator | Alias | Description | Example |
|
||
|
|
|----------|-------|-------------|---------|
|
||
|
|
| `equals` | `eq`, `is` | Exact match | `{ status: { equals: 'active' } }` |
|
||
|
|
| `notEquals` | `ne`, `isNot` | Not equal | `{ status: { notEquals: 'deleted' } }` |
|
||
|
|
|
||
|
|
**Shorthand:** A bare value is treated as `equals`:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// These are equivalent:
|
||
|
|
brain.find({ where: { status: 'active' } })
|
||
|
|
brain.find({ where: { status: { equals: 'active' } } })
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Comparison
|
||
|
|
|
||
|
|
| Operator | Alias | Description | Example |
|
||
|
|
|----------|-------|-------------|---------|
|
||
|
|
| `greaterThan` | `gt` | Greater than | `{ age: { greaterThan: 18 } }` |
|
||
|
|
| `greaterEqual` | `gte` | Greater or equal | `{ score: { greaterEqual: 90 } }` |
|
||
|
|
| `lessThan` | `lt` | Less than | `{ price: { lessThan: 100 } }` |
|
||
|
|
| `lessEqual` | `lte` | Less or equal | `{ rating: { lessEqual: 3 } }` |
|
||
|
|
| `between` | — | Inclusive range `[min, max]` | `{ year: { between: [2020, 2025] } }` |
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Range query
|
||
|
|
const recent = await brain.find({
|
||
|
|
where: {
|
||
|
|
createdAt: { between: [Date.now() - 86400000, Date.now()] }
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Array / Set
|
||
|
|
|
||
|
|
| Operator | Alias | Description | Example |
|
||
|
|
|----------|-------|-------------|---------|
|
||
|
|
| `oneOf` | `in` | Value is one of the given options | `{ color: { oneOf: ['red', 'blue'] } }` |
|
||
|
|
| `noneOf` | — | Value is NOT one of the given options | `{ status: { noneOf: ['deleted', 'archived'] } }` |
|
||
|
|
| `contains` | — | Array field contains value | `{ tags: { contains: 'ai' } }` |
|
||
|
|
| `excludes` | — | Array field does NOT contain value | `{ tags: { excludes: 'spam' } }` |
|
||
|
|
| `hasAll` | — | Array field contains ALL listed values | `{ skills: { hasAll: ['js', 'ts'] } }` |
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Find entities tagged with 'ai'
|
||
|
|
const aiEntities = await brain.find({
|
||
|
|
where: { tags: { contains: 'ai' } }
|
||
|
|
})
|
||
|
|
|
||
|
|
// Find entities of specific types
|
||
|
|
const people = await brain.find({
|
||
|
|
where: { noun: { oneOf: ['Person', 'Agent'] } }
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Existence
|
||
|
|
|
||
|
|
| Operator | Description | Example |
|
||
|
|
|----------|-------------|---------|
|
||
|
|
| `exists: true` | Field exists (has any value) | `{ email: { exists: true } }` |
|
||
|
|
| `exists: false` | Field does NOT exist | `{ email: { exists: false } }` |
|
||
|
|
| `missing: true` | Field does NOT exist (alias for `exists: false`) | `{ email: { missing: true } }` |
|
||
|
|
| `missing: false` | Field exists (alias for `exists: true`) | `{ email: { missing: false } }` |
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Find entities that have an email field
|
||
|
|
const withEmail = await brain.find({
|
||
|
|
where: { email: { exists: true } }
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Pattern (In-Memory Only)
|
||
|
|
|
||
|
|
These operators work via the in-memory filter path. They are applied **after** the indexed query, so use them with other indexed operators for best performance.
|
||
|
|
|
||
|
|
| Operator | Description | Example |
|
||
|
|
|----------|-------------|---------|
|
||
|
|
| `matches` | Regex or string pattern match | `{ name: { matches: /^Dr\./ } }` |
|
||
|
|
| `startsWith` | String prefix | `{ name: { startsWith: 'John' } }` |
|
||
|
|
| `endsWith` | String suffix | `{ email: { endsWith: '@gmail.com' } }` |
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const doctors = await brain.find({
|
||
|
|
where: {
|
||
|
|
type: NounType.Person, // Indexed — fast
|
||
|
|
name: { startsWith: 'Dr.' } // In-memory — applied after
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Logical
|
||
|
|
|
||
|
|
Combine multiple conditions:
|
||
|
|
|
||
|
|
| Operator | Description | Example |
|
||
|
|
|----------|-------------|---------|
|
||
|
|
| `allOf` | ALL sub-filters must match (AND) | `{ allOf: [{ status: 'active' }, { role: 'admin' }] }` |
|
||
|
|
| `anyOf` | ANY sub-filter must match (OR) | `{ anyOf: [{ role: 'admin' }, { role: 'owner' }] }` |
|
||
|
|
| `not` | Invert a filter | `{ not: { status: 'deleted' } }` |
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Complex OR query
|
||
|
|
const adminsOrOwners = await brain.find({
|
||
|
|
where: {
|
||
|
|
anyOf: [
|
||
|
|
{ role: 'admin' },
|
||
|
|
{ role: 'owner' }
|
||
|
|
]
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// NOT query
|
||
|
|
const notDeleted = await brain.find({
|
||
|
|
where: {
|
||
|
|
not: { status: 'deleted' }
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
// Combined AND + OR
|
||
|
|
const results = await brain.find({
|
||
|
|
where: {
|
||
|
|
allOf: [
|
||
|
|
{ department: 'engineering' },
|
||
|
|
{ anyOf: [
|
||
|
|
{ level: 'senior' },
|
||
|
|
{ yearsExperience: { greaterThan: 5 } }
|
||
|
|
]}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Indexed vs In-Memory Operators
|
||
|
|
|
||
|
|
Brainy's MetadataIndex supports a subset of operators natively for O(1) field lookups. Other operators fall back to in-memory filtering.
|
||
|
|
|
||
|
|
| Operator | MetadataIndex (Indexed) | In-Memory Fallback |
|
||
|
|
|----------|:-----------------------:|:------------------:|
|
||
|
|
| `equals` / `eq` | Yes | Yes |
|
||
|
|
| `notEquals` / `ne` | — | Yes |
|
||
|
|
| `greaterThan` / `gt` | Yes | Yes |
|
||
|
|
| `greaterEqual` / `gte` | Yes | Yes |
|
||
|
|
| `lessThan` / `lt` | Yes | Yes |
|
||
|
|
| `lessEqual` / `lte` | Yes | Yes |
|
||
|
|
| `between` | Yes | Yes |
|
||
|
|
| `oneOf` / `in` | Yes | Yes |
|
||
|
|
| `noneOf` | — | Yes |
|
||
|
|
| `contains` | Yes | Yes |
|
||
|
|
| `exists` / `missing` | Yes | Yes |
|
||
|
|
| `matches` | — | Yes |
|
||
|
|
| `startsWith` | — | Yes |
|
||
|
|
| `endsWith` | — | Yes |
|
||
|
|
| `allOf` | Partial | Yes |
|
||
|
|
| `anyOf` | Partial | Yes |
|
||
|
|
| `not` | — | Yes |
|
||
|
|
|
||
|
|
**Performance tip:** Combine indexed operators (equals, greaterThan, oneOf, between, contains, exists) with pattern operators for optimal speed — the index narrows results first, then patterns filter in memory.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Practical Examples
|
||
|
|
|
||
|
|
### Filter by entity type
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// Using the type shorthand (recommended)
|
||
|
|
brain.find({ type: NounType.Person })
|
||
|
|
|
||
|
|
// Using where.noun directly
|
||
|
|
brain.find({ where: { noun: NounType.Person } })
|
||
|
|
|
||
|
|
// Multiple types
|
||
|
|
brain.find({ type: [NounType.Person, NounType.Agent] })
|
||
|
|
```
|
||
|
|
|
||
|
|
### Combine semantic search with filters
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const results = await brain.find({
|
||
|
|
query: 'machine learning engineer', // Semantic search (on data)
|
||
|
|
type: NounType.Person, // Type filter (indexed)
|
||
|
|
where: {
|
||
|
|
department: 'engineering', // Exact match (indexed)
|
||
|
|
yearsExperience: { greaterThan: 3 } // Range filter (indexed)
|
||
|
|
},
|
||
|
|
limit: 10
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Temporal queries
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const lastWeek = Date.now() - 7 * 24 * 60 * 60 * 1000
|
||
|
|
const recentEntities = await brain.find({
|
||
|
|
where: {
|
||
|
|
createdAt: { greaterThan: lastWeek }
|
||
|
|
},
|
||
|
|
orderBy: 'createdAt',
|
||
|
|
order: 'desc',
|
||
|
|
limit: 50
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
### Graph + metadata combination
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const results = await brain.find({
|
||
|
|
connected: {
|
||
|
|
from: teamLeadId,
|
||
|
|
via: VerbType.WorksWith,
|
||
|
|
depth: 2
|
||
|
|
},
|
||
|
|
where: {
|
||
|
|
role: { oneOf: ['engineer', 'designer'] },
|
||
|
|
active: true
|
||
|
|
}
|
||
|
|
})
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## See Also
|
||
|
|
|
||
|
|
- [Data Model](./DATA_MODEL.md) — Entity structure, data vs metadata
|
||
|
|
- [API Reference](./api/README.md) — Complete API documentation
|
||
|
|
- [Find System](./FIND_SYSTEM.md) — Natural language find() details
|