Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
31 changes: 31 additions & 0 deletions content/docs/references/data/data-engine.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: Data Engine
description: Data Engine protocol schemas
---

# Data Engine

<Callout type="info">
**Source:** `packages/spec/src/data/data-engine.zod.ts`
</Callout>

## TypeScript Usage

```typescript
import { QueryFilterSchema } from '@objectstack/spec/data';
import type { QueryFilter } from '@objectstack/spec/data';

// Validate data
const result = QueryFilterSchema.parse(data);
```

---

## QueryFilter

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **where** | `any` | optional | |

14 changes: 2 additions & 12 deletions content/docs/references/data/filter.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ description: Filter protocol schemas
## TypeScript Usage

```typescript
import { ComparisonOperatorSchema, EqualityOperatorSchema, FieldOperatorsSchema, FieldReferenceSchema, FilterConditionSchema, NormalizedFilterSchema, QueryFilterSchema, RangeOperatorSchema, SetOperatorSchema, SpecialOperatorSchema, StringOperatorSchema } from '@objectstack/spec/data';
import type { ComparisonOperator, EqualityOperator, FieldOperators, FieldReference, FilterCondition, NormalizedFilter, QueryFilter, RangeOperator, SetOperator, SpecialOperator, StringOperator } from '@objectstack/spec/data';
import { ComparisonOperatorSchema, EqualityOperatorSchema, FieldOperatorsSchema, FieldReferenceSchema, FilterConditionSchema, NormalizedFilterSchema, RangeOperatorSchema, SetOperatorSchema, SpecialOperatorSchema, StringOperatorSchema } from '@objectstack/spec/data';
import type { ComparisonOperator, EqualityOperator, FieldOperators, FieldReference, FilterCondition, NormalizedFilter, RangeOperator, SetOperator, SpecialOperator, StringOperator } from '@objectstack/spec/data';

// Validate data
const result = ComparisonOperatorSchema.parse(data);
Expand Down Expand Up @@ -94,16 +94,6 @@ const result = ComparisonOperatorSchema.parse(data);

---

## QueryFilter

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **where** | `any` | optional | |

---

## RangeOperator

### Properties
Expand Down
55 changes: 55 additions & 0 deletions content/docs/references/system/data-engine.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
title: Data Engine
description: Data Engine protocol schemas
---

# Data Engine

<Callout type="info">
**Source:** `packages/spec/src/system/data-engine.zod.ts`
</Callout>

## TypeScript Usage

```typescript
import { DataEngineSchema, QueryFilterSchema, QueryOptionsSchema } from '@objectstack/spec/system';
import type { DataEngine, QueryFilter, QueryOptions } from '@objectstack/spec/system';

// Validate data
const result = DataEngineSchema.parse(data);
```

---

## DataEngine

Data Engine Interface

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |

---

## QueryFilter

Query filter conditions

---

## QueryOptions

Query options for find operations

### Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| **filter** | `Record<string, any>` | optional | Query filter conditions |
| **select** | `string[]` | optional | |
| **sort** | `Record<string, Enum<'1' \| '-1' \| 'asc' \| 'desc'>>` | optional | |
| **limit** | `number` | optional | |
| **skip** | `number` | optional | |
| **top** | `number` | optional | |

1 change: 1 addition & 0 deletions content/docs/references/system/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ This section contains all protocol schemas for the system layer of ObjectStack.
<Cards>
<Card href="./audit" title="Audit" description="Source: packages/spec/src/system/audit.zod.ts" />
<Card href="./context" title="Context" description="Source: packages/spec/src/system/context.zod.ts" />
<Card href="./data-engine" title="Data Engine" description="Source: packages/spec/src/system/data-engine.zod.ts" />
<Card href="./datasource" title="Datasource" description="Source: packages/spec/src/system/datasource.zod.ts" />
<Card href="./driver" title="Driver" description="Source: packages/spec/src/system/driver.zod.ts" />
<Card href="./events" title="Events" description="Source: packages/spec/src/system/events.zod.ts" />
Expand Down
1 change: 1 addition & 0 deletions content/docs/references/system/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"pages": [
"audit",
"context",
"data-engine",
"datasource",
"driver",
"events",
Expand Down
91 changes: 74 additions & 17 deletions packages/objectql/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { QueryAST, HookContext } from '@objectstack/spec/data';
import { ObjectStackManifest } from '@objectstack/spec/system';
import { DriverInterface, DriverOptions } from '@objectstack/spec/system';
import { IDataEngine, QueryOptions } from '@objectstack/spec/system';
import { SchemaRegistry } from './registry';

// Export Registry for consumers
Expand All @@ -20,8 +21,10 @@ export interface PluginContext {

/**
* ObjectQL Engine
*
* Implements the IDataEngine interface for data persistence.
*/
export class ObjectQL {
export class ObjectQL implements IDataEngine {
private drivers = new Map<string, DriverInterface>();
private defaultDriver: string | null = null;

Expand Down Expand Up @@ -211,27 +214,57 @@ export class ObjectQL {
}

// ============================================
// Data Access Methods
// Data Access Methods (IDataEngine Interface)
// ============================================

async find(object: string, query: any = {}, options?: DriverOptions) {
/**
* Find records matching a query (IDataEngine interface)
*
* @param object - Object name
* @param query - Query options (IDataEngine format)
* @returns Promise resolving to array of records
*/
async find(object: string, query?: QueryOptions): Promise<any[]> {
const driver = this.getDriver(object);

// Normalize QueryAST
let ast: QueryAST;
if (query.where || query.fields || query.orderBy || query.limit) {
ast = { object, ...query } as QueryAST;
} else {
ast = { object, where: query } as QueryAST;
// Convert QueryOptions to QueryAST
let ast: QueryAST = { object };

if (query) {
// Map QueryOptions to QueryAST
if (query.filter) {
ast.where = query.filter;
}
if (query.select) {
ast.fields = query.select;
}
if (query.sort) {
// Convert sort Record to orderBy array
// sort: { createdAt: -1, name: 'asc' } => orderBy: [{ field: 'createdAt', order: 'desc' }, { field: 'name', order: 'asc' }]
ast.orderBy = Object.entries(query.sort).map(([field, order]) => ({
field,
order: (order === -1 || order === 'desc') ? 'desc' : 'asc'
}));
}
// Handle both limit and top (top takes precedence)
if (query.top !== undefined) {
ast.limit = query.top;
} else if (query.limit !== undefined) {
ast.limit = query.limit;
}
if (query.skip !== undefined) {
ast.offset = query.skip;
}
}

// Set default limit if not specified
if (ast.limit === undefined) ast.limit = 100;

// Trigger Before Hook
const hookContext: HookContext = {
object,
event: 'beforeFind',
input: { ast, options }, // Hooks can modify AST here
input: { ast, options: undefined },
ql: this
};
await this.triggerHooks('beforeFind', hookContext);
Expand Down Expand Up @@ -275,7 +308,14 @@ export class ObjectQL {
return driver.findOne(object, ast, options);
}

async insert(object: string, data: Record<string, any>, options?: DriverOptions) {
/**
* Insert a new record (IDataEngine interface)
*
* @param object - Object name
* @param data - Data to insert
* @returns Promise resolving to the created record
*/
async insert(object: string, data: any): Promise<any> {
const driver = this.getDriver(object);

// 1. Get Schema
Expand All @@ -290,7 +330,7 @@ export class ObjectQL {
const hookContext: HookContext = {
object,
event: 'beforeInsert',
input: { data, options },
input: { data, options: undefined },
ql: this
};
await this.triggerHooks('beforeInsert', hookContext);
Expand All @@ -306,13 +346,21 @@ export class ObjectQL {
return hookContext.result;
}

async update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions) {
/**
* Update a record by ID (IDataEngine interface)
*
* @param object - Object name
* @param id - Record ID
* @param data - Updated data
* @returns Promise resolving to the updated record
*/
async update(object: string, id: any, data: any): Promise<any> {
const driver = this.getDriver(object);

const hookContext: HookContext = {
object,
event: 'beforeUpdate',
input: { id, data, options },
input: { id, data, options: undefined },
ql: this
};
await this.triggerHooks('beforeUpdate', hookContext);
Expand All @@ -326,13 +374,20 @@ export class ObjectQL {
return hookContext.result;
}

async delete(object: string, id: string | number, options?: DriverOptions) {
/**
* Delete a record by ID (IDataEngine interface)
*
* @param object - Object name
* @param id - Record ID
* @returns Promise resolving to true if deleted, false otherwise
*/
async delete(object: string, id: any): Promise<boolean> {
const driver = this.getDriver(object);

const hookContext: HookContext = {
object,
event: 'beforeDelete',
input: { id, options },
input: { id, options: undefined },
ql: this
};
await this.triggerHooks('beforeDelete', hookContext);
Expand All @@ -343,6 +398,8 @@ export class ObjectQL {
hookContext.result = result;
await this.triggerHooks('afterDelete', hookContext);

return hookContext.result;
// Return boolean - true if deletion was successful
// The driver.delete should return the deleted record or null
return hookContext.result !== null && hookContext.result !== undefined;
}
}
Loading
Loading