Skip to content

Commit 7fd8f1f

Browse files
committed
Document and enforce driver ID compatibility requirements
Updated documentation and driver interface schema to clarify that all drivers must return an 'id' field as a string and must not expose implementation-specific IDs like '_id'. Added comments and examples in both English and Chinese guides, and updated JSDoc in the driver interface schema to enforce this requirement.
1 parent 5533f95 commit 7fd8f1f

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

content/docs/guides/custom-driver.cn.mdx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ description: 学习如何使用驱动接口为 ObjectStack 实现自定义数据
1818

1919
所有驱动程序必须实现规范中定义的 `DriverInterface`
2020

21+
### 重要:ID 兼容性规范
22+
23+
ObjectStack 协议强制要求所有记录必须包含一个类型为 `string``id` 字段。
24+
底层数据库可能使用不同的惯例(例如 MongoDB 使用 `_id`,SQL 使用 `int` 类型的 ID)。
25+
**驱动程序负责将这些转换为标准的字符串 `id`**
26+
27+
- **输入**: 当 `update``delete` 方法接收 `id` 参数时,将其视为唯一主键。
28+
- **输出**: 当 `find``create``update` 返回记录时,确保:
29+
- 必须包含 `id` 字段(字符串)。
30+
- 应移除或映射实现特定的 ID(如 `_id`)。
31+
2132
```typescript
2233
import { DriverInterface, DriverOptions } from '@objectstack/spec';
2334
import { QueryAST, QueryResult } from '@objectstack/objectql';
@@ -49,8 +60,14 @@ export class MyCustomDriver implements DriverInterface {
4960
async find(object: string, query: QueryAST, options?: DriverOptions): Promise<QueryResult> {
5061
// 1. 将 QueryAST 转换为数据库的查询语言(例如 SQL)
5162
// 2. 执行查询
52-
// 3. 返回对象数组结果
53-
return [];
63+
const results = await db.query(...);
64+
65+
// 3. 规范化 ID (MongoDB 风格示例)
66+
return results.map(doc => ({
67+
...doc,
68+
id: doc._id.toString(), // 映射 _id -> id
69+
_id: undefined // 隐藏实现细节
70+
}));
5471
}
5572
```
5673

content/docs/guides/custom-driver.mdx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ In this guide, we will walk through creating a simple **In-Memory Driver** as an
1818

1919
All drivers must implement the `DriverInterface` defined in the specification.
2020

21+
### Important: ID Compatibility
22+
23+
The ObjectStack protocol mandates that all records MUST have an `id` field of type `string`.
24+
Underlying databases might use different conventions (e.g., `_id` in MongoDB, `int` IDs in SQL).
25+
**The Driver is responsible for mapping these to the standard string `id`.**
26+
27+
- **Input:** When receiving an `id` in `update` or `delete`, treat it as the primary key.
28+
- **Output:** When returning records from `find`, `create`, or `update`, ensure:
29+
- There is an `id` field (string).
30+
- Implementation-specific IDs (like `_id`) should be removed or mapped.
31+
2132
```typescript
2233
import { DriverInterface, DriverOptions } from '@objectstack/spec';
2334
import { QueryAST, QueryResult } from '@objectstack/objectql';
@@ -49,8 +60,14 @@ The `find` method receives a `QueryAST` object, which contains structured query
4960
async find(object: string, query: QueryAST, options?: DriverOptions): Promise<QueryResult> {
5061
// 1. Convert QueryAST to your database's query language (e.g., SQL)
5162
// 2. Execute query
52-
// 3. Return results as an array of objects
53-
return [];
63+
const results = await db.query(...);
64+
65+
// 3. Normalize ID (Example for Mongo-like DB)
66+
return results.map(doc => ({
67+
...doc,
68+
id: doc._id.toString(), // Map _id -> id
69+
_id: undefined // Hide implementation details
70+
}));
5471
}
5572
```
5673

packages/driver-memory/src/memory-driver.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export class InMemoryDriver implements DriverInterface {
8787
async create(object: string, data: Record<string, any>, options?: DriverOptions) {
8888
const table = this.getTable(object);
8989

90+
// COMPATIBILITY: Driver must return 'id' as string
9091
const newRecord = {
9192
id: this.generateId(),
9293
...data,

packages/spec/src/system/driver.zod.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ export const DriverInterfaceSchema = z.object({
153153
* sort: [{ field: 'created_at', order: 'desc' }],
154154
* top: 10
155155
* });
156+
* @returns Array of records.
157+
* MUST return `id` as string. MUST NOT return implementation details like `_id`.
156158
*/
157159
find: z.function()
158160
.args(z.string(), QuerySchema, DriverOptionsSchema.optional())
@@ -166,6 +168,8 @@ export const DriverInterfaceSchema = z.object({
166168
* @param object - The name of the object.
167169
* @param query - QueryAST.
168170
* @param options - Driver options.
171+
* @returns The record or null.
172+
* MUST return `id` as string. MUST NOT return implementation details like `_id`.
169173
*/
170174
findOne: z.function()
171175
.args(z.string(), QuerySchema, DriverOptionsSchema.optional())
@@ -179,6 +183,7 @@ export const DriverInterfaceSchema = z.object({
179183
* @param data - Key-value map of field data.
180184
* @param options - Driver options.
181185
* @returns The created record, including server-generated fields (id, created_at, etc.).
186+
* MUST return `id` as string. MUST NOT return implementation details like `_id`.
182187
*/
183188
create: z.function()
184189
.args(z.string(), z.record(z.any()), DriverOptionsSchema.optional())
@@ -193,6 +198,7 @@ export const DriverInterfaceSchema = z.object({
193198
* @param data - The fields to update.
194199
* @param options - Driver options.
195200
* @returns The updated record.
201+
* MUST return `id` as string. MUST NOT return implementation details like `_id`.
196202
*/
197203
update: z.function()
198204
.args(z.string(), z.string().or(z.number()), z.record(z.any()), DriverOptionsSchema.optional())

0 commit comments

Comments
 (0)