Skip to content

Commit f5e8b5a

Browse files
Copilothotlong
andcommitted
docs: enhance JSDoc and README for Redis driver v4.0
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent f18b9b0 commit f5e8b5a

2 files changed

Lines changed: 268 additions & 14 deletions

File tree

packages/drivers/redis/README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,20 @@
22

33
> ⚠️ **Note**: This is an **example/template implementation** to demonstrate how to create custom ObjectQL drivers. It is not production-ready and serves as a reference for driver development.
44
5+
**Version 4.0.0** - Now compliant with DriverInterface from @objectstack/spec
6+
57
## Overview
68

79
The Redis Driver is a reference implementation showing how to adapt a key-value store (Redis) to work with ObjectQL's universal data protocol. While Redis is primarily designed for caching and simple key-value operations, this driver demonstrates how to map ObjectQL's rich query interface to a simpler database model.
810

11+
This driver implements both the legacy Driver interface from @objectql/types and the standard DriverInterface from @objectstack/spec for full compatibility with the new kernel-based plugin system.
12+
913
## Features
1014

1115
- ✅ Basic CRUD operations (Create, Read, Update, Delete)
16+
-**v4.0**: executeQuery() with QueryAST support
17+
-**v4.0**: executeCommand() with unified command interface
18+
-**v4.0**: Bulk operations (bulkCreate, bulkUpdate, bulkDelete) using Redis PIPELINE
1219
- ✅ Query filtering (in-memory)
1320
- ✅ Sorting (in-memory)
1421
- ✅ Pagination (skip/limit)
@@ -136,6 +143,7 @@ new RedisDriver(config: RedisDriverConfig)
136143

137144
All standard Driver interface methods are implemented:
138145

146+
**Legacy Driver Interface:**
139147
- `find(objectName, query, options)` - Query multiple records
140148
- `findOne(objectName, id, query, options)` - Get single record by ID
141149
- `create(objectName, data, options)` - Create new record
@@ -144,6 +152,110 @@ All standard Driver interface methods are implemented:
144152
- `count(objectName, filters, options)` - Count matching records
145153
- `disconnect()` - Close Redis connection
146154

155+
**DriverInterface v4.0 Methods:**
156+
- `executeQuery(ast, options)` - Execute queries using QueryAST format
157+
- `executeCommand(command, options)` - Execute commands (create, update, delete, bulk operations)
158+
159+
### executeQuery Examples
160+
161+
The new `executeQuery` method uses the QueryAST format from @objectstack/spec:
162+
163+
```typescript
164+
// Basic query
165+
const result = await driver.executeQuery({
166+
object: 'users',
167+
fields: ['name', 'email']
168+
});
169+
console.log(result.value); // Array of users
170+
console.log(result.count); // Number of results
171+
172+
// Query with filters
173+
const result = await driver.executeQuery({
174+
object: 'users',
175+
filters: {
176+
type: 'comparison',
177+
field: 'age',
178+
operator: '>',
179+
value: 18
180+
},
181+
sort: [{ field: 'name', order: 'asc' }],
182+
top: 10,
183+
skip: 0
184+
});
185+
186+
// Complex filters (AND/OR)
187+
const result = await driver.executeQuery({
188+
object: 'users',
189+
filters: {
190+
type: 'and',
191+
children: [
192+
{ type: 'comparison', field: 'role', operator: '=', value: 'user' },
193+
{ type: 'comparison', field: 'age', operator: '>', value: 30 }
194+
]
195+
}
196+
});
197+
```
198+
199+
### executeCommand Examples
200+
201+
The new `executeCommand` method provides a unified interface for mutations:
202+
203+
```typescript
204+
// Create a record
205+
const result = await driver.executeCommand({
206+
type: 'create',
207+
object: 'users',
208+
data: { name: 'Alice', email: 'alice@example.com' }
209+
});
210+
console.log(result.success); // true
211+
console.log(result.data); // Created user object
212+
console.log(result.affected); // 1
213+
214+
// Update a record
215+
const result = await driver.executeCommand({
216+
type: 'update',
217+
object: 'users',
218+
id: 'user-123',
219+
data: { email: 'newemail@example.com' }
220+
});
221+
222+
// Delete a record
223+
const result = await driver.executeCommand({
224+
type: 'delete',
225+
object: 'users',
226+
id: 'user-123'
227+
});
228+
229+
// Bulk create (uses Redis PIPELINE for performance)
230+
const result = await driver.executeCommand({
231+
type: 'bulkCreate',
232+
object: 'users',
233+
records: [
234+
{ name: 'Alice', age: 30 },
235+
{ name: 'Bob', age: 25 },
236+
{ name: 'Charlie', age: 35 }
237+
]
238+
});
239+
console.log(result.affected); // 3
240+
241+
// Bulk update
242+
const result = await driver.executeCommand({
243+
type: 'bulkUpdate',
244+
object: 'users',
245+
updates: [
246+
{ id: 'user-1', data: { age: 31 } },
247+
{ id: 'user-2', data: { age: 26 } }
248+
]
249+
});
250+
251+
// Bulk delete
252+
const result = await driver.executeCommand({
253+
type: 'bulkDelete',
254+
object: 'users',
255+
ids: ['user-1', 'user-2', 'user-3']
256+
});
257+
```
258+
147259
## Example: Using as Cache Layer
148260

149261
Redis works great as a caching layer in front of another driver:

packages/drivers/redis/src/index.ts

Lines changed: 156 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,25 +36,66 @@ import { createClient, RedisClientType } from 'redis';
3636

3737
/**
3838
* Command interface for executeCommand method
39+
*
40+
* This interface defines the structure for all mutation operations
41+
* (create, update, delete, and their bulk variants) in the v4.0 DriverInterface.
42+
*
43+
* @example
44+
* // Create a single record
45+
* const cmd: Command = { type: 'create', object: 'users', data: { name: 'Alice' } };
46+
*
47+
* @example
48+
* // Update a record
49+
* const cmd: Command = { type: 'update', object: 'users', id: '123', data: { email: 'new@example.com' } };
50+
*
51+
* @example
52+
* // Bulk create multiple records
53+
* const cmd: Command = {
54+
* type: 'bulkCreate',
55+
* object: 'users',
56+
* records: [{ name: 'Alice' }, { name: 'Bob' }]
57+
* };
3958
*/
4059
export interface Command {
60+
/** Command type: create, update, delete, or their bulk variants */
4161
type: 'create' | 'update' | 'delete' | 'bulkCreate' | 'bulkUpdate' | 'bulkDelete';
62+
/** Target object name */
4263
object: string;
64+
/** Data for create/update operations */
4365
data?: any;
66+
/** Record ID for single update/delete operations */
4467
id?: string | number;
68+
/** Array of IDs for bulkDelete operation */
4569
ids?: Array<string | number>;
70+
/** Array of records for bulkCreate operation */
4671
records?: any[];
72+
/** Array of updates for bulkUpdate operation */
4773
updates?: Array<{id: string | number, data: any}>;
74+
/** Additional command-specific options */
4875
options?: any;
4976
}
5077

5178
/**
5279
* Command result interface
80+
*
81+
* Standardized result format for all executeCommand operations.
82+
*
83+
* @example
84+
* // Successful create
85+
* { success: true, data: { id: '123', name: 'Alice' }, affected: 1 }
86+
*
87+
* @example
88+
* // Failed operation
89+
* { success: false, error: 'Record not found', affected: 0 }
5390
*/
5491
export interface CommandResult {
92+
/** Whether the command executed successfully */
5593
success: boolean;
94+
/** The resulting data (for create/update operations) */
5695
data?: any;
96+
/** Number of records affected by the operation */
5797
affected: number;
98+
/** Error message if the command failed */
5899
error?: string;
59100
}
60101

@@ -325,12 +366,41 @@ export class RedisDriver implements Driver, DriverInterface {
325366
/**
326367
* Execute a query (DriverInterface v4.0 method)
327368
*
328-
* This method handles all read operations using the QueryAST format.
329-
* Converts QueryAST to legacy query format and delegates to find().
369+
* This method handles all read operations using the QueryAST format from @objectstack/spec.
370+
* It provides a standardized query interface that supports:
371+
* - Field selection (projection)
372+
* - Filter conditions (using FilterNode AST)
373+
* - Sorting
374+
* - Pagination (skip/top)
375+
* - Grouping and aggregations (delegated to find)
376+
*
377+
* The method converts the QueryAST format to the legacy query format and delegates
378+
* to the existing find() method for backward compatibility.
330379
*
331-
* @param ast - The query AST
380+
* @param ast - The query Abstract Syntax Tree
332381
* @param options - Optional execution options
333-
* @returns Query results with count
382+
* @returns Object containing query results and count
383+
*
384+
* @example
385+
* // Simple query
386+
* const result = await driver.executeQuery({
387+
* object: 'users',
388+
* fields: ['name', 'email']
389+
* });
390+
*
391+
* @example
392+
* // Query with filters and sorting
393+
* const result = await driver.executeQuery({
394+
* object: 'users',
395+
* filters: {
396+
* type: 'comparison',
397+
* field: 'age',
398+
* operator: '>',
399+
* value: 18
400+
* },
401+
* sort: [{ field: 'name', order: 'asc' }],
402+
* top: 10
403+
* });
334404
*/
335405
async executeQuery(ast: QueryAST, options?: any): Promise<{ value: any[]; count?: number }> {
336406
const objectName = ast.object || '';
@@ -356,16 +426,54 @@ export class RedisDriver implements Driver, DriverInterface {
356426
/**
357427
* Execute a command (DriverInterface v4.0 method)
358428
*
359-
* This method handles all mutation operations (create, update, delete)
360-
* using a unified command interface.
429+
* This method provides a unified interface for all mutation operations (create, update, delete)
430+
* using the Command pattern from @objectstack/spec.
431+
*
432+
* Supports both single operations and bulk operations:
433+
* - Single: create, update, delete
434+
* - Bulk: bulkCreate, bulkUpdate, bulkDelete
361435
*
362-
* Supports both single operations and bulk operations using Redis PIPELINE
363-
* for optimal performance.
436+
* Bulk operations use Redis PIPELINE for optimal performance, executing multiple
437+
* commands in a single round-trip to the server.
364438
*
365-
* @param command - The command to execute
366-
* @param parameters - Optional command parameters (unused in this driver)
439+
* All operations return a standardized CommandResult with:
440+
* - success: boolean indicating operation success/failure
441+
* - data: the resulting data (for create/update)
442+
* - affected: number of records affected
443+
* - error: error message if operation failed
444+
*
445+
* @param command - The command to execute (see Command interface)
367446
* @param options - Optional execution options
368-
* @returns Command execution result
447+
* @returns Standardized command execution result
448+
*
449+
* @example
450+
* // Create a single record
451+
* const result = await driver.executeCommand({
452+
* type: 'create',
453+
* object: 'users',
454+
* data: { name: 'Alice', email: 'alice@example.com' }
455+
* });
456+
*
457+
* @example
458+
* // Bulk create multiple records
459+
* const result = await driver.executeCommand({
460+
* type: 'bulkCreate',
461+
* object: 'users',
462+
* records: [
463+
* { name: 'Alice' },
464+
* { name: 'Bob' },
465+
* { name: 'Charlie' }
466+
* ]
467+
* });
468+
*
469+
* @example
470+
* // Update a record
471+
* const result = await driver.executeCommand({
472+
* type: 'update',
473+
* object: 'users',
474+
* id: 'user-123',
475+
* data: { email: 'newemail@example.com' }
476+
* });
369477
*/
370478
async executeCommand(command: Command, options?: any): Promise<CommandResult> {
371479
try {
@@ -506,9 +614,31 @@ export class RedisDriver implements Driver, DriverInterface {
506614

507615
/**
508616
* Convert FilterNode (QueryAST format) to legacy filter array format
509-
* This allows reuse of existing filter logic while supporting new QueryAST
510617
*
618+
* This method bridges the gap between the new QueryAST filter format (tree-based)
619+
* and the legacy array-based filter format used internally by the driver.
620+
*
621+
* QueryAST FilterNode format:
622+
* - type: 'comparison' | 'and' | 'or' | 'not'
623+
* - field, operator, value for comparisons
624+
* - children for logical operators
625+
*
626+
* Legacy format:
627+
* - Array of conditions: [field, operator, value]
628+
* - String separators: 'and', 'or'
629+
* - Example: [['age', '>', 18], 'and', ['role', '=', 'user']]
630+
*
631+
* @param node - The FilterNode to convert
632+
* @returns Legacy filter array format, or undefined if no filters
511633
* @private
634+
*
635+
* @example
636+
* // Input: { type: 'comparison', field: 'age', operator: '>', value: 18 }
637+
* // Output: [['age', '>', 18]]
638+
*
639+
* @example
640+
* // Input: { type: 'and', children: [...] }
641+
* // Output: [['field1', '=', 'val1'], 'and', ['field2', '>', 10]]
512642
*/
513643
private convertFilterNodeToLegacy(node?: FilterNode): any {
514644
if (!node) return undefined;
@@ -563,10 +693,22 @@ export class RedisDriver implements Driver, DriverInterface {
563693
/**
564694
* Generate Redis key for an object record
565695
*
566-
* Strategy: objectName:id
567-
* Example: users:user-123
696+
* This method implements the key naming strategy for storing records in Redis.
697+
* The strategy uses a simple pattern: `objectName:id`
568698
*
699+
* This ensures:
700+
* - Easy querying by pattern (e.g., `users:*` to find all user records)
701+
* - Clear namespace separation between different object types
702+
* - Human-readable keys for debugging
703+
*
704+
* @param objectName - The object/collection name
705+
* @param id - The record ID
706+
* @returns Redis key in format "objectName:id"
569707
* @private
708+
*
709+
* @example
710+
* generateRedisKey('users', '123') // Returns: 'users:123'
711+
* generateRedisKey('orders', 'order-456') // Returns: 'orders:order-456'
570712
*/
571713
private generateRedisKey(objectName: string, id: string | number): string {
572714
return `${objectName}:${id}`;

0 commit comments

Comments
 (0)