database-connector 2.0.4 → 2.1.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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,187 @@
1
1
  # Changelog - Database Connector Models Refactoring
2
2
 
3
- ## 2025-12-08
3
+ ## Database Indexes Optimization
4
+
5
+ ### Added
6
+
7
+ #### Database Indexes for Performance Optimization
8
+
9
+ After comprehensive analysis of model usage across all microservices (AuthService, CartService, CategoryService, NotificationService, OfferService, OrderService, ProductService, SalesAnalyticsService, SearchExplorationService, StoreService, SubscriptionService, and UserService), the following database indexes have been added to optimize query performance:
10
+
11
+ **1. User Model** (`User.js`)
12
+ - **Indexes Added:**
13
+ - `email` (sparse): For login authentication by email in AuthService
14
+ - `phone` (sparse): For login authentication by phone in AuthService
15
+ - `role`: For filtering users by role (admin, seller, manager, etc.)
16
+ - `storeSubs`: For finding users subscribed to specific stores (ProductService notifications)
17
+ - **Justification:** AuthService performs frequent lookups by email, phone, and username. The sparse indexes on email and phone allow for null values while optimizing lookups. Role-based queries are common in user management.
18
+
19
+ **2. Cart Model** (`Cart.js`)
20
+ - **Indexes Added:**
21
+ - `userId` (unique): Already existed, ensures one cart per user
22
+ - `items.productId`: For querying carts containing specific products
23
+ - **Justification:** CartService frequently queries carts by userId and needs to find items by productId for cart operations.
24
+
25
+ **3. Product Model** (`Product.js`)
26
+ - **Indexes Added:**
27
+ - `storeId, deleted` (compound): For fetching active products by store
28
+ - `sellerId`: For seller-specific product queries in SellerRecommendationEngine
29
+ - `categoryId`: For category-based filtering
30
+ - `storeCategoryId`: For store category navigation
31
+ - `deleted`: For filtering deleted products
32
+ - `numberOfSales` (descending): For top-selling products
33
+ - `numberOfViews` (descending): For most-viewed products
34
+ - `averageRating` (descending): For highest-rated products sorting
35
+ - `createdAt` (descending): For newest products first
36
+ - `price`: For price range filtering
37
+ - `tags`: For tag-based search
38
+ - `variants._id`: For variant-specific queries in OrderService
39
+ - **Justification:** ProductService has extensive query patterns including filtering by store, category, deletion status, and sorting by sales/views/ratings. SearchExplorationService uses price and tag filters. The compound index on storeId+deleted optimizes the most common query pattern.
40
+
41
+ **4. Store Model** (`Store.js`)
42
+ - **Indexes Added:**
43
+ - `location` (2dsphere): For geospatial proximity queries
44
+ - `sellerId`: For finding stores by seller
45
+ - `address.city`: For city-based store searches
46
+ - `isActive`: For filtering active stores
47
+ - `activated`: For filtering activated stores
48
+ - `storeCategorieIds`: For category-based store filtering
49
+ - `ratingSum, ratingCount` (compound descending): For sorting stores by rating
50
+ - **Justification:** StoreService performs geospatial queries for nearby stores, city-based searches, and seller-specific queries. The 2dsphere index enables efficient location-based searches. Rating-based sorting is common in store listings.
51
+
52
+ **5. Order Model** (`Order.js`)
53
+ - **Indexes Added:**
54
+ - `clientId`: For user order history
55
+ - `storeId`: For store order management
56
+ - `status`: For filtering orders by status
57
+ - `paymentId`: For payment-related order lookups
58
+ - `shippingStatus`: For shipping status filtering
59
+ - `paymentStatus`: For payment status filtering
60
+ - `createdAt` (descending): For recent orders first
61
+ - `clientId, storeId` (compound): For user orders at specific store
62
+ - `storeId, status` (compound): For store orders by status
63
+ - **Justification:** OrderService queries orders by client, store, various statuses, and payment ID. GDPR service exports user order data. Compound indexes optimize common filtering combinations.
64
+
65
+ **6. Notification Model** (`Notification.js`)
66
+ - **Indexes Added:**
67
+ - `owner_id, createdAt` (compound descending): For user notifications sorted by date
68
+ - `owner_id, seend` (compound): For filtering read/unread notifications
69
+ - `owner_id, seendInList` (compound): For list view status
70
+ - `type`: For filtering by notification type
71
+ - `sub_type`: For filtering by notification sub-type
72
+ - **Justification:** NotificationService queries notifications by owner with sorting by date and filtering by read status. Compound indexes optimize these common query patterns.
73
+
74
+ **7. Sale Model** (`Sale.js`)
75
+ - **Indexes Added:**
76
+ - `sellerId, date` (compound descending): For seller sales analytics
77
+ - `storeId, date` (compound descending): For store sales analytics
78
+ - `productId, date` (compound descending): For product sales analytics
79
+ - `date` (descending): For time-based analytics
80
+ - `region`: For regional sales analysis
81
+ - **Justification:** SalesAnalyticsService aggregates sales by seller, store, product, and time periods. Compound indexes with date optimize time-series analytics queries.
82
+
83
+ **8. View Model** (`View.js`)
84
+ - **Indexes Added:**
85
+ - `sellerId, date` (compound descending): For seller view analytics
86
+ - `storeId, date` (compound descending): For store view analytics
87
+ - `productId, date` (compound descending): For product view analytics
88
+ - `clientId, date` (compound descending): For user browsing history
89
+ - `date` (descending): For time-based view analytics
90
+ - `region`: For regional view analysis
91
+ - **Justification:** SalesAnalyticsService aggregates views by seller, store, product, client, and time periods. These indexes support the analytics aggregation pipelines.
92
+
93
+ **9. Payment Model** (`Payment.js`)
94
+ - **Indexes Added:**
95
+ - `userId, createdAt` (compound descending): For user payment history
96
+ - `sellerId, createdAt` (compound descending): For seller payment tracking
97
+ - `paymentStatus`: For filtering by payment status
98
+ - `token`: For payment token lookups
99
+ - `gateway`: For filtering by payment gateway
100
+ - `paymentDate` (descending): For date-based payment queries
101
+ - **Justification:** Payment tracking requires efficient queries by user, seller, status, and token. Date-based sorting is common in payment history.
102
+
103
+ **10. Bill Model** (`Bill.js`)
104
+ - **Indexes Added:**
105
+ - `userId, createdAt` (compound descending): For user billing history
106
+ - `storeId, createdAt` (compound descending): For store billing records
107
+ - `paymentStatus`: For payment status filtering
108
+ - `shippingStatus`: For shipping status filtering
109
+ - `refundStatus`: For refund status filtering
110
+ - `paymentId`: For payment-bill relationship
111
+ - `userId, storeId` (compound): For user bills at specific store
112
+ - **Justification:** Bill management requires queries by user, store, various statuses, and payment associations. Compound indexes optimize common query patterns.
113
+
114
+ **11. Offer Model** (`Offer.js`)
115
+ - **Indexes Added:**
116
+ - `sellerId, offerStatus` (compound): For seller offers by status
117
+ - `storeId, offerStatus` (compound): For store offers by status
118
+ - `productId`: For product-specific offers
119
+ - `offerStatus`: For filtering active/expired offers
120
+ - `offerExpiration`: For expiration-based queries
121
+ - `createdAt` (descending): For newest offers first
122
+ - `offerDeleted`: For filtering deleted offers
123
+ - **Justification:** OfferService queries offers by seller, store, product, and status. Compound indexes optimize seller and store offer listings with status filtering.
124
+
125
+ **12. Subscription Model** (`Subscription.js`)
126
+ - **Indexes Added:**
127
+ - `storeId`: For store subscription lookup
128
+ - `status`: For filtering by subscription status
129
+ - `endDate`: For expiration checks
130
+ - `startDate`: For activation tracking
131
+ - `planId`: For plan-based queries
132
+ - `paymentManagerId`: For payment manager tracking
133
+ - `storeId, status` (compound): For store subscription status
134
+ - `status, endDate` (compound): For active subscription expiration monitoring
135
+ - **Justification:** SubscriptionService queries subscriptions by store, status, and expiration dates. Compound indexes optimize active subscription monitoring and store subscription management.
136
+
137
+ **13. UserAction Model** (`UserAction.js`)
138
+ - **Indexes Added:**
139
+ - `userId, timestamp` (compound descending): For user activity timeline
140
+ - `type`: For filtering by action type
141
+ - `sessionId`: For session-based analytics
142
+ - `deviceId`: For device-based tracking
143
+ - `timestamp` (descending): For recent activity queries
144
+ - **Justification:** UserService tracks user actions for analytics and GDPR data export. Compound index on userId+timestamp optimizes user activity timeline queries.
145
+
146
+ **14. ResetPassword Model** (`ResetPassword.js`)
147
+ - **Indexes Added:**
148
+ - `userId`: For user password reset lookups
149
+ - `token`: For token verification
150
+ - `createAt` (TTL index, expires: 3600s): Automatic cleanup of expired tokens
151
+ - **Justification:** AuthService looks up reset tokens by userId and token. TTL index automatically removes expired reset tokens after 1 hour, maintaining database hygiene.
152
+
153
+ ### Performance Impact
154
+
155
+ **Query Performance Improvements:**
156
+ - **Single-field indexes:** Reduce query time from O(n) to O(log n) for exact matches
157
+ - **Compound indexes:** Optimize queries filtering by multiple fields simultaneously
158
+ - **Geospatial index (Store):** Enable efficient proximity searches for nearby stores
159
+ - **Descending indexes:** Optimize sorted queries (newest first, highest rated, etc.)
160
+ - **Sparse indexes (User email/phone):** Allow null values while optimizing lookups
161
+ - **TTL index (ResetPassword):** Automatic cleanup reduces manual maintenance
162
+
163
+ **Index Strategy:**
164
+ - Compound indexes placed on most frequent query combinations
165
+ - Sort direction matches common query patterns (descending for dates, ratings, views)
166
+ - Sparse indexes used for optional fields to reduce index size
167
+ - Array field indexes support multi-valued queries
168
+
169
+ ### Maintenance Notes
170
+
171
+ **Index Monitoring:**
172
+ - Monitor index usage with MongoDB's `db.collection.aggregate([{$indexStats:{}}])`
173
+ - Review slow query logs to identify missing indexes
174
+ - Update indexes as query patterns evolve
175
+
176
+ **Index Size Considerations:**
177
+ - Each index consumes disk space and memory
178
+ - Indexes slow down write operations slightly
179
+ - Compound indexes can serve queries on their prefix fields
180
+ - Consider removing unused indexes based on monitoring data
181
+
182
+ ---
183
+
184
+ ## Initial Refactoring
4
185
 
5
186
  ### Added
6
187
 
package/models/Bill.js CHANGED
@@ -173,6 +173,15 @@ const BillSchema = new mongoose.Schema(
173
173
  }
174
174
  );
175
175
 
176
+ // Indexes for performance optimization
177
+ BillSchema.index({ userId: 1, createdAt: -1 });
178
+ BillSchema.index({ storeId: 1, createdAt: -1 });
179
+ BillSchema.index({ paymentStatus: 1 });
180
+ BillSchema.index({ shippingStatus: 1 });
181
+ BillSchema.index({ refundStatus: 1 });
182
+ BillSchema.index({ paymentId: 1 });
183
+ BillSchema.index({ userId: 1, storeId: 1 });
184
+
176
185
  // Virtual: calculate amount that needs to be paid
177
186
  BillSchema.virtual('needToBePay').get(function () {
178
187
  if (this.paymentStatus === 'pending') {
package/models/Cart.js CHANGED
@@ -105,4 +105,8 @@ cartSchema.virtual('totalPriceForAllProducts').get(function () {
105
105
  }, 0);
106
106
  });
107
107
 
108
+ // Indexes for performance optimization
109
+ cartSchema.index({ userId: 1 }, { unique: true });
110
+ cartSchema.index({ 'items.productId': 1 });
111
+
108
112
  module.exports = model('Cart', cartSchema);
@@ -91,4 +91,11 @@ const NotificationSchema = new mongoose.Schema(
91
91
  { timestamps: true }
92
92
  );
93
93
 
94
+ // Indexes for performance optimization
95
+ NotificationSchema.index({ owner_id: 1, createdAt: -1 });
96
+ NotificationSchema.index({ owner_id: 1, seend: 1 });
97
+ NotificationSchema.index({ owner_id: 1, seendInList: 1 });
98
+ NotificationSchema.index({ type: 1 });
99
+ NotificationSchema.index({ sub_type: 1 });
100
+
94
101
  module.exports = mongoose.model('Notification', NotificationSchema);
package/models/Offer.js CHANGED
@@ -146,4 +146,13 @@ OfferSchema.virtual('active').get(function () {
146
146
  return this.offerExpiration > Date.now();
147
147
  });
148
148
 
149
+ // Indexes for performance optimization
150
+ OfferSchema.index({ sellerId: 1, offerStatus: 1 });
151
+ OfferSchema.index({ storeId: 1, offerStatus: 1 });
152
+ OfferSchema.index({ productId: 1 });
153
+ OfferSchema.index({ offerStatus: 1 });
154
+ OfferSchema.index({ offerExpiration: 1 });
155
+ OfferSchema.index({ createdAt: -1 });
156
+ OfferSchema.index({ offerDeleted: 1 });
157
+
149
158
  module.exports = mongoose.model('Offer', OfferSchema);
package/models/Order.js CHANGED
@@ -67,7 +67,9 @@ const { policySchema } = require('./Policy');
67
67
  * properties:
68
68
  * totalAmount:
69
69
  * type: number
70
- * paymentMethodeId:
70
+ * paymentMethodId:
71
+ * type: number
72
+ * paymentIntentId:
71
73
  * type: number
72
74
  * pickUp:
73
75
  * type: boolean
@@ -80,7 +82,7 @@ const { policySchema } = require('./Policy');
80
82
  * description: Whether order is canceled
81
83
  * status:
82
84
  * type: string
83
- * enum: [Pending, InPreparation, LoadingDelivery, OnTheWay, Delivered, AwaitingRecovery, Recovered, Reserved, WaitingForReturn, Returned, UnderRefund, Refunded, succeeded]
85
+ * enum: [Pending, Payed, InPreparation, LoadingDelivery, OnTheWay, Delivered, AwaitingRecovery, Recovered, Reserved, WaitingForReturn, Returned, UnderRefund, Refunded, succeeded]
84
86
  * default: Pending
85
87
  * description: Order status
86
88
  * createdAt:
@@ -250,18 +252,8 @@ const orderSchema = new mongoose.Schema(
250
252
 
251
253
  paymentInfos: {
252
254
  totalAmount: { type: Number, required: true },
253
- paymentMethodeId: { type: Number, required: true, },
254
- card: {
255
- cardNumber: { type: String, default: null },
256
- ccv: { type: String, default: null },
257
- expdate: { type: String, default: null },
258
- name: { type: String, required: true },
259
- phone: { type: String, required: true },
260
- postalCode: { type: String, required: true },
261
- address_city: { type: String, required: true },
262
- address_line1: { type: String, required: true },
263
- address_line2: { type: String, default: "" },
264
- },
255
+ paymentMethodId: { type: Number, required: true, },
256
+ paymentIntentId: { type: Number, required: false, default: null }
265
257
  },
266
258
  pickUp: { type: Boolean, required: true, default: null },
267
259
  delivery: { type: Boolean, required: true, default: null },
@@ -278,6 +270,7 @@ const orderSchema = new mongoose.Schema(
278
270
  status: {
279
271
  type: String, required: true, enum: [
280
272
  'Pending',
273
+ 'Payed',
281
274
  'InPreparation',
282
275
  'LoadingDelivery',
283
276
  'OnTheWay',
@@ -314,4 +307,16 @@ orderSchema.post('save', async function (doc) {
314
307
  console.error('Error creating sale records:', error);
315
308
  }
316
309
  });
310
+
311
+ // Indexes for performance optimization
312
+ orderSchema.index({ clientId: 1 });
313
+ orderSchema.index({ storeId: 1 });
314
+ orderSchema.index({ status: 1 });
315
+ orderSchema.index({ paymentId: 1 });
316
+ orderSchema.index({ shippingStatus: 1 });
317
+ orderSchema.index({ paymentStatus: 1 });
318
+ orderSchema.index({ createdAt: -1 });
319
+ orderSchema.index({ clientId: 1, storeId: 1 });
320
+ orderSchema.index({ storeId: 1, status: 1 });
321
+
317
322
  module.exports = mongoose.model('Order', orderSchema);
package/models/Payment.js CHANGED
@@ -191,4 +191,12 @@ const paymentSchema = new mongoose.Schema(
191
191
  { timestamps: true }
192
192
  );
193
193
 
194
+ // Indexes for performance optimization
195
+ paymentSchema.index({ userId: 1, createdAt: -1 });
196
+ paymentSchema.index({ sellerId: 1, createdAt: -1 });
197
+ paymentSchema.index({ paymentStatus: 1 });
198
+ paymentSchema.index({ token: 1 });
199
+ paymentSchema.index({ gateway: 1 });
200
+ paymentSchema.index({ paymentDate: -1 });
201
+
194
202
  module.exports = mongoose.model('Payment', paymentSchema);
package/models/Product.js CHANGED
@@ -295,4 +295,18 @@ productSchema.virtual('variantImages').get(function () {
295
295
  }
296
296
  });
297
297
 
298
+ // Indexes for performance optimization
299
+ productSchema.index({ storeId: 1, deleted: 1 });
300
+ productSchema.index({ sellerId: 1 });
301
+ productSchema.index({ categoryId: 1 });
302
+ productSchema.index({ storeCategoryId: 1 });
303
+ productSchema.index({ deleted: 1 });
304
+ productSchema.index({ numberOfSales: -1 });
305
+ productSchema.index({ numberOfViews: -1 });
306
+ productSchema.index({ averageRating: -1 });
307
+ productSchema.index({ createdAt: -1 });
308
+ productSchema.index({ price: 1 });
309
+ productSchema.index({ tags: 1 });
310
+ productSchema.index({ 'variants._id': 1 });
311
+
298
312
  module.exports = mongoose.model('Product', productSchema);
@@ -49,4 +49,9 @@ const resetPasswordSchema = new mongoose.Schema(
49
49
  { toJSON: { virtuals: true } }
50
50
  );
51
51
 
52
+ // Indexes for performance optimization
53
+ resetPasswordSchema.index({ userId: 1 });
54
+ resetPasswordSchema.index({ token: 1 });
55
+ resetPasswordSchema.index({ createAt: 1 }, { expireAfterSeconds: 3600 });
56
+
52
57
  module.exports = mongoose.model('ResetPassword', resetPasswordSchema);
package/models/Sale.js CHANGED
@@ -44,4 +44,11 @@ const Sale = new mongoose.Schema({
44
44
  region: { type: String }
45
45
  });
46
46
 
47
+ // Indexes for performance optimization (analytics queries)
48
+ Sale.index({ sellerId: 1, date: -1 });
49
+ Sale.index({ storeId: 1, date: -1 });
50
+ Sale.index({ productId: 1, date: -1 });
51
+ Sale.index({ date: -1 });
52
+ Sale.index({ region: 1 });
53
+
47
54
  module.exports = mongoose.model('Sale', Sale);
package/models/Store.js CHANGED
@@ -357,7 +357,14 @@ storeSchema.virtual('rating').get(function () {
357
357
  }
358
358
  return 0;
359
359
  });
360
- //creat index for location
360
+
361
+ // Indexes for performance optimization
361
362
  storeSchema.index({ location: '2dsphere' });
363
+ storeSchema.index({ sellerId: 1 });
364
+ storeSchema.index({ 'address.city': 1 });
365
+ storeSchema.index({ isActive: 1 });
366
+ storeSchema.index({ activated: 1 });
367
+ storeSchema.index({ storeCategorieIds: 1 });
368
+ storeSchema.index({ ratingSum: -1, ratingCount: -1 });
362
369
 
363
370
  module.exports = mongoose.model('Store', storeSchema);
@@ -189,4 +189,14 @@ const subscriptionSchema = new mongoose.Schema(
189
189
  { toJSON: { virtuals: true } }
190
190
  );
191
191
 
192
+ // Indexes for performance optimization
193
+ subscriptionSchema.index({ storeId: 1 });
194
+ subscriptionSchema.index({ status: 1 });
195
+ subscriptionSchema.index({ endDate: 1 });
196
+ subscriptionSchema.index({ startDate: 1 });
197
+ subscriptionSchema.index({ planId: 1 });
198
+ subscriptionSchema.index({ paymentManagerId: 1 });
199
+ subscriptionSchema.index({ storeId: 1, status: 1 });
200
+ subscriptionSchema.index({ status: 1, endDate: 1 });
201
+
192
202
  module.exports = mongoose.model('Subscription', subscriptionSchema);
package/models/User.js CHANGED
@@ -330,5 +330,11 @@ const userSchema = new mongoose.Schema(
330
330
  { timestamps: true }
331
331
  );
332
332
 
333
+ // Indexes for performance optimization
333
334
  userSchema.index({ username: 1 }, { unique: true });
335
+ userSchema.index({ email: 1 }, { sparse: true });
336
+ userSchema.index({ phone: 1 }, { sparse: true });
337
+ userSchema.index({ role: 1 });
338
+ userSchema.index({ storeSubs: 1 });
339
+
334
340
  module.exports = mongoose.model('User', userSchema);
@@ -67,6 +67,13 @@ const UserActionSchema = new mongoose.Schema({
67
67
  }
68
68
  });
69
69
 
70
+ // Indexes for performance optimization
71
+ UserActionSchema.index({ userId: 1, timestamp: -1 });
72
+ UserActionSchema.index({ type: 1 });
73
+ UserActionSchema.index({ sessionId: 1 });
74
+ UserActionSchema.index({ deviceId: 1 });
75
+ UserActionSchema.index({ timestamp: -1 });
76
+
70
77
  const UserAction = mongoose.model("UserAction", UserActionSchema);
71
78
 
72
79
  module.exports = UserAction;
package/models/View.js CHANGED
@@ -49,6 +49,14 @@ const viewsSchema = new mongoose.Schema({
49
49
  region: { type: String }
50
50
  });
51
51
 
52
+ // Indexes for performance optimization (analytics queries)
53
+ viewsSchema.index({ sellerId: 1, date: -1 });
54
+ viewsSchema.index({ storeId: 1, date: -1 });
55
+ viewsSchema.index({ productId: 1, date: -1 });
56
+ viewsSchema.index({ clientId: 1, date: -1 });
57
+ viewsSchema.index({ date: -1 });
58
+ viewsSchema.index({ region: 1 });
59
+
52
60
  module.exports = mongoose.model('View', viewsSchema);
53
61
 
54
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "database-connector",
3
- "version": "2.0.4",
3
+ "version": "2.1.1",
4
4
  "description": "MongoDB models package with Mongoose schemas for e-commerce applications. Includes User, Product, Store, Order and more with built-in validation and virtual properties.",
5
5
  "main": "models/index.js",
6
6
  "scripts": {