@storecraft/database-mongodb 1.0.15 → 1.0.16

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 CHANGED
@@ -47,6 +47,31 @@ const server = http.createServer(app.handler).listen(
47
47
 
48
48
  ```
49
49
 
50
+ ## Testing Locally (I recommend to use `Atlas`)
51
+
52
+ 1. First start a `mongo-db` server
53
+ First, make sure you have `docker` installed,
54
+ Then, run
55
+
56
+ ```bash
57
+ npm run database-mongodb:docker-compose-up
58
+ ```
59
+
60
+ 2. create Environment
61
+
62
+ create `.env` file with
63
+
64
+ ```bash
65
+ MONGODB_URL="mongodb://127.0.0.1:27017/?replicaSet=rs0"
66
+ MONGODB_NAME="main"
67
+ ```
68
+
69
+ 3. Run `tests/runner.test.js`
70
+
71
+ ```bash
72
+ npm run database-mongodb:test
73
+ ```
74
+
50
75
 
51
76
  ```text
52
77
  Author: Tomer Shalev <tomer.shalev@gmail.com>
package/index.js CHANGED
@@ -68,6 +68,7 @@ export class MongoDB {
68
68
  constructor(config) {
69
69
  this.#is_ready = false;
70
70
  this.#config = {
71
+ db_name: 'main',
71
72
  options: {
72
73
  ignoreUndefined: true,
73
74
  serverApi: {
@@ -161,6 +162,10 @@ export class MongoDB {
161
162
  return this.#mongo_client;
162
163
  }
163
164
 
165
+ get db() {
166
+ return this.mongo_client.db(this.name);
167
+ }
168
+
164
169
  /**
165
170
  *
166
171
  * @description Get the config object
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storecraft/database-mongodb",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Storecraft database driver for mongodb on node / bun / deno platform",
5
5
  "license": "MIT",
6
6
  "author": "Tomer Shalev (https://github.com/store-craft)",
@@ -33,6 +33,7 @@
33
33
  }
34
34
  },
35
35
  "scripts": {
36
+ "database-mongodb:docker-compose-up": "docker compose -f ./tests/docker-compose.yml up -d ",
36
37
  "database-mongodb:test": "node ./tests/runner.test.js",
37
38
  "test": "npm run database-mongodb:test",
38
39
  "prepublishOnly": "npm version patch --force",
@@ -6,6 +6,7 @@
6
6
  import { MongoDB } from '../index.js'
7
7
  import { Collection } from 'mongodb'
8
8
  import {
9
+ handle_or_id,
9
10
  objid_or_else_filter, sanitize_one, to_objid_safe
10
11
  } from './utils.funcs.js'
11
12
  import {
@@ -62,32 +63,37 @@ const getByEmail = (driver) => {
62
63
 
63
64
  /**
64
65
  * @param {MongoDB} driver
65
- *
66
- *
67
66
  * @returns {db_col["remove"]}
68
67
  */
69
68
  const remove = (driver) => {
70
- return async (id) => {
69
+ return async (id_or_handle) => {
70
+
71
+ const session = driver.mongo_client.startSession();
72
+ try {
73
+ await session.withTransaction(
74
+ async () => {
75
+ const filter = handle_or_id(id_or_handle);
76
+ await col(driver).deleteOne(
77
+ filter,
78
+ { session }
79
+ );
80
+ // customer and auth_user have the same object-id and handle
81
+ // so we can use the same filter
82
+ await driver.resources.customers._col.deleteOne(
83
+ filter,
84
+ { session }
85
+ );
71
86
 
72
- let filter;
73
-
74
- {
75
- if(to_objid_safe(id)) {
76
- filter = {
77
- _id: to_objid_safe(id)
78
- }
79
- } else {
80
- filter = {
81
- email: id
82
87
  }
83
- }
88
+ );
89
+ } catch(e) {
90
+ console.log(e);
91
+ return false;
92
+ } finally {
93
+ await session.endSession();
84
94
  }
85
95
 
86
- const res = await col(driver).deleteOne(
87
- filter
88
- );
89
-
90
- return Boolean(res.deletedCount)
96
+ return true;
91
97
  }
92
98
  }
93
99
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @import { db_collections as db_col } from '@storecraft/core/database'
3
3
  * @import { ProductType, VariantType } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { WithRelations } from './utils.types.js'
5
5
  * @import { Filter } from 'mongodb'
6
6
  */
7
7
 
@@ -27,8 +27,6 @@ const transactionOptions = {
27
27
 
28
28
  /**
29
29
  * @param {MongoDB} d
30
- *
31
- *
32
30
  * @returns {Collection<WithRelations<db_col["$type_get"]>>}
33
31
  */
34
32
  const col = (d) => d.collection('collections');
@@ -169,8 +167,6 @@ const count = (driver) => count_regular(driver, col(driver));
169
167
 
170
168
  /**
171
169
  * @param {MongoDB} driver
172
- *
173
- *
174
170
  * @returns {db_col["list_collection_products"]}
175
171
  */
176
172
  const list_collection_products = (driver) => {
@@ -213,6 +209,72 @@ const list_collection_products = (driver) => {
213
209
  }
214
210
  }
215
211
 
212
+
213
+ /**
214
+ * @param {MongoDB} driver
215
+ * @returns {db_col["count_collection_products"]}
216
+ */
217
+ const count_collection_products = (driver) => {
218
+ return async (handle_or_id, query) => {
219
+
220
+ const {
221
+ filter: filter_query, sort, reverse_sign
222
+ } = query_to_mongo(query);
223
+
224
+ /**
225
+ * @type {Filter<WithRelations<ProductType | VariantType>>
226
+ * }
227
+ */
228
+ const filter = {
229
+ $and: [
230
+ { '_relations.search': `col:${handle_or_id}` },
231
+ ]
232
+ };
233
+
234
+ // add the query filter
235
+ isDef(filter_query) && filter.$and.push(filter_query);
236
+
237
+ const count = await driver.resources.products._col.countDocuments(
238
+ filter
239
+ );
240
+
241
+ return count;
242
+ }
243
+ }
244
+
245
+
246
+
247
+ /**
248
+ * @param {MongoDB} driver
249
+ * @returns {db_col["list_used_products_tags"]}
250
+ */
251
+ const list_used_products_tags = (driver) => {
252
+ return async (handle_or_id) => {
253
+ const items = await driver.resources.products._col.find(
254
+ {
255
+ '_relations.search': `col:${handle_or_id}`
256
+ },
257
+ {
258
+ projection: {
259
+ tags: 1
260
+ }
261
+ }
262
+ ).toArray();
263
+
264
+ const set = (items ?? []).reduce(
265
+ (p, c) => {
266
+ c.tags.forEach(
267
+ (tag) => p.add(tag)
268
+ );
269
+ return p;
270
+ }, new Set()
271
+ )
272
+ // return array from set
273
+ return Array.from(set);
274
+ }
275
+ }
276
+
277
+
216
278
  /**
217
279
  * @param {MongoDB} driver
218
280
  *
@@ -228,6 +290,8 @@ export const impl = (driver) => {
228
290
  remove: remove(driver),
229
291
  list: list(driver),
230
292
  count: count(driver),
231
- list_collection_products: list_collection_products(driver)
293
+ list_collection_products: list_collection_products(driver),
294
+ count_collection_products: count_collection_products(driver),
295
+ list_used_products_tags: list_used_products_tags(driver),
232
296
  }
233
297
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @import { db_customers as db_col } from '@storecraft/core/database'
3
3
  * @import { OrderData } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { WithRelations } from './utils.types.js'
5
5
  * @import { Filter } from 'mongodb'
6
6
  */
7
7
 
@@ -9,7 +9,7 @@ import { Collection, ObjectId } from 'mongodb'
9
9
  import { MongoDB } from '../index.js'
10
10
  import { count_regular, get_regular, list_regular,
11
11
  upsert_regular } from './con.shared.js'
12
- import { isDef, sanitize_array, to_objid } from './utils.funcs.js'
12
+ import { handle_or_id, isDef, sanitize_array, to_objid } from './utils.funcs.js'
13
13
  import { query_to_mongo } from './utils.query.js';
14
14
 
15
15
  /**
@@ -62,8 +62,6 @@ export const email_or_id = (email_or_id) => {
62
62
 
63
63
  /**
64
64
  * @param {MongoDB} driver
65
- *
66
- *
67
65
  * @returns {db_col["remove"]}
68
66
  */
69
67
  const remove = (driver) => {
@@ -74,7 +72,7 @@ const remove = (driver) => {
74
72
  await session.withTransaction(
75
73
  async () => {
76
74
  const res = await col(driver).findOneAndDelete(
77
- email_or_id(id),
75
+ handle_or_id(id),
78
76
  { session }
79
77
  );
80
78
 
@@ -120,10 +118,10 @@ const list_customer_orders = (driver) => {
120
118
 
121
119
  const { filter: filter_query, sort, reverse_sign } = query_to_mongo(query);
122
120
 
123
- console.log('query', query)
124
- console.log('filter', JSON.stringify(filter_query, null, 2))
125
- console.log('sort', sort)
126
- console.log('expand', query?.expand)
121
+ // console.log('query', query)
122
+ // console.log('filter', JSON.stringify(filter_query, null, 2))
123
+ // console.log('sort', sort)
124
+ // console.log('expand', query?.expand)
127
125
 
128
126
  /** @type {Filter<WithRelations<OrderData>>} */
129
127
  const filter = {
@@ -147,6 +145,34 @@ const list_customer_orders = (driver) => {
147
145
  }
148
146
  }
149
147
 
148
+ /**
149
+ * @param {MongoDB} driver
150
+ * @returns {db_col["count_customer_orders"]}
151
+ */
152
+ const count_customer_orders = (driver) => {
153
+ return async (customer_id, query) => {
154
+
155
+ const { filter: filter_query } = query_to_mongo(query);
156
+
157
+ /** @type {Filter<WithRelations<OrderData>>} */
158
+ const filter = {
159
+ $and: [
160
+ {'_relations.search': `customer:${customer_id}` },
161
+ ]
162
+ };
163
+
164
+ // add the query filter
165
+ isDef(filter_query) && filter.$and.push(filter_query);
166
+
167
+ const count = await driver.resources.orders._col.countDocuments(
168
+ filter
169
+ );
170
+
171
+ return count;
172
+ }
173
+ }
174
+
175
+
150
176
  /**
151
177
  * @param {MongoDB} driver
152
178
  *
@@ -163,6 +189,7 @@ export const impl = (driver) => {
163
189
  remove: remove(driver),
164
190
  list: list(driver),
165
191
  count: count(driver),
166
- list_customer_orders: list_customer_orders(driver)
192
+ list_customer_orders: list_customer_orders(driver),
193
+ count_customer_orders: count_customer_orders(driver),
167
194
  }
168
195
  }
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @import { db_discounts as db_col } from '@storecraft/core/database'
3
3
  * @import { ProductType, VariantType } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { WithRelations } from './utils.types.js'
5
5
  * @import { Filter } from 'mongodb'
6
6
  */
7
7
 
@@ -23,7 +23,10 @@ import {
23
23
  save_me,
24
24
  update_entry_on_all_connection_of_relation
25
25
  } from './utils.relations.js'
26
-
26
+ import {
27
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db,
28
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db
29
+ } from '@storecraft/core/database'
27
30
 
28
31
  /**
29
32
  * @param {MongoDB} d
@@ -54,16 +57,27 @@ const upsert = (driver) => {
54
57
  // SEARCH
55
58
  add_search_terms_relation_on(data, search_terms);
56
59
 
57
-
58
60
  ////
59
61
  // PRODUCT --> DISCOUNTS RELATION
60
62
  ////
63
+ const extra_search_for_products =
64
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db(
65
+ data
66
+ );
67
+
68
+ const extra_tags_for_products =
69
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db(
70
+ data
71
+ );
61
72
 
62
73
  // first remove discount from anywhere
63
74
  await remove_entry_from_all_connection_of_relation(
64
75
  driver, 'products', 'discounts', objid, session,
65
- [
66
- `discount:${data.handle}`, `discount:${data.id}`
76
+ [ // remove search terms
77
+ ...extra_search_for_products
78
+ ],
79
+ [ // remove tags
80
+ ...extra_tags_for_products
67
81
  ]
68
82
  );
69
83
 
@@ -77,8 +91,11 @@ const upsert = (driver) => {
77
91
  $addToSet: {
78
92
  '_relations.discounts.ids': objid,
79
93
  '_relations.search': {
80
- $each : [ `discount:${data.handle}`, `discount:${data.id}` ]
81
- }
94
+ $each : extra_search_for_products
95
+ },
96
+ 'tags': {
97
+ $each : extra_tags_for_products
98
+ }
82
99
  },
83
100
 
84
101
  },
@@ -145,13 +162,27 @@ const remove = (driver) => {
145
162
  try {
146
163
  await session.withTransaction(
147
164
  async () => {
165
+
166
+ const extra_search_for_products =
167
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db(
168
+ item
169
+ );
170
+
171
+ const extra_tags_for_products =
172
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db(
173
+ item
174
+ );
175
+
148
176
  ////
149
177
  // PRODUCT -> DISCOUNTS RELATION
150
178
  ////
151
179
  await remove_entry_from_all_connection_of_relation(
152
180
  driver, 'products', 'discounts', objid, session,
153
181
  [
154
- `discount:${item.handle}`, `discount:${item.id}`
182
+ ...extra_search_for_products
183
+ ],
184
+ [
185
+ ...extra_tags_for_products
155
186
  ]
156
187
  );
157
188
 
@@ -196,8 +227,6 @@ const count = (driver) => count_regular(driver, col(driver));
196
227
 
197
228
  /**
198
229
  * @param {MongoDB} driver
199
- *
200
- *
201
230
  * @returns {db_col["list_discount_products"]}
202
231
  */
203
232
  const list_discount_products = (driver) => {
@@ -235,6 +264,67 @@ const list_discount_products = (driver) => {
235
264
  }
236
265
  }
237
266
 
267
+
268
+ /**
269
+ * @param {MongoDB} driver
270
+ * @returns {db_col["list_all_discount_products_tags"]}
271
+ */
272
+ const list_all_discount_products_tags = (driver) => {
273
+ return async (handle_or_id) => {
274
+ const items = await driver.resources.products._col.find(
275
+ {
276
+ '_relations.search': `discount:${handle_or_id}`
277
+ },
278
+ {
279
+ projection: {
280
+ tags: 1
281
+ }
282
+ }
283
+ ).toArray();
284
+
285
+ const set = (items ?? []).reduce(
286
+ (p, c) => {
287
+ c.tags.forEach(
288
+ (tag) => p.add(tag)
289
+ );
290
+ return p;
291
+ }, new Set()
292
+ );
293
+
294
+ // return array from set
295
+ return Array.from(set);
296
+ }
297
+ }
298
+
299
+
300
+ /**
301
+ * @param {MongoDB} driver
302
+ * @returns {db_col["count_discount_products"]}
303
+ */
304
+ const count_discount_products = (driver) => {
305
+ return async (handle_or_id, query) => {
306
+
307
+ const { filter: filter_query, sort, reverse_sign } = query_to_mongo(query);
308
+
309
+ /** @type {Filter<WithRelations<ProductType | VariantType>>} */
310
+ const filter = {
311
+ $and: [
312
+ { '_relations.search': `discount:${handle_or_id}` },
313
+ ]
314
+ };
315
+
316
+ // add the query filter
317
+ isDef(filter_query) && filter.$and.push(filter_query);
318
+
319
+ const count = await driver.resources.products._col.countDocuments(
320
+ filter
321
+ );
322
+
323
+ return count;
324
+ }
325
+ }
326
+
327
+
238
328
  /**
239
329
  * @param {MongoDB} driver
240
330
  *
@@ -251,6 +341,8 @@ export const impl = (driver) => {
251
341
  remove: remove(driver),
252
342
  list: list(driver),
253
343
  count: count(driver),
254
- list_discount_products: list_discount_products(driver)
344
+ list_discount_products: list_discount_products(driver),
345
+ list_all_discount_products_tags: list_all_discount_products_tags(driver),
346
+ count_discount_products: count_discount_products(driver)
255
347
  }
256
348
  }
package/src/con.posts.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @import { db_posts as db_col } from '@storecraft/core/database'
3
- * @import { WithRelations } from './utils.relations.js'
3
+ * @import { WithRelations } from './utils.types.js'
4
4
  */
5
5
 
6
6
  import { Collection } from 'mongodb'
@@ -1,14 +1,14 @@
1
1
  /**
2
2
  * @import { db_products as db_col, RegularGetOptions } from '@storecraft/core/database'
3
3
  * @import { ProductType, VariantType } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { WithRelations } from './utils.types.js'
5
5
  * @import { Filter, AnyBulkWriteOperation } from 'mongodb'
6
6
  */
7
7
 
8
8
  import { Collection } from 'mongodb'
9
9
  import { MongoDB } from '../index.js'
10
10
  import {
11
- count_regular, expand, get_bulk, get_regular, list_regular,
11
+ count_regular, get_bulk, get_regular, list_regular,
12
12
  zeroed_relations
13
13
  } from './con.shared.js'
14
14
  import {
@@ -31,6 +31,10 @@ import { union } from '@storecraft/core/api/utils.func.js'
31
31
  import {
32
32
  test_product_filters_against_product
33
33
  } from '@storecraft/core/api/con.pricing.logic.js'
34
+ import {
35
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db,
36
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db
37
+ } from '@storecraft/core/database'
34
38
 
35
39
  /**
36
40
  * @param {MongoDB} d
@@ -104,13 +108,25 @@ const upsert = (driver) => {
104
108
  )
105
109
  }
106
110
 
111
+ replacement.tags = union(
112
+ [
113
+ // remove old discount tags
114
+ replacement.tags?.filter(t => !t.startsWith('discount_')),
115
+ // add new discount tags
116
+ eligible_discounts.map(
117
+ helper_compute_product_extra_tags_because_of_discount_side_effect_for_db
118
+ ),
119
+ ]
120
+ );
121
+
107
122
  // SEARCH
108
123
  add_search_terms_relation_on(
109
124
  replacement, union(
110
125
  [
111
126
  search_terms,
112
- eligible_discounts.map(d => `discount:${d.handle}`),
113
- eligible_discounts.map(d => `discount:${d.id}`),
127
+ eligible_discounts.map(
128
+ helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db
129
+ ),
114
130
  ]
115
131
  )
116
132
  );
@@ -295,12 +311,8 @@ const count = (driver) => count_regular(driver, col(driver));
295
311
  * For now and because each product is related to very few
296
312
  * collections, I will not expose the query api, and use aggregate
297
313
  * instead.
298
- *
299
- *
300
314
  * @param {MongoDB} driver
301
- *
302
- *
303
- * @returns {db_col["list_product_collections"]}
315
+ * @returns {db_col["list_all_product_collections"]}
304
316
  */
305
317
  const list_product_collections = (driver) => {
306
318
  return async (product) => {
@@ -311,7 +323,7 @@ const list_product_collections = (driver) => {
311
323
 
312
324
  // We have collections embedded in products, so let's use it
313
325
  const item = await get_regular(driver, col(driver))(product, options);
314
-
326
+
315
327
  return sanitize_array(item?.collections ?? []);
316
328
  }
317
329
  }
@@ -325,7 +337,7 @@ const list_product_collections = (driver) => {
325
337
  * @param {MongoDB} driver
326
338
  *
327
339
  *
328
- * @returns {db_col["list_product_variants"]}
340
+ * @returns {db_col["list_all_product_variants"]}
329
341
  */
330
342
  const list_product_variants = (driver) => {
331
343
  return async (product) => {
@@ -355,7 +367,7 @@ const list_product_variants = (driver) => {
355
367
  * @param {MongoDB} driver
356
368
  *
357
369
  *
358
- * @returns {db_col["list_related_products"]}
370
+ * @returns {db_col["list_all_related_products"]}
359
371
  */
360
372
  const list_related_products = (driver) => {
361
373
  return async (product) => {
@@ -376,7 +388,7 @@ const list_related_products = (driver) => {
376
388
  * @param {MongoDB} driver
377
389
  *
378
390
  *
379
- * @returns {db_col["list_product_discounts"]}
391
+ * @returns {db_col["list_all_product_discounts"]}
380
392
  */
381
393
  const list_product_discounts = (driver) => {
382
394
  return async (product) => {
@@ -504,11 +516,39 @@ const changeStockOfBy = (driver) => {
504
516
  }
505
517
 
506
518
 
519
+ /**
520
+ * @param {MongoDB} driver
521
+ * @returns {db_col["list_used_products_tags"]}
522
+ */
523
+ const list_used_products_tags = (driver) => {
524
+ return async () => {
525
+ const items = await driver.resources.products._col.find(
526
+ {
527
+ },
528
+ {
529
+ projection: {
530
+ tags: 1
531
+ }
532
+ }
533
+ ).toArray();
534
+
535
+ const set = (items ?? []).reduce(
536
+ (p, c) => {
537
+ c.tags.forEach(
538
+ (tag) => p.add(tag)
539
+ );
540
+ return p;
541
+ }, new Set()
542
+ )
543
+ // return array from set
544
+ return Array.from(set);
545
+ }
546
+ }
547
+
548
+
507
549
  /**
508
550
  * @param {MongoDB} driver
509
- *
510
- *
511
- * @return {db_col & { _col: ReturnType<col> }}
551
+ * @return {db_col & { _col: ReturnType<typeof col> }}
512
552
  */
513
553
  export const impl = (driver) => {
514
554
 
@@ -522,10 +562,11 @@ export const impl = (driver) => {
522
562
  list: list(driver),
523
563
  add_product_to_collection: add_product_to_collection(driver),
524
564
  remove_product_from_collection: remove_product_from_collection(driver),
525
- list_product_collections: list_product_collections(driver),
526
- list_product_variants: list_product_variants(driver),
527
- list_related_products: list_related_products(driver),
528
- list_product_discounts: list_product_discounts(driver),
565
+ list_all_product_collections: list_product_collections(driver),
566
+ list_all_product_variants: list_product_variants(driver),
567
+ list_all_related_products: list_related_products(driver),
568
+ list_all_product_discounts: list_product_discounts(driver),
569
+ list_used_products_tags: list_used_products_tags(driver),
529
570
  count: count(driver)
530
571
  }
531
572
  }