@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/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
  }
@@ -1,3 +1,7 @@
1
+ /**
2
+ * @import { WithRelations } from './utils.types.js';
3
+ * @import { Filter } from 'mongodb';
4
+ */
1
5
  import { ObjectId } from 'mongodb';
2
6
 
3
7
  /** @param {any} v */
@@ -14,11 +18,7 @@ export const delete_keys = (...keys) => {
14
18
 
15
19
  /**
16
20
  * @template T
17
- *
18
- *
19
21
  * @param {T} o
20
- *
21
- *
22
22
  * @returns {T}
23
23
  */
24
24
  return (o) => {
@@ -30,16 +30,9 @@ export const delete_keys = (...keys) => {
30
30
 
31
31
  /**
32
32
  * Sanitize hidden properties in-place
33
- *
34
- *
35
33
  * @template {object} T
36
- *
37
- *
38
34
  * @param {T} o
39
- *
40
- *
41
35
  * @return {Omit<T, '_id' | '_relations'>}
42
- *
43
36
  */
44
37
  export const sanitize_hidden = o => {
45
38
  if(!isDef(o))
@@ -53,27 +46,26 @@ export const sanitize_hidden = o => {
53
46
  }
54
47
 
55
48
  /**
56
- *
57
- * @template T
58
- *
59
- *
49
+ * Sanitize hidden properties in-place recursively
50
+ * @template {object} T
60
51
  * @param {T} o
61
- *
62
- *
63
- * @returns {T}
52
+ * @return {Omit<T, '_id' | '_relations'>}
64
53
  */
65
- export const delete_id = o => {
66
- return delete_keys('_id')(o)
54
+ export const sanitize_recursively = o => {
55
+ for(const k of Object.keys(o)) {
56
+ if(k.startsWith('_')) {
57
+ delete o[k];
58
+ } else if(typeof o[k] === 'object') {
59
+ sanitize_recursively(o[k]);
60
+ }
61
+ }
62
+ return o;
67
63
  }
68
64
 
69
65
  /**
70
- *
71
66
  * Sanitize the mongo document before sending to client
72
- *
73
67
  * @template T
74
- *
75
- *
76
- * @param {T} o
68
+ * @param {WithRelations<T>} o
77
69
  */
78
70
  export const sanitize_one = o => {
79
71
  return sanitize_hidden(o)
@@ -81,30 +73,31 @@ export const sanitize_one = o => {
81
73
 
82
74
  /**
83
75
  * Sanitize the mongo document before sending to client
84
- *
85
76
  * @template T
86
- *
87
- *
88
- * @param {T[]} o
89
- *
77
+ * @param {WithRelations<T>[]} o
90
78
  */
91
79
  export const sanitize_array = o => {
92
80
  return o?.map(sanitize_hidden);
93
81
  }
94
82
 
95
83
  /**
96
- *
84
+ * @template T
85
+ * @param {T} o
86
+ * @returns {T}
87
+ */
88
+ export const delete_id = o => {
89
+ return delete_keys('_id')(o)
90
+ }
91
+
92
+ /**
97
93
  * @param {string} id
98
- *
99
94
  */
100
95
  export const to_objid = id => {
101
96
  return new ObjectId(id.split('_').at(-1))
102
97
  }
103
98
 
104
99
  /**
105
- *
106
100
  * @param {string} id
107
- *
108
101
  */
109
102
  export const to_objid_safe = id => {
110
103
  try {
@@ -116,27 +109,20 @@ export const to_objid_safe = id => {
116
109
  }
117
110
 
118
111
  /**
112
+ * Create a `filter` for `object-id` or `handle`
119
113
  * @template {{handle?: string}} G
120
- *
121
- *
122
114
  * @param {string} handle_or_id
123
- *
124
- *
125
- * @returns {import('mongodb').Filter<G>}
115
+ * @returns {Filter<G>}
126
116
  */
127
117
  export const handle_or_id = (handle_or_id) => {
128
- return objid_or_else_filter(handle_or_id);
118
+ return objid_or_else_filter(handle_or_id, 'handle');
129
119
  }
130
120
 
131
121
 
132
122
  /**
133
123
  * @template {{handle?: string}} G
134
- *
135
- *
136
124
  * @param {string} id_or_else
137
- *
138
- *
139
- * @returns {import('mongodb').Filter<G>}
125
+ * @returns {Filter<G>}
140
126
  */
141
127
  export const objid_or_else_filter = (id_or_else, else_key='handle') => {
142
128
  try {
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * @import { ID } from '@storecraft/core/database'
3
3
  * @import { BaseType } from '@storecraft/core/api'
4
- * @import { WithRelations } from './utils.relations.js'
4
+ * @import { Relation, WithRelations } from './utils.types.js'
5
5
  * @import { Filter } from 'mongodb'
6
6
  */
7
7
 
@@ -10,20 +10,6 @@ import { isDef, isUndef, to_objid } from './utils.funcs.js';
10
10
  import { MongoDB } from '../index.js';
11
11
  import { zeroed_relations } from './con.shared.js';
12
12
 
13
- /**
14
- * @template {any} T
15
- *
16
- * @typedef {Object} Relation
17
- * @property {ObjectId[]} [ids]
18
- * @property {Record<ID, T>} [entries]
19
- */
20
-
21
- /**
22
- * @template {any} T
23
- *
24
- * @typedef {T & { _relations? : Record<string, Relation<any>> }} WithRelations
25
- */
26
-
27
13
  /**
28
14
  *
29
15
  * On upsert Create a relation on a given field that represents a relation.
@@ -259,12 +245,13 @@ export const update_specific_connection_of_relation = (
259
245
  * @param {ObjectId} entry_objid the proper `ObjectId` of the entry
260
246
  * @param {ClientSession} [session] client `session` for atomicity purposes
261
247
  * @param {string[]} [search_terms_to_remove=[]] Extra `search` terms to remove
248
+ * @param {string[]} [tags_to_remove=[]] Extra `tags` terms to remove
262
249
  * from all the connections
263
250
  *
264
251
  */
265
252
  export const remove_entry_from_all_connection_of_relation = (
266
253
  driver, collection, relation_name, entry_objid, session,
267
- search_terms_to_remove=[]
254
+ search_terms_to_remove=[], tags_to_remove=[]
268
255
  ) => {
269
256
  return driver.collection(collection).updateMany(
270
257
  {
@@ -273,7 +260,8 @@ export const remove_entry_from_all_connection_of_relation = (
273
260
  {
274
261
  $pull: {
275
262
  [`_relations.${relation_name}.ids`] : entry_objid,
276
- '_relations.search': { $in : search_terms_to_remove }
263
+ '_relations.search': { $in : search_terms_to_remove },
264
+ 'tags': { $in : tags_to_remove },
277
265
  },
278
266
  $unset: {
279
267
  [`_relations.${relation_name}.entries.${entry_objid.toString()}`]: ''