Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions content/docs/ai/coding-assistant.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Without this prompt, Copilot assumes you are using a generic ORM (like TypeORM)
With the System Prompt, it understands the **Context + Repository** pattern:

> ✅ **Good AI Output:**
> `await ctx.object('todo').find({ filters: [['priority', '=', 'high']] })`
> `await ctx.object('todo').find({ filters: { priority: 'high' } })`

### 2. Prompting Strategy

Expand Down Expand Up @@ -103,10 +103,10 @@ Use the standard generic CRUD API via a context.
const ctx = app.createContext({});

const todos = await ctx.object('todo').find({
filters: [
['completed', '=', false],
['priority', '=', 'high']
],
filters: {
completed: false,
priority: 'high'
},
fields: ['title', 'owner.name'], // Select specific fields & relations
sort: [['created_at', 'desc']],
skip: 0,
Expand Down
61 changes: 30 additions & 31 deletions content/docs/data-access/best-practices.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@ The **JSON-DSL** is ObjectQL's core query language - a structured JSON represent
```typescript
const tasks = await app.object('task').find({
fields: ['name', 'status', 'due_date'],
filters: [
['status', '=', 'active'],
'and',
['priority', '>=', 3]
],
filters: {
status: 'active',
priority: { $gte: 3 }
},
sort: [['due_date', 'asc']],
skip: 0,
limit: 20
Expand All @@ -59,7 +58,7 @@ const tasks = await app.object('task').find({
```typescript
// Returns ALL fields (inefficient)
await app.object('user').find({
filters: [['status', '=', 'active']]
filters: { status: 'active' }
});
```

Expand All @@ -68,7 +67,7 @@ await app.object('user').find({
// Returns only needed fields (efficient)
await app.object('user').find({
fields: ['id', 'name', 'email'],
filters: [['status', '=', 'active']]
filters: { status: 'active' }
});
```

Expand All @@ -79,17 +78,16 @@ await app.object('user').find({
**Bad:**
```typescript
// Filters on non-indexed field
filters: [['description', 'contains', 'urgent']]
filters: { description: { $contains: 'urgent' } }
```

**Good:**
```typescript
// Filters on indexed field first, then post-filter if needed
filters: [
['status', '=', 'open'], // Indexed
'and',
['priority', '=', 'high'] // Indexed
]
filters: {
status: 'open', // Indexed
priority: 'high' // Indexed
}
```

**Impact:** Can improve query speed by 10-100x depending on dataset size.
Expand All @@ -100,15 +98,15 @@ filters: [
```typescript
// Returns all records (dangerous)
await app.object('order').find({
filters: [['year', '=', 2024]]
filters: { year: 2024 }
});
```

**Good:**
```typescript
// Paginated results (safe and fast)
await app.object('order').find({
filters: [['year', '=', 2024]],
filters: { year: 2024 },
limit: 50,
skip: page * 50,
sort: [['created_at', 'desc']]
Expand Down Expand Up @@ -431,7 +429,7 @@ const tasksWithAssignee = await Promise.all(
const tasks = await taskRepo.find();
const userIds = tasks.map(t => t.assignee_id);
const users = await userRepo.find({
filters: [['id', 'in', userIds]]
filters: { id: { $in: userIds } }
});
const userMap = new Map(users.map(u => [u.id, u]));
const tasksWithAssigneeBatched = tasks.map((task) => ({
Expand Down Expand Up @@ -516,7 +514,7 @@ query Dashboard {
// Hook: Automatically assign to least-busy team member
async function autoAssign(task: any) {
const members = await app.object('user').aggregate({
filters: [['team_id', '=', task.team_id]],
filters: { team_id: task.team_id },
groupBy: ['id', 'name'],
aggregate: [
{ func: 'count', field: 'tasks.id', alias: 'task_count' }
Expand Down Expand Up @@ -581,7 +579,7 @@ AND priority = 'high' AND invalid_function(status);
**Bad (Application-level aggregation):**
```typescript
const orders = await app.object('order').find({
filters: [['status', '=', 'paid']]
filters: { status: 'paid' }
});

// Slow: Iterating in application code
Expand All @@ -594,7 +592,7 @@ for (const order of orders) {
**Good (Database-level aggregation):**
```typescript
const stats = await app.object('order').aggregate({
filters: [['status', '=', 'paid']],
filters: { status: 'paid' },
groupBy: ['customer_id'],
aggregate: [
{ func: 'sum', field: 'amount', alias: 'total_revenue' },
Expand All @@ -620,7 +618,7 @@ const uniqueCustomers = [...new Set(orders.map(o => o.customer_id))];
**Good:**
```typescript
const uniqueCustomers = await app.object('order').distinct('customer_id', {
filters: [['year', '=', 2024]]
filters: { year: 2024 }
});
```

Expand Down Expand Up @@ -661,18 +659,19 @@ indexes:

**Bad (OR requires multiple index scans):**
```typescript
filters: [
['status', '=', 'pending'],
'or',
['status', '=', 'active']
]
filters: {
$or: [
{ status: 'pending' },
{ status: 'active' }
]
}
```

**Good (IN uses single index scan):**
```typescript
filters: [
['status', 'in', ['pending', 'active']]
]
filters: {
status: { $in: ['pending', 'active'] }
}
```

**Impact:** 2-5x faster for large tables.
Expand All @@ -693,7 +692,7 @@ await app.object('order').find({
**Good (Cursor pagination using last ID):**
```typescript
await app.object('order').find({
filters: [['id', '>', lastSeenId]],
filters: { id: { $gt: lastSeenId } },
limit: 50,
sort: [['id', 'asc']]
});
Expand Down Expand Up @@ -754,7 +753,7 @@ GET /api/data/tasks?status=active&limit=20
**After:**
```typescript
await app.object('task').find({
filters: [['status', '=', 'active']],
filters: { status: 'active' },
limit: 20
});
```
Expand All @@ -764,7 +763,7 @@ await app.object('task').find({
**Before:**
```typescript
const tasks = await app.object('task').find({
filters: [['status', '=', 'active']],
filters: { status: 'active' },
expand: {
assignee: { fields: ['name', 'email'] }
}
Expand Down
9 changes: 4 additions & 5 deletions content/docs/data-access/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,10 @@ const ctx = app.createContext({ isSystem: true });
// Query data with filters
const products = await ctx.object('product').find({
fields: ['name', 'price', 'category'],
filters: [
['category', '=', 'electronics'],
'and',
['price', '<', 1000]
],
filters: {
category: 'electronics',
price: { $lt: 1000 }
},
sort: [['price', 'asc']],
top: 10
});
Expand Down
43 changes: 25 additions & 18 deletions content/docs/data-access/querying.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ The `find` method recovers a list of records matching specific criteria.

```typescript
const products = await app.object('product').find({
filters: [
['category', '=', 'electronics'],
['price', '>', 500]
],
filters: {
category: 'electronics',
price: { $gt: 500 }
},
fields: ['name', 'price', 'category'],
sort: ['-price'], // Descending
skip: 0,
Expand All @@ -28,19 +28,26 @@ const products = await app.object('product').find({

### Filters

Filters are defined as a 2D array: `[[ field, operator, value ]]`.
Filters use an object-based syntax with implicit equality and operator objects.

**Implicit AND**:
```typescript
filters: [
['status', '=', 'active'],
['stock', '>', 0]
]
filters: {
status: 'active',
stock: { $gt: 0 }
}
// SQL: WHERE status = 'active' AND stock > 0
```

**Explicit OR**:
Use the `_or` special operator in complex filters (see advanced docs).
```typescript
filters: {
$or: [
{ status: 'active' },
{ featured: true }
]
}
```

### Sorting

Expand All @@ -66,13 +73,13 @@ Updates are always bulk operations targeted by `filters`. To update a single rec
```typescript
// Update specific record
await app.object('user').update(
{ filters: [['_id', '=', '123']] }, // Target
{ filters: { _id: '123' } }, // Target
{ doc: { status: 'active' } } // Change
);

// Bulk update
await app.object('product').update(
{ filters: [['stock', '=', 0]] },
{ filters: { stock: 0 } },
{ doc: { status: 'out_of_stock' } }
);
```
Expand All @@ -82,7 +89,7 @@ await app.object('product').update(
```typescript
// Delete specific record
await app.object('user').delete({
filters: [['_id', '=', '123']]
filters: { _id: '123' }
});
```

Expand Down Expand Up @@ -141,9 +148,9 @@ There are two ways to filter based on relationships:
Find tasks where the *project's status* is active.
*(Note: Requires a driver that supports SQL Joins)*
```typescript
filters: [
['project.status', '=', 'active']
]
filters: {
'project.status': 'active'
}
```

**B. Filter the Expanded List**
Expand All @@ -152,7 +159,7 @@ Find projects, but only include *completed* tasks in the expansion.
app.object('project').find({
expand: {
tasks: {
filters: [['status', '=', 'completed']]
filters: { status: 'completed' }
}
}
})
Expand All @@ -165,7 +172,7 @@ ObjectQL supports SQL-like aggregation via the `aggregate()` method on the repos
```typescript
const stats = await app.object('order').aggregate({
// 1. Filter first
filters: [['status', '=', 'paid']],
filters: { status: 'paid' },

// 2. Group by specific fields
groupBy: ['customer_id'],
Expand Down
Loading
Loading