omgkit 2.2.0 → 2.3.0

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 (55) hide show
  1. package/package.json +1 -1
  2. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  3. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  4. package/plugin/skills/databases/redis/SKILL.md +53 -860
  5. package/plugin/skills/devops/aws/SKILL.md +68 -672
  6. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  7. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  8. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  9. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  10. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  11. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  12. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  13. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  14. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  15. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  16. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  17. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  18. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  19. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  20. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  21. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  22. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  23. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  24. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  25. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  26. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  27. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  28. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  29. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  30. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  31. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  32. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  33. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  34. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  35. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  36. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  37. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  38. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  39. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  40. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  41. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  42. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  43. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  44. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  45. package/plugin/skills/security/oauth/SKILL.md +80 -934
  46. package/plugin/skills/security/owasp/SKILL.md +78 -862
  47. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  48. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  49. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  50. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  51. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  52. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  53. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  54. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  55. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,812 +1,96 @@
1
1
  ---
2
- name: mongodb
3
- description: MongoDB NoSQL database with document modeling, aggregation pipelines, indexing, and Mongoose ODM
4
- category: databases
5
- triggers:
6
- - mongodb
7
- - mongo
8
- - mongoose
9
- - nosql
10
- - document database
11
- - bson
12
- - aggregation
2
+ name: Developing with MongoDB
3
+ description: The agent implements MongoDB NoSQL database solutions with document modeling, aggregation pipelines, and Mongoose ODM. Use when building document-based applications, designing schemas, writing aggregations, or implementing NoSQL patterns.
13
4
  ---
14
5
 
15
- # MongoDB
6
+ # Developing with MongoDB
16
7
 
17
- Enterprise-grade **MongoDB NoSQL database** development following industry best practices. This skill covers document modeling, CRUD operations, aggregation pipelines, indexing strategies, Mongoose ODM, transactions, and production-ready patterns used by top engineering teams.
18
-
19
- ## Purpose
20
-
21
- Build scalable document-based applications:
22
-
23
- - Design effective document schemas
24
- - Implement efficient CRUD operations
25
- - Write powerful aggregation pipelines
26
- - Optimize queries with proper indexing
27
- - Use Mongoose ODM for type safety
28
- - Handle transactions for data integrity
29
- - Implement production patterns
30
-
31
- ## Features
32
-
33
- ### 1. Document Schema Design
8
+ ## Quick Start
34
9
 
35
10
  ```typescript
36
- // src/models/user.model.ts
37
- import mongoose, { Schema, Document, Model } from 'mongoose';
38
-
39
- // TypeScript interfaces
40
- export interface IAddress {
41
- street: string;
42
- city: string;
43
- state: string;
44
- zipCode: string;
45
- country: string;
46
- }
11
+ // Schema with Mongoose
12
+ import mongoose, { Schema, Document } from 'mongoose';
47
13
 
48
- export interface IUser extends Document {
14
+ interface IUser extends Document {
49
15
  email: string;
50
- password: string;
51
- profile: {
52
- firstName: string;
53
- lastName: string;
54
- avatar?: string;
55
- bio?: string;
56
- };
57
- addresses: IAddress[];
58
- role: 'admin' | 'user' | 'guest';
59
- isActive: boolean;
60
- lastLoginAt?: Date;
16
+ profile: { firstName: string; lastName: string };
61
17
  createdAt: Date;
62
- updatedAt: Date;
63
-
64
- // Instance methods
65
- comparePassword(password: string): Promise<boolean>;
66
- getFullName(): string;
67
- }
68
-
69
- // Statics interface
70
- export interface IUserModel extends Model<IUser> {
71
- findByEmail(email: string): Promise<IUser | null>;
72
- findActiveUsers(): Promise<IUser[]>;
73
18
  }
74
19
 
75
- // Schema definition
76
- const addressSchema = new Schema<IAddress>({
77
- street: { type: String, required: true },
78
- city: { type: String, required: true },
79
- state: { type: String, required: true },
80
- zipCode: { type: String, required: true },
81
- country: { type: String, required: true, default: 'US' },
82
- }, { _id: false });
83
-
84
- const userSchema = new Schema<IUser, IUserModel>({
85
- email: {
86
- type: String,
87
- required: [true, 'Email is required'],
88
- unique: true,
89
- lowercase: true,
90
- trim: true,
91
- validate: {
92
- validator: (v: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v),
93
- message: 'Invalid email format',
94
- },
95
- },
96
- password: {
97
- type: String,
98
- required: true,
99
- minlength: [8, 'Password must be at least 8 characters'],
100
- select: false, // Don't include in queries by default
101
- },
20
+ const userSchema = new Schema<IUser>({
21
+ email: { type: String, required: true, unique: true, lowercase: true },
102
22
  profile: {
103
- firstName: { type: String, required: true, trim: true },
104
- lastName: { type: String, required: true, trim: true },
105
- avatar: String,
106
- bio: { type: String, maxlength: 500 },
23
+ firstName: { type: String, required: true },
24
+ lastName: { type: String, required: true },
107
25
  },
108
- addresses: [addressSchema],
109
- role: {
110
- type: String,
111
- enum: ['admin', 'user', 'guest'],
112
- default: 'user',
113
- },
114
- isActive: { type: Boolean, default: true },
115
- lastLoginAt: Date,
116
- }, {
117
- timestamps: true,
118
- toJSON: {
119
- virtuals: true,
120
- transform: (doc, ret) => {
121
- delete ret.password;
122
- delete ret.__v;
123
- return ret;
124
- },
125
- },
126
- });
26
+ }, { timestamps: true });
127
27
 
128
- // Indexes
129
28
  userSchema.index({ email: 1 });
130
- userSchema.index({ 'profile.firstName': 1, 'profile.lastName': 1 });
131
- userSchema.index({ role: 1, isActive: 1 });
132
- userSchema.index({ createdAt: -1 });
133
-
134
- // Virtual properties
135
- userSchema.virtual('fullName').get(function() {
136
- return `${this.profile.firstName} ${this.profile.lastName}`;
137
- });
138
-
139
- // Instance methods
140
- userSchema.methods.comparePassword = async function(password: string): Promise<boolean> {
141
- const bcrypt = await import('bcrypt');
142
- return bcrypt.compare(password, this.password);
143
- };
144
-
145
- userSchema.methods.getFullName = function(): string {
146
- return `${this.profile.firstName} ${this.profile.lastName}`;
147
- };
148
-
149
- // Static methods
150
- userSchema.statics.findByEmail = function(email: string) {
151
- return this.findOne({ email: email.toLowerCase() });
152
- };
153
-
154
- userSchema.statics.findActiveUsers = function() {
155
- return this.find({ isActive: true });
156
- };
157
-
158
- // Middleware (hooks)
159
- userSchema.pre('save', async function(next) {
160
- if (this.isModified('password')) {
161
- const bcrypt = await import('bcrypt');
162
- this.password = await bcrypt.hash(this.password, 12);
163
- }
164
- next();
165
- });
166
-
167
- export const User = mongoose.model<IUser, IUserModel>('User', userSchema);
168
- ```
169
-
170
- ### 2. CRUD Operations
171
-
172
- ```typescript
173
- // src/repositories/user.repository.ts
174
- import { User, IUser } from '../models/user.model';
175
- import { FilterQuery, UpdateQuery, QueryOptions } from 'mongoose';
176
-
177
- export interface PaginationOptions {
178
- page?: number;
179
- limit?: number;
180
- sort?: Record<string, 1 | -1>;
181
- }
182
-
183
- export interface PaginatedResult<T> {
184
- data: T[];
185
- pagination: {
186
- page: number;
187
- limit: number;
188
- total: number;
189
- totalPages: number;
190
- hasMore: boolean;
191
- };
192
- }
193
-
194
- export class UserRepository {
195
- // Create
196
- async create(userData: Partial<IUser>): Promise<IUser> {
197
- const user = new User(userData);
198
- return user.save();
199
- }
200
-
201
- async createMany(users: Partial<IUser>[]): Promise<IUser[]> {
202
- return User.insertMany(users);
203
- }
204
-
205
- // Read
206
- async findById(id: string): Promise<IUser | null> {
207
- return User.findById(id);
208
- }
209
-
210
- async findByIdWithPassword(id: string): Promise<IUser | null> {
211
- return User.findById(id).select('+password');
212
- }
213
-
214
- async findByEmail(email: string): Promise<IUser | null> {
215
- return User.findByEmail(email);
216
- }
217
-
218
- async findOne(filter: FilterQuery<IUser>): Promise<IUser | null> {
219
- return User.findOne(filter);
220
- }
221
-
222
- async find(
223
- filter: FilterQuery<IUser>,
224
- options: PaginationOptions = {}
225
- ): Promise<PaginatedResult<IUser>> {
226
- const { page = 1, limit = 20, sort = { createdAt: -1 } } = options;
227
- const skip = (page - 1) * limit;
228
-
229
- const [data, total] = await Promise.all([
230
- User.find(filter).sort(sort).skip(skip).limit(limit),
231
- User.countDocuments(filter),
232
- ]);
233
-
234
- return {
235
- data,
236
- pagination: {
237
- page,
238
- limit,
239
- total,
240
- totalPages: Math.ceil(total / limit),
241
- hasMore: page * limit < total,
242
- },
243
- };
244
- }
245
-
246
- // Update
247
- async updateById(
248
- id: string,
249
- update: UpdateQuery<IUser>,
250
- options: QueryOptions = {}
251
- ): Promise<IUser | null> {
252
- return User.findByIdAndUpdate(id, update, {
253
- new: true,
254
- runValidators: true,
255
- ...options,
256
- });
257
- }
258
-
259
- async updateOne(
260
- filter: FilterQuery<IUser>,
261
- update: UpdateQuery<IUser>
262
- ): Promise<IUser | null> {
263
- return User.findOneAndUpdate(filter, update, {
264
- new: true,
265
- runValidators: true,
266
- });
267
- }
268
-
269
- async updateMany(
270
- filter: FilterQuery<IUser>,
271
- update: UpdateQuery<IUser>
272
- ): Promise<{ modifiedCount: number }> {
273
- const result = await User.updateMany(filter, update);
274
- return { modifiedCount: result.modifiedCount };
275
- }
276
-
277
- // Delete
278
- async deleteById(id: string): Promise<IUser | null> {
279
- return User.findByIdAndDelete(id);
280
- }
281
-
282
- async deleteMany(filter: FilterQuery<IUser>): Promise<{ deletedCount: number }> {
283
- const result = await User.deleteMany(filter);
284
- return { deletedCount: result.deletedCount };
285
- }
286
-
287
- // Soft delete
288
- async softDelete(id: string): Promise<IUser | null> {
289
- return this.updateById(id, { isActive: false });
290
- }
291
-
292
- // Exists check
293
- async exists(filter: FilterQuery<IUser>): Promise<boolean> {
294
- const result = await User.exists(filter);
295
- return !!result;
296
- }
297
-
298
- // Count
299
- async count(filter: FilterQuery<IUser> = {}): Promise<number> {
300
- return User.countDocuments(filter);
301
- }
302
- }
303
- ```
304
-
305
- ### 3. Aggregation Pipelines
306
-
307
- ```typescript
308
- // src/services/analytics.service.ts
309
- import { User } from '../models/user.model';
310
- import { Order } from '../models/order.model';
311
-
312
- export class AnalyticsService {
313
- // User statistics by role
314
- async getUserStatsByRole() {
315
- return User.aggregate([
316
- { $match: { isActive: true } },
317
- {
318
- $group: {
319
- _id: '$role',
320
- count: { $sum: 1 },
321
- avgAddresses: { $avg: { $size: '$addresses' } },
322
- },
323
- },
324
- { $sort: { count: -1 } },
325
- ]);
326
- }
327
-
328
- // Monthly user registrations
329
- async getMonthlyRegistrations(year: number) {
330
- return User.aggregate([
331
- {
332
- $match: {
333
- createdAt: {
334
- $gte: new Date(`${year}-01-01`),
335
- $lt: new Date(`${year + 1}-01-01`),
336
- },
337
- },
338
- },
339
- {
340
- $group: {
341
- _id: { $month: '$createdAt' },
342
- count: { $sum: 1 },
343
- },
344
- },
345
- {
346
- $project: {
347
- _id: 0,
348
- month: '$_id',
349
- count: 1,
350
- },
351
- },
352
- { $sort: { month: 1 } },
353
- ]);
354
- }
355
-
356
- // Order revenue by product category
357
- async getRevenueByCategory(startDate: Date, endDate: Date) {
358
- return Order.aggregate([
359
- {
360
- $match: {
361
- status: 'completed',
362
- createdAt: { $gte: startDate, $lte: endDate },
363
- },
364
- },
365
- { $unwind: '$items' },
366
- {
367
- $lookup: {
368
- from: 'products',
369
- localField: 'items.productId',
370
- foreignField: '_id',
371
- as: 'product',
372
- },
373
- },
374
- { $unwind: '$product' },
375
- {
376
- $group: {
377
- _id: '$product.category',
378
- totalRevenue: { $sum: { $multiply: ['$items.quantity', '$items.price'] } },
379
- totalQuantity: { $sum: '$items.quantity' },
380
- orderCount: { $sum: 1 },
381
- },
382
- },
383
- {
384
- $project: {
385
- _id: 0,
386
- category: '$_id',
387
- totalRevenue: { $round: ['$totalRevenue', 2] },
388
- totalQuantity: 1,
389
- orderCount: 1,
390
- avgOrderValue: {
391
- $round: [{ $divide: ['$totalRevenue', '$orderCount'] }, 2],
392
- },
393
- },
394
- },
395
- { $sort: { totalRevenue: -1 } },
396
- ]);
397
- }
398
-
399
- // Top customers with order summary
400
- async getTopCustomers(limit: number = 10) {
401
- return Order.aggregate([
402
- { $match: { status: 'completed' } },
403
- {
404
- $group: {
405
- _id: '$userId',
406
- totalOrders: { $sum: 1 },
407
- totalSpent: { $sum: '$total' },
408
- avgOrderValue: { $avg: '$total' },
409
- lastOrderDate: { $max: '$createdAt' },
410
- },
411
- },
412
- {
413
- $lookup: {
414
- from: 'users',
415
- localField: '_id',
416
- foreignField: '_id',
417
- as: 'user',
418
- },
419
- },
420
- { $unwind: '$user' },
421
- {
422
- $project: {
423
- _id: 0,
424
- userId: '$_id',
425
- email: '$user.email',
426
- name: {
427
- $concat: ['$user.profile.firstName', ' ', '$user.profile.lastName'],
428
- },
429
- totalOrders: 1,
430
- totalSpent: { $round: ['$totalSpent', 2] },
431
- avgOrderValue: { $round: ['$avgOrderValue', 2] },
432
- lastOrderDate: 1,
433
- },
434
- },
435
- { $sort: { totalSpent: -1 } },
436
- { $limit: limit },
437
- ]);
438
- }
439
-
440
- // Search with text score
441
- async searchProducts(query: string, options: { category?: string; limit?: number }) {
442
- const pipeline: any[] = [
443
- {
444
- $search: {
445
- index: 'products_search',
446
- text: {
447
- query,
448
- path: ['name', 'description', 'tags'],
449
- fuzzy: { maxEdits: 1 },
450
- },
451
- },
452
- },
453
- {
454
- $addFields: {
455
- score: { $meta: 'searchScore' },
456
- },
457
- },
458
- ];
459
-
460
- if (options.category) {
461
- pipeline.push({ $match: { category: options.category } });
462
- }
463
-
464
- pipeline.push(
465
- { $sort: { score: -1 } },
466
- { $limit: options.limit || 20 }
467
- );
468
-
469
- return Product.aggregate(pipeline);
470
- }
471
- }
29
+ export const User = mongoose.model<IUser>('User', userSchema);
472
30
  ```
473
31
 
474
- ### 4. Transactions
475
-
476
- ```typescript
477
- // src/services/order.service.ts
478
- import mongoose from 'mongoose';
479
- import { Order } from '../models/order.model';
480
- import { Product } from '../models/product.model';
481
- import { User } from '../models/user.model';
482
-
483
- export class OrderService {
484
- async createOrder(userId: string, items: Array<{ productId: string; quantity: number }>) {
485
- const session = await mongoose.startSession();
486
-
487
- try {
488
- session.startTransaction();
489
-
490
- // Validate user
491
- const user = await User.findById(userId).session(session);
492
- if (!user) {
493
- throw new Error('User not found');
494
- }
495
-
496
- // Calculate order total and validate stock
497
- let total = 0;
498
- const orderItems = [];
499
-
500
- for (const item of items) {
501
- const product = await Product.findById(item.productId).session(session);
502
- if (!product) {
503
- throw new Error(`Product ${item.productId} not found`);
504
- }
505
-
506
- if (product.stock < item.quantity) {
507
- throw new Error(`Insufficient stock for ${product.name}`);
508
- }
509
-
510
- // Decrement stock
511
- await Product.updateOne(
512
- { _id: item.productId },
513
- { $inc: { stock: -item.quantity } },
514
- { session }
515
- );
516
-
517
- const itemTotal = product.price * item.quantity;
518
- total += itemTotal;
519
-
520
- orderItems.push({
521
- productId: product._id,
522
- name: product.name,
523
- price: product.price,
524
- quantity: item.quantity,
525
- total: itemTotal,
526
- });
527
- }
528
-
529
- // Create order
530
- const [order] = await Order.create([{
531
- userId,
532
- items: orderItems,
533
- total,
534
- status: 'pending',
535
- }], { session });
536
-
537
- await session.commitTransaction();
538
- return order;
539
- } catch (error) {
540
- await session.abortTransaction();
541
- throw error;
542
- } finally {
543
- session.endSession();
544
- }
545
- }
546
-
547
- async cancelOrder(orderId: string) {
548
- const session = await mongoose.startSession();
549
-
550
- try {
551
- session.startTransaction();
552
-
553
- const order = await Order.findById(orderId).session(session);
554
- if (!order) {
555
- throw new Error('Order not found');
556
- }
557
-
558
- if (order.status !== 'pending') {
559
- throw new Error('Only pending orders can be cancelled');
560
- }
32
+ ## Features
561
33
 
562
- // Restore stock
563
- for (const item of order.items) {
564
- await Product.updateOne(
565
- { _id: item.productId },
566
- { $inc: { stock: item.quantity } },
567
- { session }
568
- );
569
- }
34
+ | Feature | Description | Guide |
35
+ |---------|-------------|-------|
36
+ | Document Schema | Type-safe schema design with Mongoose | Embed related data, use references for large collections |
37
+ | CRUD Operations | Create, read, update, delete with type safety | Use `findById`, `findOne`, `updateOne`, `deleteOne` |
38
+ | Aggregation Pipelines | Complex data transformations and analytics | Chain `$match`, `$group`, `$lookup`, `$project` stages |
39
+ | Indexing | Query optimization with proper indexes | Create compound indexes matching query patterns |
40
+ | Transactions | Multi-document ACID operations | Use sessions for operations requiring atomicity |
41
+ | Change Streams | Real-time data change notifications | Watch collections for inserts, updates, deletes |
570
42
 
571
- // Update order status
572
- order.status = 'cancelled';
573
- await order.save({ session });
43
+ ## Common Patterns
574
44
 
575
- await session.commitTransaction();
576
- return order;
577
- } catch (error) {
578
- await session.abortTransaction();
579
- throw error;
580
- } finally {
581
- session.endSession();
582
- }
583
- }
584
- }
585
- ```
586
-
587
- ### 5. Connection and Configuration
45
+ ### Repository Pattern with Pagination
588
46
 
589
47
  ```typescript
590
- // src/config/database.ts
591
- import mongoose from 'mongoose';
592
-
593
- interface DatabaseConfig {
594
- uri: string;
595
- options?: mongoose.ConnectOptions;
596
- }
597
-
598
- export async function connectDatabase(config: DatabaseConfig): Promise<void> {
599
- const defaultOptions: mongoose.ConnectOptions = {
600
- maxPoolSize: 10,
601
- serverSelectionTimeoutMS: 5000,
602
- socketTimeoutMS: 45000,
603
- family: 4,
604
- };
605
-
606
- mongoose.set('strictQuery', true);
607
-
608
- mongoose.connection.on('connected', () => {
609
- console.log('MongoDB connected successfully');
610
- });
611
-
612
- mongoose.connection.on('error', (err) => {
613
- console.error('MongoDB connection error:', err);
614
- });
615
-
616
- mongoose.connection.on('disconnected', () => {
617
- console.log('MongoDB disconnected');
618
- });
619
-
620
- process.on('SIGINT', async () => {
621
- await mongoose.connection.close();
622
- process.exit(0);
623
- });
624
-
625
- await mongoose.connect(config.uri, {
626
- ...defaultOptions,
627
- ...config.options,
628
- });
629
- }
630
-
631
- export async function disconnectDatabase(): Promise<void> {
632
- await mongoose.connection.close();
633
- }
634
-
635
- // Health check
636
- export async function checkDatabaseHealth(): Promise<boolean> {
637
- try {
638
- await mongoose.connection.db.admin().ping();
639
- return true;
640
- } catch {
641
- return false;
642
- }
48
+ async findPaginated(filter: FilterQuery<IUser>, page = 1, limit = 20) {
49
+ const [data, total] = await Promise.all([
50
+ User.find(filter).sort({ createdAt: -1 }).skip((page - 1) * limit).limit(limit),
51
+ User.countDocuments(filter),
52
+ ]);
53
+ return { data, pagination: { page, limit, total, totalPages: Math.ceil(total / limit) } };
643
54
  }
644
55
  ```
645
56
 
646
- ### 6. Indexing Strategies
647
-
648
- ```javascript
649
- // Creating indexes for optimal query performance
650
-
651
- // Compound index for common query patterns
652
- db.orders.createIndex(
653
- { userId: 1, status: 1, createdAt: -1 },
654
- { name: 'user_status_date' }
655
- );
656
-
657
- // Partial index for active records only
658
- db.users.createIndex(
659
- { email: 1 },
660
- {
661
- unique: true,
662
- partialFilterExpression: { isActive: true },
663
- name: 'active_users_email'
664
- }
665
- );
666
-
667
- // TTL index for expiring documents
668
- db.sessions.createIndex(
669
- { expiresAt: 1 },
670
- { expireAfterSeconds: 0, name: 'session_ttl' }
671
- );
672
-
673
- // Text index for full-text search
674
- db.products.createIndex(
675
- { name: 'text', description: 'text', tags: 'text' },
676
- {
677
- weights: { name: 10, tags: 5, description: 1 },
678
- name: 'product_search'
679
- }
680
- );
681
-
682
- // Geospatial index
683
- db.stores.createIndex(
684
- { location: '2dsphere' },
685
- { name: 'store_location' }
686
- );
687
-
688
- // Wildcard index for dynamic fields
689
- db.logs.createIndex(
690
- { 'metadata.$**': 1 },
691
- { name: 'log_metadata_wildcard' }
692
- );
693
- ```
694
-
695
- ## Use Cases
696
-
697
- ### Real-time Analytics Dashboard
57
+ ### Aggregation Pipeline
698
58
 
699
59
  ```typescript
700
- // Aggregation for dashboard metrics
701
- async function getDashboardMetrics(timeRange: { start: Date; end: Date }) {
702
- const [orderStats, userStats, revenueByDay] = await Promise.all([
703
- Order.aggregate([
704
- {
705
- $match: {
706
- createdAt: { $gte: timeRange.start, $lte: timeRange.end },
707
- },
708
- },
709
- {
710
- $group: {
711
- _id: '$status',
712
- count: { $sum: 1 },
713
- total: { $sum: '$total' },
714
- },
715
- },
716
- ]),
717
-
718
- User.aggregate([
719
- {
720
- $facet: {
721
- total: [{ $count: 'count' }],
722
- newUsers: [
723
- {
724
- $match: {
725
- createdAt: { $gte: timeRange.start, $lte: timeRange.end },
726
- },
727
- },
728
- { $count: 'count' },
729
- ],
730
- byRole: [{ $group: { _id: '$role', count: { $sum: 1 } } }],
731
- },
732
- },
733
- ]),
734
-
735
- Order.aggregate([
736
- {
737
- $match: {
738
- status: 'completed',
739
- createdAt: { $gte: timeRange.start, $lte: timeRange.end },
740
- },
741
- },
742
- {
743
- $group: {
744
- _id: { $dateToString: { format: '%Y-%m-%d', date: '$createdAt' } },
745
- revenue: { $sum: '$total' },
746
- orders: { $sum: 1 },
747
- },
748
- },
749
- { $sort: { _id: 1 } },
750
- ]),
751
- ]);
752
-
753
- return { orderStats, userStats: userStats[0], revenueByDay };
754
- }
60
+ const stats = await Order.aggregate([
61
+ { $match: { status: 'completed', createdAt: { $gte: startDate } } },
62
+ { $group: { _id: '$userId', total: { $sum: '$amount' }, count: { $sum: 1 } } },
63
+ { $sort: { total: -1 } },
64
+ { $limit: 10 },
65
+ ]);
755
66
  ```
756
67
 
757
- ### Change Streams for Real-time Updates
68
+ ### Transaction for Order Creation
758
69
 
759
70
  ```typescript
760
- // Watch for real-time changes
761
- async function watchOrderChanges(callback: (change: any) => void) {
762
- const changeStream = Order.watch([
763
- { $match: { operationType: { $in: ['insert', 'update'] } } },
764
- ], { fullDocument: 'updateLookup' });
765
-
766
- changeStream.on('change', (change) => {
767
- callback({
768
- type: change.operationType,
769
- document: change.fullDocument,
770
- timestamp: change.clusterTime,
771
- });
772
- });
773
-
774
- return () => changeStream.close();
71
+ const session = await mongoose.startSession();
72
+ try {
73
+ session.startTransaction();
74
+ await Product.updateOne({ _id: productId }, { $inc: { stock: -quantity } }, { session });
75
+ const order = await Order.create([{ userId, items, total }], { session });
76
+ await session.commitTransaction();
77
+ return order[0];
78
+ } catch (error) {
79
+ await session.abortTransaction();
80
+ throw error;
81
+ } finally {
82
+ session.endSession();
775
83
  }
776
84
  ```
777
85
 
778
86
  ## Best Practices
779
87
 
780
- ### Do's
781
-
782
- - Design schemas based on query patterns
783
- - Use appropriate indexes for queries
784
- - Implement proper error handling
785
- - Use transactions for multi-document operations
786
- - Set up connection pooling
787
- - Use lean() for read-only queries
788
- - Implement pagination for large datasets
789
- - Use aggregation for complex queries
790
- - Monitor slow queries
791
- - Set up replica sets for production
792
-
793
- ### Don'ts
794
-
795
- - Don't embed large arrays in documents
796
- - Don't create too many indexes
797
- - Don't use $where or mapReduce
798
- - Don't ignore index usage in queries
799
- - Don't skip validation
800
- - Don't store large files directly
801
- - Don't use synchronous operations
802
- - Don't ignore connection errors
803
- - Don't hardcode connection strings
804
- - Don't skip backups
805
-
806
- ## References
807
-
808
- - [MongoDB Documentation](https://docs.mongodb.com/)
809
- - [Mongoose Documentation](https://mongoosejs.com/docs/)
810
- - [MongoDB University](https://university.mongodb.com/)
811
- - [MongoDB Performance Best Practices](https://www.mongodb.com/docs/manual/administration/analyzing-mongodb-performance/)
812
- - [Schema Design Patterns](https://www.mongodb.com/blog/post/building-with-patterns-a-summary)
88
+ | Do | Avoid |
89
+ |----|-------|
90
+ | Design schemas based on query patterns | Embedding large arrays in documents |
91
+ | Create indexes for frequently queried fields | Using `$where` or mapReduce in production |
92
+ | Use `lean()` for read-only queries | Skipping validation on writes |
93
+ | Implement pagination for large datasets | Storing large files directly (use GridFS) |
94
+ | Set connection pool size appropriately | Hardcoding connection strings |
95
+ | Use transactions for multi-document ops | Ignoring index usage in explain plans |
96
+ | Add TTL indexes for expiring data | Creating too many indexes (write overhead) |