@bairock/lenz 0.0.6 → 0.0.7
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.
- package/README.md +377 -26
- package/dist/engine/CodeGenerator.d.ts +1 -0
- package/dist/engine/CodeGenerator.d.ts.map +1 -1
- package/dist/engine/CodeGenerator.js +231 -46
- package/dist/engine/CodeGenerator.js.map +1 -1
- package/dist/engine/GraphQLParser.d.ts +9 -3
- package/dist/engine/GraphQLParser.d.ts.map +1 -1
- package/dist/engine/GraphQLParser.js +88 -56
- package/dist/engine/GraphQLParser.js.map +1 -1
- package/dist/engine/SchemaValidator.d.ts +4 -0
- package/dist/engine/SchemaValidator.d.ts.map +1 -1
- package/dist/engine/SchemaValidator.js +38 -0
- package/dist/engine/SchemaValidator.js.map +1 -1
- package/dist/engine/directives.d.ts.map +1 -1
- package/dist/engine/directives.js +8 -0
- package/dist/engine/directives.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,10 +6,12 @@ GraphQL-based ORM for MongoDB (Prisma style)
|
|
|
6
6
|
|
|
7
7
|
- ✅ **GraphQL SDL Schema** - Define models using GraphQL syntax
|
|
8
8
|
- ✅ **TypeScript First** - Full type safety and autocompletion
|
|
9
|
-
- ✅ **MongoDB Native** - Leverage all MongoDB features
|
|
9
|
+
- ✅ **MongoDB Native** - Leverage all MongoDB features including aggregation
|
|
10
10
|
- ✅ **Prisma Style** - Familiar configuration and client generation
|
|
11
11
|
- ✅ **Auto-generated Client** - Generate TypeScript client from GraphQL schema
|
|
12
12
|
- ✅ **Relations Support** - One-to-One, One-to-Many, Many-to-Many
|
|
13
|
+
- ✅ **Smart Loading Strategies** - Automatic choice between populate (separate queries) and lookup (server-side joins)
|
|
14
|
+
- ✅ **Automatic Indexing** - Intelligent index creation for foreign key fields
|
|
13
15
|
- ✅ **Transactions** - ACID transactions with MongoDB
|
|
14
16
|
|
|
15
17
|
## Quick Start
|
|
@@ -41,8 +43,15 @@ type User @model {
|
|
|
41
43
|
id: ID! @id
|
|
42
44
|
email: String! @unique
|
|
43
45
|
name: String!
|
|
46
|
+
|
|
47
|
+
# One-to-many relationship (auto-selects lookup strategy)
|
|
44
48
|
posts: [Post!]! @relation(field: "postIds")
|
|
45
|
-
postIds: [ID!]!
|
|
49
|
+
postIds: [ID!]! # Array automatically indexed
|
|
50
|
+
|
|
51
|
+
# One-to-one relationship (auto-selects populate strategy)
|
|
52
|
+
profile: Profile @relation(field: "profileId")
|
|
53
|
+
profileId: ID # Sparse index automatically created
|
|
54
|
+
|
|
46
55
|
createdAt: DateTime! @createdAt
|
|
47
56
|
updatedAt: DateTime! @updatedAt
|
|
48
57
|
}
|
|
@@ -51,8 +60,28 @@ type Post @model {
|
|
|
51
60
|
id: ID! @id
|
|
52
61
|
title: String!
|
|
53
62
|
content: String
|
|
63
|
+
|
|
64
|
+
# Many-to-one relationship (auto-selects populate strategy)
|
|
54
65
|
author: User! @relation(field: "authorId")
|
|
55
|
-
authorId: ID!
|
|
66
|
+
authorId: ID! # Foreign key index automatically created
|
|
67
|
+
|
|
68
|
+
# Many-to-many relationship with ID arrays (auto-selects lookup strategy)
|
|
69
|
+
categories: [Category!]! @relation(field: "categoryIds")
|
|
70
|
+
categoryIds: [ID!]! # Multikey index automatically created
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
type Profile @model {
|
|
74
|
+
id: ID! @id
|
|
75
|
+
bio: String
|
|
76
|
+
user: User @relation(field: "userId", strategy: "populate")
|
|
77
|
+
userId: ID
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
type Category @model {
|
|
81
|
+
id: ID! @id
|
|
82
|
+
name: String! @unique
|
|
83
|
+
posts: [Post!]! @relation(field: "postIds", strategy: "lookup", index: false)
|
|
84
|
+
postIds: [ID!]! # No auto-index (index: false)
|
|
56
85
|
}
|
|
57
86
|
```
|
|
58
87
|
|
|
@@ -72,12 +101,13 @@ import { LenzClient } from '../generated/lenz/client';
|
|
|
72
101
|
async function main() {
|
|
73
102
|
const lenz = new LenzClient({
|
|
74
103
|
url: process.env.MONGODB_URI,
|
|
75
|
-
database: 'myapp'
|
|
104
|
+
database: 'myapp',
|
|
105
|
+
log: ['query', 'info'] // Enable query logging
|
|
76
106
|
});
|
|
77
107
|
|
|
78
108
|
await lenz.$connect();
|
|
79
109
|
|
|
80
|
-
// Create a user
|
|
110
|
+
// Create a user with posts (populate strategy for posts)
|
|
81
111
|
const user = await lenz.user.create({
|
|
82
112
|
data: {
|
|
83
113
|
email: 'alice@example.com',
|
|
@@ -89,19 +119,95 @@ async function main() {
|
|
|
89
119
|
}
|
|
90
120
|
},
|
|
91
121
|
include: {
|
|
92
|
-
posts: true
|
|
122
|
+
posts: true, // Uses populate strategy (separate queries)
|
|
123
|
+
profile: true // Uses populate strategy (one-to-one)
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Query with include - automatic strategy selection
|
|
128
|
+
const authorWithBooks = await lenz.author.findUnique({
|
|
129
|
+
where: { id: 'some-author-id' },
|
|
130
|
+
include: {
|
|
131
|
+
books: true // Uses lookup strategy (one-to-many, single query)
|
|
93
132
|
}
|
|
94
133
|
});
|
|
95
134
|
|
|
96
|
-
//
|
|
97
|
-
|
|
135
|
+
// Manual array synchronization for lookup strategy
|
|
136
|
+
// When creating a book, add its ID to author.bookIds
|
|
137
|
+
const newBook = await lenz.book.create({
|
|
138
|
+
data: {
|
|
139
|
+
title: 'New Book',
|
|
140
|
+
authorId: 'author-id'
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Manually update the author's bookIds array
|
|
145
|
+
await lenz.author.update({
|
|
146
|
+
where: { id: 'author-id' },
|
|
147
|
+
data: {
|
|
148
|
+
bookIds: {
|
|
149
|
+
push: newBook.id // Add new book ID to array
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Complex query with filtering and sorting
|
|
155
|
+
const posts = await lenz.post.findMany({
|
|
98
156
|
where: {
|
|
99
|
-
|
|
100
|
-
|
|
157
|
+
title: { contains: 'tutorial' },
|
|
158
|
+
categories: {
|
|
159
|
+
some: { name: { equals: 'Programming' } }
|
|
101
160
|
}
|
|
161
|
+
},
|
|
162
|
+
orderBy: { createdAt: 'desc' },
|
|
163
|
+
take: 10,
|
|
164
|
+
include: {
|
|
165
|
+
author: true, // populate strategy
|
|
166
|
+
categories: true // many-to-many lookup strategy (server-side join with $lookup)
|
|
102
167
|
}
|
|
103
168
|
});
|
|
104
169
|
|
|
170
|
+
// Transaction example (requires replica set)
|
|
171
|
+
await lenz.$transaction(async (tx) => {
|
|
172
|
+
const updatedUser = await lenz.user.update({
|
|
173
|
+
where: { id: user.id },
|
|
174
|
+
data: { name: 'Alice Updated' }
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await lenz.post.create({
|
|
178
|
+
data: {
|
|
179
|
+
title: 'Transaction Post',
|
|
180
|
+
authorId: user.id
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Advanced many-to-many lookup with filtering
|
|
186
|
+
// Uses MongoDB aggregation pipeline for server-side joins
|
|
187
|
+
const postsWithTechCategories = await lenz.post.findMany({
|
|
188
|
+
where: {
|
|
189
|
+
categories: {
|
|
190
|
+
some: {
|
|
191
|
+
name: { equals: 'Technology' }
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
include: {
|
|
196
|
+
categories: {
|
|
197
|
+
where: {
|
|
198
|
+
name: { equals: 'Technology' }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
orderBy: { createdAt: 'desc' },
|
|
203
|
+
take: 5
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// Note: For lookup strategy, array synchronization is manual
|
|
207
|
+
// When creating relationships, update both arrays:
|
|
208
|
+
// await lenz.post.update({ where: { id: postId }, data: { categoryIds: { push: categoryId } } });
|
|
209
|
+
// await lenz.category.update({ where: { id: categoryId }, data: { postIds: { push: postId } } });
|
|
210
|
+
|
|
105
211
|
await lenz.$disconnect();
|
|
106
212
|
}
|
|
107
213
|
```
|
|
@@ -143,13 +249,35 @@ Lenz extends GraphQL with custom directives:
|
|
|
143
249
|
- `@unique` - Creates a unique index
|
|
144
250
|
- `@index` - Creates a regular index
|
|
145
251
|
- `@default(value: "...")` - Sets default value
|
|
146
|
-
- `@relation(field: "...")` - Defines relation
|
|
252
|
+
- `@relation(field: "...", strategy: "populate|lookup", index: true|false)` - Defines relation with loading strategy and index control
|
|
147
253
|
- `@createdAt` - Auto-sets creation timestamp
|
|
148
254
|
- `@updatedAt` - Auto-updates timestamp
|
|
255
|
+
- `@embedded` - Marks a type as embedded document
|
|
256
|
+
- `@hide` - Excludes field from query results by default
|
|
149
257
|
|
|
150
258
|
## Relations
|
|
151
259
|
|
|
152
|
-
Lenz
|
|
260
|
+
Lenz implements MongoDB-native relations with explicit foreign key fields and intelligent loading strategies. Each relation type has an optimal default strategy for loading related data (see [Relation Loading Strategies](#relation-loading-strategies) for details).
|
|
261
|
+
|
|
262
|
+
**Key principles:**
|
|
263
|
+
- Foreign keys must be in the **source model** (the model containing `@relation`)
|
|
264
|
+
- Arrays are automatically initialized for required array fields
|
|
265
|
+
- Indexes are automatically created for foreign key fields (configurable)
|
|
266
|
+
- Loading strategy is automatically selected based on relation type
|
|
267
|
+
|
|
268
|
+
### Automatic Array Initialization
|
|
269
|
+
|
|
270
|
+
Required array fields (like `bookIds: [ID!]!`) are automatically initialized with empty arrays `[]` when creating documents. This prevents MongoDB `$in needs an array` errors when querying relations.
|
|
271
|
+
|
|
272
|
+
```typescript
|
|
273
|
+
// When creating a document, required arrays are automatically set to []
|
|
274
|
+
const author = await lenz.author.create({
|
|
275
|
+
data: {
|
|
276
|
+
name: 'John Doe',
|
|
277
|
+
// bookIds is automatically set to [] even if not provided
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
```
|
|
153
281
|
|
|
154
282
|
### One-to-Many / Many-to-One
|
|
155
283
|
A bidirectional relationship where one side has an array and the other has a single reference:
|
|
@@ -157,52 +285,203 @@ A bidirectional relationship where one side has an array and the other has a sin
|
|
|
157
285
|
```graphql
|
|
158
286
|
type Author @model {
|
|
159
287
|
id: ID! @id
|
|
160
|
-
books: [Book!]! @relation(field: "bookIds") # one-to-many side
|
|
161
|
-
bookIds: [ID!]! # array
|
|
288
|
+
books: [Book!]! @relation(field: "bookIds") # one-to-many side, auto: lookup strategy
|
|
289
|
+
bookIds: [ID!]! # array automatically indexed (multikey index)
|
|
162
290
|
}
|
|
163
291
|
|
|
164
292
|
type Book @model {
|
|
165
293
|
id: ID! @id
|
|
166
|
-
author: Author! @relation(field: "authorId") # many-to-one side
|
|
167
|
-
authorId: ID! # single
|
|
294
|
+
author: Author! @relation(field: "authorId") # many-to-one side, auto: populate strategy
|
|
295
|
+
authorId: ID! # single ID automatically indexed (sparse index)
|
|
168
296
|
}
|
|
169
297
|
```
|
|
170
298
|
|
|
171
|
-
- **Author → Book (one-to-many):**
|
|
172
|
-
- **Book → Author (many-to-one):** Foreign key `authorId`
|
|
299
|
+
- **Author → Book (one-to-many):** Uses `lookup` strategy by default (server-side join). Requires manual synchronization of `bookIds` array.
|
|
300
|
+
- **Book → Author (many-to-one):** Uses `populate` strategy by default (separate query). Foreign key `authorId` has automatic sparse index.
|
|
301
|
+
- **Indexes:** Multikey index on `bookIds`, sparse index on `authorId` (created automatically).
|
|
173
302
|
|
|
174
303
|
### One-to-One (foreign key single ID in source model)
|
|
304
|
+
|
|
175
305
|
```graphql
|
|
176
306
|
type User @model {
|
|
177
307
|
id: ID! @id
|
|
178
|
-
profile: Profile @relation(field: "profileId")
|
|
179
|
-
profileId: ID
|
|
308
|
+
profile: Profile @relation(field: "profileId") # auto: populate strategy
|
|
309
|
+
profileId: ID # optional, sparse index
|
|
180
310
|
}
|
|
181
311
|
|
|
182
312
|
type Profile @model {
|
|
183
313
|
id: ID! @id
|
|
184
|
-
user: User @relation(field: "userId")
|
|
185
|
-
userId: ID
|
|
314
|
+
user: User @relation(field: "userId", strategy: "populate") # explicit populate
|
|
315
|
+
userId: ID # optional, sparse index
|
|
186
316
|
}
|
|
187
317
|
```
|
|
188
318
|
|
|
319
|
+
- **Strategy:** Uses `populate` by default (separate queries for each side)
|
|
320
|
+
- **Indexes:** Sparse indexes on `profileId` and `userId` (optional fields)
|
|
321
|
+
- **Optional:** Both sides can be optional (nullable IDs)
|
|
322
|
+
- **Bidirectional:** Each side maintains its own foreign key
|
|
323
|
+
|
|
189
324
|
### Many-to-Many (ID arrays on both sides)
|
|
325
|
+
|
|
190
326
|
```graphql
|
|
191
327
|
type Post @model {
|
|
192
328
|
id: ID! @id
|
|
193
|
-
categories: [Category!]! @relation(field: "categoryIds")
|
|
194
|
-
categoryIds: [ID!]!
|
|
329
|
+
categories: [Category!]! @relation(field: "categoryIds") # auto: lookup strategy (default for arrays)
|
|
330
|
+
categoryIds: [ID!]! # multikey index
|
|
195
331
|
}
|
|
196
332
|
|
|
197
333
|
type Category @model {
|
|
198
334
|
id: ID! @id
|
|
199
|
-
posts: [Post!]! @relation(field: "postIds")
|
|
200
|
-
postIds: [ID!]!
|
|
335
|
+
posts: [Post!]! @relation(field: "postIds", strategy: "lookup", index: false)
|
|
336
|
+
postIds: [ID!]! # no auto-index (index: false)
|
|
201
337
|
}
|
|
202
338
|
```
|
|
203
339
|
|
|
340
|
+
- **Strategy:** Uses `lookup` by default when foreign key arrays are specified (server-side joins with MongoDB's `$lookup` aggregation), `populate` for join collections
|
|
341
|
+
- **Indexes:** Multikey indexes on both array fields (configurable with `index: false`)
|
|
342
|
+
- **Bidirectional:** Both sides maintain arrays of IDs
|
|
343
|
+
- **Manual sync:** You must manually synchronize both arrays when creating/updating relations (for `lookup` strategy)
|
|
344
|
+
|
|
345
|
+
**Example - Manual synchronization for many-to-many lookup strategy:**
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// Create a post and category
|
|
349
|
+
const post = await lenz.post.create({
|
|
350
|
+
data: {
|
|
351
|
+
title: 'My Post',
|
|
352
|
+
categoryIds: [] // Initialize empty array
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
const category = await lenz.category.create({
|
|
357
|
+
data: {
|
|
358
|
+
name: 'Technology',
|
|
359
|
+
postIds: [] // Initialize empty array
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Add category to post
|
|
364
|
+
await lenz.post.update({
|
|
365
|
+
where: { id: post.id },
|
|
366
|
+
data: {
|
|
367
|
+
categoryIds: {
|
|
368
|
+
push: category.id // Add category ID to post's array
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Add post to category (bidirectional sync)
|
|
374
|
+
await lenz.category.update({
|
|
375
|
+
where: { id: category.id },
|
|
376
|
+
data: {
|
|
377
|
+
postIds: {
|
|
378
|
+
push: post.id // Add post ID to category's array
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
```
|
|
383
|
+
|
|
204
384
|
**Important:** Foreign keys must always be in the **source model** (the model containing the `@relation` directive). Classic one-to-many patterns with foreign keys in target models will cause validation errors.
|
|
205
385
|
|
|
386
|
+
## Relation Loading Strategies
|
|
387
|
+
|
|
388
|
+
Lenz automatically chooses the optimal strategy for loading related data, balancing performance and simplicity. You can also explicitly override the strategy.
|
|
389
|
+
|
|
390
|
+
### Populate Strategy (Default for oneToOne, manyToOne)
|
|
391
|
+
|
|
392
|
+
Uses separate queries - first fetches the main document, then queries related collections. Best for simple relationships and sharded environments.
|
|
393
|
+
|
|
394
|
+
```graphql
|
|
395
|
+
type User @model {
|
|
396
|
+
profile: Profile @relation(field: "profileId", strategy: "populate")
|
|
397
|
+
profileId: ID
|
|
398
|
+
}
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Lookup Strategy (Default for oneToMany)
|
|
402
|
+
|
|
403
|
+
Uses MongoDB's `$lookup` aggregation operator for server-side joins. Best for high-read scenarios with bidirectional relationships.
|
|
404
|
+
|
|
405
|
+
```graphql
|
|
406
|
+
type Author @model {
|
|
407
|
+
books: [Book!]! @relation(field: "bookIds", strategy: "lookup")
|
|
408
|
+
bookIds: [ID!]!
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**Note:** When using `lookup` strategy, you must manually synchronize ID arrays (e.g., `bookIds`) when creating/updating/deleting related documents.
|
|
413
|
+
|
|
414
|
+
**Include options support:** Lookup strategy now supports `where`, `orderBy`, `take`, and `skip` options for filtering, sorting, and paginating related documents.
|
|
415
|
+
|
|
416
|
+
Example:
|
|
417
|
+
```typescript
|
|
418
|
+
const author = await lenz.author.findUnique({
|
|
419
|
+
where: { id: 'author-id' },
|
|
420
|
+
include: {
|
|
421
|
+
books: {
|
|
422
|
+
where: { published: true },
|
|
423
|
+
orderBy: { createdAt: 'desc' },
|
|
424
|
+
take: 5
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
### Automatic Strategy Selection
|
|
431
|
+
|
|
432
|
+
| Relation Type | Default Strategy | Reason |
|
|
433
|
+
|--------------|------------------|--------|
|
|
434
|
+
| `oneToOne` | `populate` | Simple relationships, no arrays |
|
|
435
|
+
| `manyToOne` | `populate` | Single reference, no arrays |
|
|
436
|
+
| `oneToMany` | `lookup` | Array of IDs in source document (e.g., Author.bookIds) |
|
|
437
|
+
| `manyToMany` | `lookup` (if foreign key array specified) or `populate` (if join collection) | Foreign key arrays use server-side joins, join collections use separate queries |
|
|
438
|
+
|
|
439
|
+
**Note:** The lookup strategy now supports include options (where, orderBy, take, skip) for both array foreign keys and single foreign key relations. However, nested includes are not yet supported for lookup strategy but are fully supported for populate strategy.
|
|
440
|
+
|
|
441
|
+
**Many-to-Many Lookup Strategy:**
|
|
442
|
+
|
|
443
|
+
When a many-to-many relationship uses foreign key arrays (either single-sided or both sides), Lenz automatically uses the `lookup` strategy with MongoDB's `$lookup` aggregation operator. This performs server-side joins for optimal read performance. For example, `post.categoryIds` array referencing categories.
|
|
444
|
+
|
|
445
|
+
```graphql
|
|
446
|
+
type Post @model {
|
|
447
|
+
categories: [Category!]! @relation(field: "categoryIds", strategy: "lookup")
|
|
448
|
+
categoryIds: [ID!]! # multikey index automatically created
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
type Category @model {
|
|
452
|
+
posts: [Post!]! @relation(field: "postIds", strategy: "lookup", index: false)
|
|
453
|
+
postIds: [ID!]! # no auto-index (index: false)
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
**Many-to-Many Populate Strategy:**
|
|
458
|
+
|
|
459
|
+
When a many-to-many relationship uses a join collection (no foreign key arrays), Lenz uses the `populate` strategy with separate queries.
|
|
460
|
+
|
|
461
|
+
```graphql
|
|
462
|
+
type Post @model {
|
|
463
|
+
categories: [Category!]! @relation # no field parameter → join collection
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
type Category @model {
|
|
467
|
+
posts: [Post!]! @relation
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Automatic Indexing
|
|
472
|
+
|
|
473
|
+
Lenz automatically creates indexes for foreign key fields when `index: true` (default). You can disable this:
|
|
474
|
+
|
|
475
|
+
```graphql
|
|
476
|
+
type Author @model {
|
|
477
|
+
books: [Book!]! @relation(field: "bookIds", index: false)
|
|
478
|
+
bookIds: [ID!]!
|
|
479
|
+
}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
- For arrays (oneToMany): Creates multikey indexes
|
|
483
|
+
- For single IDs (oneToOne, manyToOne): Creates sparse indexes
|
|
484
|
+
|
|
206
485
|
## Supported Field Types
|
|
207
486
|
|
|
208
487
|
- `String`
|
|
@@ -215,6 +494,78 @@ type Category @model {
|
|
|
215
494
|
- `Json` (any JSON value)
|
|
216
495
|
- `ObjectId` (MongoDB ObjectId as string)
|
|
217
496
|
|
|
497
|
+
## Best Practices with MongoDB
|
|
498
|
+
|
|
499
|
+
### 1. Schema Design
|
|
500
|
+
- **Embed documents** when data is accessed together frequently (use `@embedded` directive)
|
|
501
|
+
- **Reference documents** when data is large or accessed independently
|
|
502
|
+
- **Use arrays judiciously** - large arrays can impact performance
|
|
503
|
+
- **Denormalize carefully** - duplicate data for read performance, but maintain consistency
|
|
504
|
+
|
|
505
|
+
### 2. Index Strategy
|
|
506
|
+
- **Auto-index foreign keys** - Lenz does this by default with `index: true`
|
|
507
|
+
- **Add compound indexes** for frequently queried field combinations
|
|
508
|
+
- **Use sparse indexes** for optional fields (auto-created for optional relations)
|
|
509
|
+
- **Monitor index usage** with `$indexStats`
|
|
510
|
+
|
|
511
|
+
### 3. Performance Optimization
|
|
512
|
+
- **Use `lookup` strategy** for high-read bidirectional relationships
|
|
513
|
+
- **Use `populate` strategy** for simple relationships and sharded clusters
|
|
514
|
+
- **Batch operations** with `createMany`, `updateMany` instead of individual calls
|
|
515
|
+
- **Project only needed fields** with `select` option
|
|
516
|
+
|
|
517
|
+
### 4. Query Patterns
|
|
518
|
+
- **Filter early** - push filters to database with `where` clauses
|
|
519
|
+
- **Avoid large skip/limit** - use cursor-based pagination (`cursor` option)
|
|
520
|
+
- **Use transactions** for multi-document consistency (requires replica set)
|
|
521
|
+
- **Monitor slow queries** with MongoDB profiler
|
|
522
|
+
|
|
523
|
+
### 5. Data Consistency
|
|
524
|
+
- **Manual array sync** - when using `lookup` strategy, maintain ID arrays
|
|
525
|
+
- **Use default values** for required fields to avoid validation errors
|
|
526
|
+
- **Handle race conditions** with optimistic concurrency or transactions
|
|
527
|
+
- **Implement soft deletes** with `deletedAt` field instead of hard deletes
|
|
528
|
+
|
|
529
|
+
### 6. Many-to-Many Performance Optimization
|
|
530
|
+
- **Array size limits:** Keep array sizes reasonable (<1000 elements). Large arrays increase `$lookup` complexity and memory usage.
|
|
531
|
+
- **Batch synchronization:** When updating multiple relationships, batch array operations to reduce database round trips.
|
|
532
|
+
- **Index management:** Regularly monitor and optimize multikey indexes for array fields.
|
|
533
|
+
- **Alternative approaches:** For extremely large many-to-many relationships, consider:
|
|
534
|
+
- **Denormalization:** Embed frequently accessed data
|
|
535
|
+
- **Hybrid approach:** Use `lookup` for recent/active relationships, `populate` for historical
|
|
536
|
+
- **Materialized views:** Pre-compute relationships for read-heavy scenarios
|
|
537
|
+
|
|
538
|
+
### 7. When to Use Each Strategy
|
|
539
|
+
|
|
540
|
+
#### Use **Populate** when:
|
|
541
|
+
- Simple one-to-one or many-to-one relationships
|
|
542
|
+
- Working with sharded clusters (`$lookup` doesn't work across shards)
|
|
543
|
+
- Relationships are rarely accessed
|
|
544
|
+
- You prefer automatic data consistency
|
|
545
|
+
- Many-to-many relationships with join collections (no foreign key arrays)
|
|
546
|
+
- Small datasets where separate queries are acceptable
|
|
547
|
+
|
|
548
|
+
#### Use **Lookup** when:
|
|
549
|
+
- High-read scenarios with one-to-many or many-to-many relationships
|
|
550
|
+
- Need server-side joins for complex filtering/sorting
|
|
551
|
+
- Willing to manually maintain ID arrays
|
|
552
|
+
- Maximum read performance is critical
|
|
553
|
+
- Many-to-many relationships with foreign key arrays (both sides)
|
|
554
|
+
- Medium to large datasets where join performance matters
|
|
555
|
+
|
|
556
|
+
#### Special Considerations for Many-to-Many Lookup:
|
|
557
|
+
- **Array size:** Large arrays (>1000 IDs) can impact `$lookup` performance. Consider denormalization or hybrid approaches.
|
|
558
|
+
- **Indexing:** Multikey indexes are essential for array fields. Monitor index size and performance.
|
|
559
|
+
- **Consistency:** Manual array synchronization requires careful application logic to maintain data integrity.
|
|
560
|
+
- **Sharding:** `$lookup` doesn't work across shards. For sharded clusters, use `populate` strategy or ensure related documents are on the same shard.
|
|
561
|
+
|
|
562
|
+
### 8. Production Readiness
|
|
563
|
+
- **Enable replica set** for transaction support
|
|
564
|
+
- **Set up proper connection pooling** in `lenz.config.ts`
|
|
565
|
+
- **Implement retry logic** for transient failures
|
|
566
|
+
- **Use environment-specific configurations**
|
|
567
|
+
- **Monitor connection health** with regular `ping` commands
|
|
568
|
+
|
|
218
569
|
## CLI Commands
|
|
219
570
|
|
|
220
571
|
```bash
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeGenerator.d.ts","sourceRoot":"","sources":["../../src/engine/CodeGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAG3E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAUb;IAEF,OAAO,CAAC,6BAA6B;IAyBrC,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,wBAAwB;IA4IhC,OAAO,CAAC,oBAAoB;IAM5B,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc;IAmClD,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,cAAc;IAwLtB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,aAAa;IAiKrB,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,yBAAyB;IA4LjC,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,oBAAoB;IA2N5B,OAAO,CAAC,wBAAwB;IAiEhC,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,qBAAqB;
|
|
1
|
+
{"version":3,"file":"CodeGenerator.d.ts","sourceRoot":"","sources":["../../src/engine/CodeGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAG3E,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAUb;IAEF,OAAO,CAAC,6BAA6B;IAyBrC,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,wBAAwB;IA4IhC,OAAO,CAAC,oBAAoB;IAM5B,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc;IAmClD,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,cAAc;IAwLtB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,aAAa;IAiKrB,OAAO,CAAC,iBAAiB;IA+BzB,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,aAAa;IAiBrB,OAAO,CAAC,yBAAyB;IA4LjC,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,oBAAoB;IA2N5B,OAAO,CAAC,wBAAwB;IAiEhC,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,qBAAqB;IAqb7B,OAAO,CAAC,6BAA6B;IAuOrC,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,gBAAgB;CAGzB;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5B"}
|