@storecraft/database-mongodb 1.0.14 → 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.
@@ -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
  }
package/src/con.search.js CHANGED
@@ -74,10 +74,12 @@ export const quicksearch = (driver) => {
74
74
 
75
75
  const { filter, sort, reverse_sign } = query_to_mongo(query);
76
76
  const expand = query.expand ?? ['*'];
77
- const tables_filtered = tables.filter(t => expand.includes('*') || expand.includes(t));
77
+ const tables_filtered = tables.filter(
78
+ t => expand.includes('*') || expand.includes(t)
79
+ );
78
80
 
79
81
  if(tables_filtered.length==0)
80
- return {};
82
+ return {};
81
83
 
82
84
  const pipeline = [
83
85
  {
@@ -101,8 +103,6 @@ export const quicksearch = (driver) => {
101
103
  ];
102
104
 
103
105
  const db = driver.mongo_client.db(driver.name);
104
-
105
-
106
106
  const items = /** @type {QuickSearchResource[]} */ (
107
107
  await db.collection(tables_filtered[0]).aggregate(
108
108
  [
package/src/con.shared.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * @import { db_crud, RegularGetOptions } from '@storecraft/core/database'
3
3
  * @import { ApiQuery, BaseType, Cursor, ExpandQuery, QuickSearchResource, QuickSearchResult, Tuple, withOptionalID } from '@storecraft/core/api'
4
4
  * @import { VQL } from '@storecraft/core/vql'
5
- * @import { WithRelations } from './utils.relations.js'
5
+ * @import { WithRelations } from './utils.types.js'
6
6
  * @import { WithId } from 'mongodb'
7
7
  */
8
8
 
@@ -43,17 +43,49 @@ export const upsert_regular = (driver, col) => {
43
43
  ]
44
44
  );
45
45
 
46
- const res = await col.replaceOne(
46
+ // if(!data.handle) {
47
+ // throw new Error('Handle is required');
48
+ // }
49
+
50
+ await col.deleteMany(
47
51
  // @ts-ignore
52
+ {
53
+ $or: [
54
+ data.id && { _id: to_objid(data.id) },
55
+ data.handle && { handle: data.handle }
56
+ ].filter(Boolean)
57
+ },
58
+ {
59
+ session
60
+ }
61
+ );
62
+
63
+ await col.insertOne(
64
+ // @ts-ignore
65
+ {
66
+ ...data,
67
+ _id: data.id ? to_objid(data.id) : undefined,
68
+ },
48
69
  {
49
- _id: to_objid(data.id)
50
- },
51
- data,
52
- {
53
- session, upsert: true
70
+ session,
54
71
  }
72
+
55
73
  );
56
74
 
75
+ // const res = await col.replaceOne(
76
+ // // @ts-ignore
77
+ // {
78
+ // $or: [
79
+ // data.id && { _id: to_objid(data.id) },
80
+ // data.handle && { handle: data.handle }
81
+ // ].filter(Boolean)
82
+ // },
83
+ // data,
84
+ // {
85
+ // session, upsert: true
86
+ // }
87
+ // );
88
+
57
89
  ////
58
90
  // REPORT IMAGES USAGE
59
91
  ////
@@ -74,11 +106,7 @@ export const upsert_regular = (driver, col) => {
74
106
 
75
107
  /**
76
108
  * Extract relations names from item
77
- *
78
- *
79
- * @template {import('./utils.relations.js').WithRelations<{}>} T
80
- *
81
- *
109
+ * @template {WithRelations<{}>} T
82
110
  * @param {T} item
83
111
  */
84
112
  export const get_relations_names = item => {
@@ -165,10 +193,8 @@ export const expand_to_mongo_projection = (expand) => {
165
193
 
166
194
  /**
167
195
  * @template T, G
168
- *
169
196
  * @param {MongoDB} driver
170
197
  * @param {Collection<G>} col
171
- *
172
198
  * @returns {db_crud<T, G>["get"]}
173
199
  */
174
200
  export const get_regular = (driver, col) => {
@@ -184,25 +210,26 @@ export const get_regular = (driver, col) => {
184
210
  );
185
211
 
186
212
  // try to expand relations
187
- expand([res], options?.expand);
213
+ expand(
214
+ [res],
215
+ /** @type {ExpandQuery<WithId<G>>} */(
216
+ options?.expand
217
+ )
218
+ );
188
219
 
189
- return sanitize_one(res);
220
+ return /** @type {G} */(
221
+ sanitize_one(res)
222
+ );
190
223
  }
191
224
  }
192
225
 
193
226
  /**
194
227
  * get bulk of items, ordered, if something is missing, `undefined`
195
228
  * should be instead
196
- *
197
- *
198
229
  * @template {withOptionalID} T
199
230
  * @template {withOptionalID} G
200
- *
201
- *
202
231
  * @param {MongoDB} driver
203
232
  * @param {Collection<G>} col
204
- *
205
- *
206
233
  * @returns {db_crud<T, G>["getBulk"]}
207
234
  */
208
235
  export const get_bulk = (driver, col) => {
@@ -213,27 +240,40 @@ export const get_bulk = (driver, col) => {
213
240
 
214
241
 
215
242
 
216
- const res = await col.find(
217
- // @ts-ignore
218
- {
219
- $or: [
220
- {
221
- _id: { $in: objids }
222
- },
223
- {
224
- handle: { $in: ids }
225
- }
226
- ]
227
- }
228
- ).toArray();
243
+ const res = /** @type {(WithRelations<WithId<G>>)[]} */ (
244
+ await col.find(
245
+ // @ts-ignore
246
+ {
247
+ $or: [
248
+ {
249
+ _id: { $in: objids }
250
+ },
251
+ {
252
+ handle: { $in: ids }
253
+ }
254
+ ]
255
+ }
256
+ ).toArray()
257
+ );
229
258
 
230
259
  // try to expand relations
231
- expand(res, options?.expand);
232
- const sanitized = sanitize_array(res);
233
- // console.log('res', sanitized)
260
+ expand(
261
+ res,
262
+ /** @type {ExpandQuery<WithId<G>>} */ (
263
+ options?.expand
264
+ )
265
+ );
266
+
267
+
268
+ const sanitized = /** @type {G[]} */(
269
+ /** @type {unknown} */(
270
+ sanitize_array(res)
271
+ )
272
+ );
273
+
234
274
  // now let's order them
235
275
  return ids.map(
236
- id => sanitized.find(s => s.id===id || s?.handle===id)
276
+ id => sanitized.find(s => s.id===id || s?.['handle']===id)
237
277
  );
238
278
 
239
279
  }
@@ -264,12 +304,8 @@ export const remove_regular = (driver, col) => {
264
304
  /**
265
305
  * @template T
266
306
  * @template G
267
- *
268
- *
269
307
  * @param {MongoDB} driver
270
308
  * @param {Collection<G>} col
271
- *
272
- *
273
309
  * @returns {db_crud<T, G>["list"]}
274
310
  */
275
311
  export const list_regular = (driver, col) => {
@@ -295,13 +331,18 @@ export const list_regular = (driver, col) => {
295
331
  if(reverse_sign==-1) items.reverse();
296
332
 
297
333
  // try expand relations, that were asked
298
- const items_expended = expand(items, query?.expand);
334
+ const items_expended = expand(
335
+ items,
336
+ /** @type {ExpandQuery<WithId<G>>} */ (
337
+ query?.expand
338
+ )
339
+ );
299
340
 
300
341
  const sanitized = sanitize_array(items_expended);
301
342
 
302
343
  // console.log('sanitized', sanitized)
303
344
 
304
- return sanitized;
345
+ return /** @type {G[]} */(sanitized);
305
346
  }
306
347
  }
307
348
 
@@ -323,7 +364,6 @@ export const count_regular = (driver, col) => {
323
364
 
324
365
  // console.log('query', query);
325
366
  // console.log('filter', JSON.stringify(filter, null, 2));
326
-
327
367
  const count = await col.countDocuments(
328
368
  filter
329
369
  );
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @import { db_shipping 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'
@@ -16,8 +16,6 @@ import {
16
16
 
17
17
  /**
18
18
  * @param {MongoDB} d
19
- *
20
- *
21
19
  * @returns {Collection<WithRelations<db_col["$type_get"]>>}
22
20
  */
23
21
  const col = (d) => d.collection('shipping_methods');
@@ -2,14 +2,15 @@
2
2
  * @import {
3
3
  * db_storefronts as db_col, RegularGetOptions
4
4
  * } from '@storecraft/core/database'
5
- * @import { WithRelations } from './utils.relations.js'
5
+ * @import { StorefrontType } from '@storecraft/core/api'
6
+ * @import { WithRelations } from './utils.types.js'
6
7
  */
7
8
 
8
9
  import { Collection } from 'mongodb'
9
10
  import { MongoDB } from '../index.js'
10
- import { count_regular, get_regular, list_regular,
11
+ import { count_regular, expand, get_regular, list_regular,
11
12
  remove_regular } from './con.shared.js'
12
- import { sanitize_array, to_objid } from './utils.funcs.js'
13
+ import { sanitize_array, sanitize_recursively, to_objid } from './utils.funcs.js'
13
14
  import {
14
15
  add_search_terms_relation_on, create_explicit_relation, save_me
15
16
  } from './utils.relations.js';
@@ -17,7 +18,6 @@ import { report_document_media } from './con.images.js';
17
18
 
18
19
  /**
19
20
  * @param {MongoDB} d
20
- *
21
21
  * @returns {Collection<WithRelations<db_col["$type_get"]>>}
22
22
  */
23
23
  const col = (d) => d.collection('storefronts');
@@ -106,95 +106,118 @@ const count = (driver) => count_regular(driver, col(driver));
106
106
 
107
107
  /**
108
108
  * @param {MongoDB} driver
109
- *
110
- *
111
- * @returns {db_col["list_storefront_products"]}
112
- */
113
- const list_storefront_products = (driver) => {
114
- return async (product) => {
115
- /** @type {RegularGetOptions} */
116
- const options = {
117
- expand: ['products']
118
- };
119
-
120
- const item = await get_regular(driver, col(driver))(product, options);
121
-
122
- return sanitize_array(item?.products ?? []);
123
- }
124
- }
125
-
126
- /**
127
- * @param {MongoDB} driver
128
- *
129
- *
130
- * @returns {db_col["list_storefront_collections"]}
131
- */
132
- const list_storefront_collections = (driver) => {
133
- return async (product) => {
134
- /** @type {RegularGetOptions} */
135
- const options = {
136
- expand: ['collections']
137
- };
138
-
139
- const item = await get_regular(driver, col(driver))(product, options);
140
-
141
- return sanitize_array(item?.collections ?? []);
142
- }
143
- }
144
-
145
- /**
146
- * @param {MongoDB} driver
147
- *
148
- *
149
- * @returns {db_col["list_storefront_discounts"]}
150
- */
151
- const list_storefront_discounts = (driver) => {
152
- return async (product) => {
153
- /** @type {RegularGetOptions} */
154
- const options = {
155
- expand: ['discounts']
156
- };
157
-
158
- const item = await get_regular(driver, col(driver))(product, options);
159
-
160
- return sanitize_array(item?.discounts ?? []);
161
- }
162
- }
163
-
164
- /**
165
- * @param {MongoDB} driver
166
- *
167
- * @returns {db_col["list_storefront_shipping_methods"]}
109
+ * @returns {db_col["get_default_auto_generated_storefront"]}
168
110
  */
169
- const list_storefront_shipping_methods = (driver) => {
170
- return async (product) => {
171
- /** @type {RegularGetOptions} */
172
- const options = {
173
- expand: ['shipping_methods']
174
- };
175
-
176
- const item = await get_regular(driver, col(driver))(product, options);
177
-
178
- return sanitize_array(item?.shipping_methods ?? []);
179
- }
180
- }
111
+ const get_default_auto_generated_storefront = (driver) => {
112
+ return async () => {
113
+ /** @type {Partial<StorefrontType>[]} */
114
+ const items = await driver.db.aggregate(
115
+ [
116
+ { $documents: [{}] },
117
+ {
118
+ $lookup: {
119
+ from: "products",
120
+ pipeline: [
121
+ { $match: { active: true } },
122
+ { $sort: { updated_at: -1} },
123
+ { $limit: 10 },
124
+ ],
125
+ as: "products"
126
+ }
127
+ },
128
+ {
129
+ $lookup: {
130
+ from: "collections",
131
+ pipeline: [
132
+ { $match: { active: true } },
133
+ { $sort: { updated_at: -1} },
134
+ ],
135
+ as: "collections"
136
+ }
137
+ },
138
+ {
139
+ $lookup: {
140
+ from: "discounts",
141
+ pipeline: [
142
+ { $match: { active: true } },
143
+ { $sort: { updated_at: -1} },
144
+ ],
145
+ as: "discounts"
146
+ }
147
+ },
148
+ {
149
+ $lookup: {
150
+ from: "shipping_methods",
151
+ pipeline: [
152
+ { $match: { active: true } },
153
+ { $sort: { updated_at: -1} },
154
+ ],
155
+ as: "shipping_methods"
156
+ }
157
+ },
158
+ {
159
+ $lookup: {
160
+ from: "posts",
161
+ pipeline: [
162
+ { $match: { active: true } },
163
+ { $sort: { updated_at: -1} },
164
+ { $limit: 5 },
165
+ ],
166
+ as: "posts"
167
+ }
168
+ },
169
+ {
170
+ $lookup: {
171
+ from: "products",
172
+ pipeline: [
173
+ { $match: { active: true, tags: { $exists: true } } },
174
+ { $project: { tags: 1 }}
175
+ ],
176
+ as: "all_used_products_tags"
177
+ }
178
+ },
179
+
180
+ ]
181
+ ).toArray();
182
+
183
+ const pre_all_tags = /** @type {{tags?: string[]}[]} */(
184
+ items[0].all_used_products_tags ?? []
185
+ );
186
+ const all_used_products_tags = pre_all_tags.reduce(
187
+ (p, c) => {
188
+ (c?.tags ?? []).forEach(
189
+ (tag) => p.add(tag)
190
+ );
191
+ return p;
192
+ },
193
+ /** @type {Set<string>} */ (new Set())
194
+ );
195
+
196
+ /** @type {StorefrontType} */
197
+ let sf = {
198
+ ...items[0],
199
+ active: true,
200
+ created_at: new Date().toISOString(),
201
+ handle: 'default-auto-generated-storefront',
202
+ id: 'default',
203
+ title: 'Default Auto Generated Storefront',
204
+ description: 'Default Auto Generated Storefront',
205
+ all_used_products_tags: Array.from(all_used_products_tags)
206
+ }
181
207
 
182
- /**
183
- * @param {MongoDB} driver
184
- *
185
- *
186
- * @returns {db_col["list_storefront_posts"]}
187
- */
188
- const list_storefront_posts = (driver) => {
189
- return async (product) => {
190
- /** @type {RegularGetOptions} */
191
- const options = {
192
- expand: ['posts']
193
- };
208
+ expand(
209
+ sf.products,
210
+ [
211
+ 'discounts', 'collections',
212
+ 'related_products', 'variants'
213
+ ],
214
+ );
194
215
 
195
- const item = await get_regular(driver, col(driver))(product, options);
216
+ sanitize_recursively(
217
+ sf
218
+ );
196
219
 
197
- return sanitize_array(item?.posts ?? []);
220
+ return sf;
198
221
  }
199
222
  }
200
223
 
@@ -213,10 +236,6 @@ export const impl = (driver) => {
213
236
  remove: remove(driver),
214
237
  list: list(driver),
215
238
  count: count(driver),
216
- list_storefront_products: list_storefront_products(driver),
217
- list_storefront_collections: list_storefront_collections(driver),
218
- list_storefront_discounts: list_storefront_discounts(driver),
219
- list_storefront_shipping_methods: list_storefront_shipping_methods(driver),
220
- list_storefront_posts: list_storefront_posts(driver),
239
+ get_default_auto_generated_storefront: get_default_auto_generated_storefront(driver),
221
240
  }
222
241
  }