omgkit 2.2.0 → 2.3.1

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 (60) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/plugin/skills/databases/database-management/SKILL.md +288 -0
  4. package/plugin/skills/databases/database-migration/SKILL.md +285 -0
  5. package/plugin/skills/databases/database-schema-design/SKILL.md +195 -0
  6. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  7. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  8. package/plugin/skills/databases/redis/SKILL.md +53 -860
  9. package/plugin/skills/databases/supabase/SKILL.md +283 -0
  10. package/plugin/skills/devops/aws/SKILL.md +68 -672
  11. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  12. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  13. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  14. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  15. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  16. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  17. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  18. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  19. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  20. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  21. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  22. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  23. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  24. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  25. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  26. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  27. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  28. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  29. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  30. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  31. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  32. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  33. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  34. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  35. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  36. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  37. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  38. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  39. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  40. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  41. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  42. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  43. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  44. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  45. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  46. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  47. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  48. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  49. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  50. package/plugin/skills/security/oauth/SKILL.md +80 -934
  51. package/plugin/skills/security/owasp/SKILL.md +78 -862
  52. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  53. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  54. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  55. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  56. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  57. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  58. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  59. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  60. 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) |