@objectstack/objectql 0.9.0 → 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +286 -0
  3. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @objectstack/objectql
2
2
 
3
+ ## 0.9.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @objectstack/spec@0.9.2
9
+ - @objectstack/core@0.9.2
10
+ - @objectstack/types@0.9.2
11
+
12
+ ## 0.9.1
13
+
14
+ ### Patch Changes
15
+
16
+ - Patch release for maintenance and stability improvements. All packages updated with unified versioning.
17
+ - Updated dependencies
18
+ - @objectstack/spec@0.9.1
19
+ - @objectstack/core@0.9.1
20
+ - @objectstack/types@0.9.1
21
+
3
22
  ## 0.8.2
4
23
 
5
24
  ### Patch Changes
package/README.md CHANGED
@@ -10,6 +10,19 @@
10
10
  - **Plugin System**: Load objects and logic via standard Manifests.
11
11
  - **Middleware**: (Planned) Support for Hooks and Validators.
12
12
 
13
+ ## 🤖 AI Development Context
14
+
15
+ **Role**: Data Engine / Query Layer
16
+ **Usage**:
17
+ - Use this package to execute data operations (`find`, `insert`).
18
+ - Do NOT bypass this layer to access drivers directly unless building a driver.
19
+ - This is the "Backend Brain" for data.
20
+
21
+ **Key Concepts**:
22
+ - `SchemaRegistry`: Holds all object definitions.
23
+ - `ObjectQL`: The main facade.
24
+ - `Driver`: Interface for storage adapters.
25
+
13
26
  ## Usage
14
27
 
15
28
  ### 1. Standalone Usage
@@ -91,6 +104,279 @@ await kernel.bootstrap();
91
104
  - **QueryPlanner**: (Internal) Normalizes simplified queries into `QueryAST`.
92
105
  - **Executor**: Routes AST to the correct driver.
93
106
 
107
+ ## Advanced Examples
108
+
109
+ ### Complex Queries
110
+
111
+ ```typescript
112
+ // Filtering with multiple conditions
113
+ const activeTasks = await ql.find('todo_task', {
114
+ where: {
115
+ $and: [
116
+ { status: 'active' },
117
+ { priority: { $gte: 3 } },
118
+ { due_date: { $lte: new Date() } }
119
+ ]
120
+ },
121
+ orderBy: [
122
+ { field: 'priority', order: 'desc' },
123
+ { field: 'due_date', order: 'asc' }
124
+ ],
125
+ limit: 50
126
+ });
127
+
128
+ // Text search
129
+ const searchResults = await ql.find('todo_task', {
130
+ where: {
131
+ $or: [
132
+ { title: { $contains: 'urgent' } },
133
+ { description: { $contains: 'urgent' } }
134
+ ]
135
+ }
136
+ });
137
+
138
+ // Nested conditions
139
+ const complexQuery = await ql.find('project', {
140
+ where: {
141
+ $and: [
142
+ { status: { $in: ['active', 'planning'] } },
143
+ {
144
+ $or: [
145
+ { budget: { $gt: 100000 } },
146
+ { priority: { $eq: 1 } }
147
+ ]
148
+ }
149
+ ]
150
+ }
151
+ });
152
+ ```
153
+
154
+ ### Batch Operations
155
+
156
+ ```typescript
157
+ // Batch insert
158
+ const newTasks = [
159
+ { title: 'Task 1', priority: 1 },
160
+ { title: 'Task 2', priority: 2 },
161
+ { title: 'Task 3', priority: 3 }
162
+ ];
163
+
164
+ for (const task of newTasks) {
165
+ await ql.insert('todo_task', task);
166
+ }
167
+
168
+ // Batch update
169
+ const tasksToUpdate = await ql.find('todo_task', {
170
+ where: { status: 'pending' }
171
+ });
172
+
173
+ for (const task of tasksToUpdate) {
174
+ await ql.update('todo_task', task.id, {
175
+ status: 'in_progress',
176
+ started_at: new Date()
177
+ });
178
+ }
179
+ ```
180
+
181
+ ### Working with Relationships
182
+
183
+ ```typescript
184
+ // One-to-Many: Get project with tasks
185
+ const project = await ql.find('project', {
186
+ where: { id: 'proj_123' },
187
+ include: ['tasks'] // Assumes 'tasks' is a relation field
188
+ });
189
+
190
+ // Many-to-One: Get task with project details
191
+ const task = await ql.find('todo_task', {
192
+ where: { id: 'task_456' },
193
+ include: ['project']
194
+ });
195
+
196
+ // Manual join (when driver doesn't support relations)
197
+ const tasks = await ql.find('todo_task', {
198
+ where: { project_id: 'proj_123' }
199
+ });
200
+
201
+ const project = await ql.find('project', {
202
+ where: { id: 'proj_123' }
203
+ });
204
+
205
+ const projectWithTasks = {
206
+ ...project[0],
207
+ tasks: tasks
208
+ };
209
+ ```
210
+
211
+ ### Aggregations
212
+
213
+ ```typescript
214
+ // Count records
215
+ const totalTasks = await ql.count('todo_task', {
216
+ where: { status: 'active' }
217
+ });
218
+
219
+ // Sum (if driver supports)
220
+ const totalBudget = await ql.aggregate('project', {
221
+ operation: 'sum',
222
+ field: 'budget',
223
+ where: { status: 'active' }
224
+ });
225
+
226
+ // Group by (if driver supports)
227
+ const tasksByStatus = await ql.aggregate('todo_task', {
228
+ operation: 'count',
229
+ groupBy: ['status']
230
+ });
231
+ ```
232
+
233
+ ### Multi-Datasource Queries
234
+
235
+ ```typescript
236
+ import { PostgresDriver } from '@objectstack/driver-postgres';
237
+ import { MongoDBDriver } from '@objectstack/driver-mongodb';
238
+ import { RedisDriver } from '@objectstack/driver-redis';
239
+
240
+ const ql = new ObjectQL();
241
+
242
+ // Register multiple drivers
243
+ const pgDriver = new PostgresDriver({ connectionString: 'postgres://...' });
244
+ const mongoDriver = new MongoDBDriver({ url: 'mongodb://...' });
245
+ const redisDriver = new RedisDriver({ host: 'localhost' });
246
+
247
+ ql.registerDriver(pgDriver, false, 'postgres');
248
+ ql.registerDriver(mongoDriver, false, 'mongodb');
249
+ ql.registerDriver(redisDriver, false, 'redis');
250
+
251
+ // Configure objects to use different datasources
252
+ await ql.use({
253
+ name: 'multi-db-app',
254
+ objects: [
255
+ {
256
+ name: 'user',
257
+ datasource: 'postgres', // Users in PostgreSQL
258
+ fields: { /* ... */ }
259
+ },
260
+ {
261
+ name: 'product',
262
+ datasource: 'mongodb', // Products in MongoDB
263
+ fields: { /* ... */ }
264
+ },
265
+ {
266
+ name: 'session',
267
+ datasource: 'redis', // Sessions in Redis
268
+ fields: { /* ... */ }
269
+ }
270
+ ]
271
+ });
272
+
273
+ await ql.init();
274
+
275
+ // Query automatically routes to correct datasource
276
+ const users = await ql.find('user'); // → PostgreSQL
277
+ const products = await ql.find('product'); // → MongoDB
278
+ const sessions = await ql.find('session'); // → Redis
279
+ ```
280
+
281
+ ### Custom Hooks and Middleware
282
+
283
+ ```typescript
284
+ // Register hooks for data operations
285
+ ql.registerHook('beforeInsert', async (ctx) => {
286
+ console.log(`Creating ${ctx.object}:`, ctx.data);
287
+
288
+ // Add timestamps
289
+ ctx.data.created_at = new Date();
290
+ ctx.data.updated_at = new Date();
291
+
292
+ // Validate
293
+ if (ctx.object === 'user' && !ctx.data.email) {
294
+ throw new Error('Email is required');
295
+ }
296
+ });
297
+
298
+ ql.registerHook('afterInsert', async (ctx) => {
299
+ console.log(`Created ${ctx.object}:`, ctx.result);
300
+
301
+ // Trigger events - get event bus from kernel or context
302
+ // Note: eventBus should be injected or accessed from context
303
+ const eventBus = ctx.getService?.('event-bus');
304
+ if (eventBus) {
305
+ await eventBus.emit('data:created', {
306
+ object: ctx.object,
307
+ id: ctx.result.id
308
+ });
309
+ }
310
+ });
311
+
312
+ ql.registerHook('beforeUpdate', async (ctx) => {
313
+ // Update timestamp
314
+ ctx.data.updated_at = new Date();
315
+ });
316
+
317
+ ql.registerHook('beforeDelete', async (ctx) => {
318
+ // Soft delete
319
+ if (ctx.object === 'user') {
320
+ throw new Error('Cannot delete users, use deactivate instead');
321
+ }
322
+ });
323
+ ```
324
+
325
+ ### Transaction Support
326
+
327
+ ```typescript
328
+ // If driver supports transactions
329
+ const driver = ql.getDriver('default');
330
+
331
+ if (driver.supports.transactions) {
332
+ await driver.beginTransaction();
333
+
334
+ try {
335
+ await ql.insert('order', {
336
+ customer_id: 'cust_123',
337
+ total: 100.00
338
+ });
339
+
340
+ await ql.update('inventory', 'inv_456', {
341
+ quantity: { $decrement: 1 }
342
+ });
343
+
344
+ await driver.commitTransaction();
345
+ } catch (error) {
346
+ await driver.rollbackTransaction();
347
+ throw error;
348
+ }
349
+ }
350
+ ```
351
+
352
+ ### Schema Migration
353
+
354
+ ```typescript
355
+ // Add new field to existing object
356
+ const currentSchema = ql.getSchema('todo_task');
357
+
358
+ const updatedSchema = {
359
+ ...currentSchema,
360
+ fields: {
361
+ ...currentSchema.fields,
362
+ assignee: {
363
+ type: 'lookup',
364
+ reference: 'user',
365
+ label: 'Assignee'
366
+ }
367
+ }
368
+ };
369
+
370
+ // Re-register schema
371
+ ql.registerObject(updatedSchema);
372
+
373
+ // Driver might need to sync schema
374
+ const driver = ql.getDriver('default');
375
+ if (driver.syncSchema) {
376
+ await driver.syncSchema('todo_task', updatedSchema);
377
+ }
378
+ ```
379
+
94
380
  ## Roadmap
95
381
 
96
382
  - [x] Basic CRUD
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@objectstack/objectql",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Isomorphic ObjectQL Engine for ObjectStack",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "dependencies": {
8
- "@objectstack/core": "0.9.0",
9
- "@objectstack/spec": "0.9.0",
10
- "@objectstack/types": "0.9.0"
8
+ "@objectstack/core": "0.9.2",
9
+ "@objectstack/spec": "0.9.2",
10
+ "@objectstack/types": "0.9.2"
11
11
  },
12
12
  "devDependencies": {
13
13
  "typescript": "^5.0.0",