Skip to content

Commit dced650

Browse files
Copilothotlong
andcommitted
Add full backward compatibility for _id in MongoDB driver queries
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent 0f2d27d commit dced650

4 files changed

Lines changed: 97 additions & 15 deletions

File tree

docs/guide/drivers/mongo.md

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,25 +85,52 @@ const users = await app.find('users', query);
8585

8686
### Migration from Legacy Code
8787

88-
If you have existing code using `_id`, you should migrate to using `id` for consistency:
88+
If you have existing code using `_id`, you have two options:
89+
90+
1. **Recommended: Migrate to `id`** for consistency across drivers:
8991

9092
**Before (Legacy):**
9193
```typescript
9294
// ❌ Old way - inconsistent with SQL drivers
9395
const query = {
94-
filters: [['_id', '=', '507f1f77bcf86cd799439011']]
96+
filters: [['_id', '=', '507f1f77bcf86cd799439011']],
97+
sort: [['_id', 'desc']],
98+
fields: ['_id', 'name']
9599
};
96100
```
97101

98102
**After (Recommended):**
99103
```typescript
100104
// ✅ New way - consistent across all drivers
101105
const query = {
102-
filters: [['id', '=', '507f1f77bcf86cd799439011']]
106+
filters: [['id', '=', '507f1f77bcf86cd799439011']],
107+
sort: [['id', 'desc']],
108+
fields: ['id', 'name']
103109
};
104110
```
105111

106-
**Note:** The MongoDB driver still accepts `_id` in filters for backward compatibility, but using `id` is strongly recommended for new code.
112+
2. **Backward Compatible Mode:** Continue using `_id` in queries (results still return `id`)
113+
114+
The MongoDB driver **fully supports `_id` in filters, sorting, and field projections** for backward compatibility:
115+
116+
```typescript
117+
// ✅ This works - backward compatible
118+
const query = {
119+
filters: [['_id', '=', '507f1f77bcf86cd799439011']],
120+
sort: [['_id', 'desc']],
121+
fields: ['_id', 'name']
122+
};
123+
const users = await app.find('users', query);
124+
125+
// Results ALWAYS use 'id' regardless of query field name
126+
console.log(users[0].id); // '507f1f77bcf86cd799439011'
127+
console.log(users[0]._id); // undefined
128+
```
129+
130+
**Key Points:**
131+
- Queries accept both `id` and `_id` (automatically mapped to MongoDB's `_id`)
132+
- Results always return `id` field (never `_id`)
133+
- Using `id` is recommended for database portability
107134

108135
## ID Generation
109136

docs/guide/migration-id-field.md

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,25 +133,34 @@ find src/ -type f -name "*.ts" -exec sed -i "s/\['_id'/\['id'/g" {} +
133133

134134
### MongoDB Driver
135135

136-
The MongoDB driver maintains backward compatibility:
136+
The MongoDB driver maintains **full backward compatibility** for `_id` usage:
137137

138-
- `_id` in filters still works (mapped to `id` internally)
139-
- `_id` in create operations is mapped to `id`
140-
- Results always return `id` (not `_id`)
138+
- `_id` in **filters** is automatically mapped to MongoDB's `_id`
139+
- `_id` in **sorting** is automatically mapped to MongoDB's `_id`
140+
- `_id` in **field projections** is automatically mapped to MongoDB's `_id`
141+
- `_id` in **create operations** is mapped to `id`
142+
- Results **always** return `id` (not `_id`)
141143

142144
**Example:**
143145
```typescript
144-
// Legacy code - still works but not recommended
146+
// Legacy code - fully supported for backward compatibility
145147
const query = {
146-
filters: [['_id', '=', 'user-123']]
148+
filters: [['_id', '=', 'user-123']],
149+
sort: [['_id', 'desc']],
150+
fields: ['_id', 'name', 'email']
147151
};
148152
const users = await app.find('users', query);
149153

150-
// Result uses 'id' (not '_id')
154+
// Results use 'id' (not '_id')
151155
console.log(users[0].id); // 'user-123'
152156
console.log(users[0]._id); // undefined
153157
```
154158

159+
**No Breaking Changes:** Your existing queries using `_id` will continue to work without modification. However, migrating to `id` is recommended for:
160+
- Consistency with SQL drivers
161+
- Database portability
162+
- Future-proofing your code
163+
155164
### SQL Driver
156165

157166
The SQL driver has always used `id`, so no migration needed for SQL-only codebases.

packages/drivers/mongo/src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,16 @@ export class MongoDriver implements Driver {
136136
// map [['field', 'desc']] to { field: -1 }
137137
findOptions.sort = {};
138138
for (const [field, order] of query.sort) {
139-
// Map 'id' to '_id' for sorting
140-
const dbField = field === 'id' ? '_id' : field;
139+
// Map both 'id' and '_id' to '_id' for backward compatibility
140+
const dbField = (field === 'id' || field === '_id') ? '_id' : field;
141141
(findOptions.sort as any)[dbField] = order === 'desc' ? -1 : 1;
142142
}
143143
}
144144
if (query.fields && query.fields.length > 0) {
145145
findOptions.projection = {};
146146
for (const field of query.fields) {
147-
// Map 'id' to '_id' for projection
148-
const dbField = field === 'id' ? '_id' : field;
147+
// Map both 'id' and '_id' to '_id' for backward compatibility
148+
const dbField = (field === 'id' || field === '_id') ? '_id' : field;
149149
(findOptions.projection as any)[dbField] = 1;
150150
}
151151
}

packages/drivers/mongo/test/index.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,4 +174,50 @@ describe('MongoDriver', () => {
174174
expect(result).toHaveProperty('name', 'Charlie');
175175
});
176176

177+
// Backward compatibility tests for legacy '_id' usage
178+
it('should accept "_id" field in filters for backward compatibility', async () => {
179+
const query = {
180+
filters: [['_id', '=', '12345']]
181+
};
182+
await driver.find('users', query);
183+
184+
expect(mockCollection.find).toHaveBeenCalledWith(
185+
{ _id: '12345' },
186+
expect.any(Object)
187+
);
188+
});
189+
190+
it('should accept "_id" in sorting for backward compatibility', async () => {
191+
const query = {
192+
sort: [['_id', 'asc']]
193+
};
194+
await driver.find('users', query);
195+
196+
expect(mockCollection.find).toHaveBeenCalledWith(
197+
{},
198+
expect.objectContaining({
199+
sort: { _id: 1 }
200+
})
201+
);
202+
});
203+
204+
it('should accept "_id" in field projection for backward compatibility', async () => {
205+
mockCollection.toArray.mockResolvedValueOnce([{ _id: '456', name: 'Dave' }]);
206+
207+
const query = {
208+
fields: ['_id', 'name']
209+
};
210+
const results = await driver.find('users', query);
211+
212+
expect(mockCollection.find).toHaveBeenCalledWith(
213+
{},
214+
expect.objectContaining({
215+
projection: { _id: 1, name: 1 }
216+
})
217+
);
218+
219+
// Should still return 'id' instead of '_id' in results
220+
expect(results[0]).toEqual({ id: '456', name: 'Dave' });
221+
});
222+
177223
});

0 commit comments

Comments
 (0)