@storecraft/database-mongodb 1.0.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/README.md +50 -0
- package/db-strategy.md +284 -0
- package/index.js +193 -0
- package/jsconfig.json +14 -0
- package/migrate.js +39 -0
- package/migrations/00000_init_tables.js +60 -0
- package/migrations/00001_seed_email_templates.js +271 -0
- package/package.json +38 -0
- package/src/con.auth_users.js +147 -0
- package/src/con.collections.js +232 -0
- package/src/con.customers.js +172 -0
- package/src/con.discounts.js +261 -0
- package/src/con.discounts.utils.js +137 -0
- package/src/con.images.js +173 -0
- package/src/con.notifications.js +101 -0
- package/src/con.orders.js +61 -0
- package/src/con.posts.js +149 -0
- package/src/con.products.js +537 -0
- package/src/con.search.js +162 -0
- package/src/con.shared.js +333 -0
- package/src/con.shipping.js +153 -0
- package/src/con.storefronts.js +223 -0
- package/src/con.tags.js +62 -0
- package/src/con.templates.js +62 -0
- package/src/utils.funcs.js +152 -0
- package/src/utils.query.js +186 -0
- package/src/utils.relations.js +410 -0
- package/tests/mongo-ping.test.js +34 -0
- package/tests/query.cursor.test.js +389 -0
- package/tests/query.vql.test.js +71 -0
- package/tests/runner.test.js +35 -0
- package/tests/sandbox.test.js +56 -0
- package/types.public.d.ts +22 -0
@@ -0,0 +1,537 @@
|
|
1
|
+
import { Collection } from 'mongodb'
|
2
|
+
import { MongoDB } from '../index.js'
|
3
|
+
import {
|
4
|
+
count_regular, expand, get_bulk, get_regular, list_regular,
|
5
|
+
zeroed_relations
|
6
|
+
} from './con.shared.js'
|
7
|
+
import {
|
8
|
+
delete_keys, handle_or_id, sanitize_array, sanitize_one, to_objid
|
9
|
+
} from './utils.funcs.js'
|
10
|
+
import {
|
11
|
+
add_search_terms_relation_on, create_explicit_relation,
|
12
|
+
delete_me,
|
13
|
+
remove_entry_from_all_connection_of_relation,
|
14
|
+
remove_specific_connection_of_relation,
|
15
|
+
remove_specific_connection_of_relation_with_filter,
|
16
|
+
save_me,
|
17
|
+
update_entry_on_all_connection_of_relation,
|
18
|
+
update_specific_connection_of_relation,
|
19
|
+
update_specific_connection_of_relation_with_filter
|
20
|
+
} from './utils.relations.js'
|
21
|
+
import { enums } from '@storecraft/core/v-api'
|
22
|
+
import { report_document_media } from './con.images.js'
|
23
|
+
import { union } from '@storecraft/core/v-api/utils.func.js'
|
24
|
+
import {
|
25
|
+
test_product_filters_against_product
|
26
|
+
} from '@storecraft/core/v-api/con.pricing.logic.js'
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @typedef {import('@storecraft/core/v-database').db_products} db_col
|
30
|
+
*/
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @param {MongoDB} d
|
34
|
+
*
|
35
|
+
*
|
36
|
+
* @returns {Collection<
|
37
|
+
* import('./utils.relations.js').WithRelations<db_col["$type_get"]>
|
38
|
+
* >}
|
39
|
+
*
|
40
|
+
*/
|
41
|
+
const col = (d) => d.collection('products');
|
42
|
+
|
43
|
+
/**
|
44
|
+
* @param {MongoDB} driver
|
45
|
+
*
|
46
|
+
*
|
47
|
+
* @returns {db_col["upsert"]}
|
48
|
+
*/
|
49
|
+
const upsert = (driver) => {
|
50
|
+
return async (data, search_terms=[]) => {
|
51
|
+
// console.log('search_terms', search_terms)
|
52
|
+
data = {...data};
|
53
|
+
|
54
|
+
const objid = to_objid(data.id);
|
55
|
+
const session = driver.mongo_client.startSession();
|
56
|
+
|
57
|
+
try {
|
58
|
+
await session.withTransaction(
|
59
|
+
async () => {
|
60
|
+
|
61
|
+
////
|
62
|
+
// COLLECTIONS RELATION (explicit)
|
63
|
+
////
|
64
|
+
let replacement = await create_explicit_relation(
|
65
|
+
driver, data, 'collections', 'collections', true
|
66
|
+
);
|
67
|
+
|
68
|
+
////
|
69
|
+
// PRODUCTS -> Related Products RELATION (explicit)
|
70
|
+
////
|
71
|
+
replacement = await create_explicit_relation(
|
72
|
+
driver, replacement, 'related_products', 'products', true
|
73
|
+
);
|
74
|
+
|
75
|
+
////
|
76
|
+
// DISCOUNTS RELATION
|
77
|
+
////
|
78
|
+
// get all automatic + active discounts
|
79
|
+
const discounts = await driver.resources.discounts._col.find(
|
80
|
+
{
|
81
|
+
'application.id': enums.DiscountApplicationEnum.Auto.id,
|
82
|
+
active: true
|
83
|
+
}, {
|
84
|
+
limit: 1000,
|
85
|
+
projection: zeroed_relations
|
86
|
+
}
|
87
|
+
).toArray();
|
88
|
+
|
89
|
+
// now test locally
|
90
|
+
const eligible_discounts = discounts.filter(
|
91
|
+
d => test_product_filters_against_product(
|
92
|
+
d.info.filters, data
|
93
|
+
)
|
94
|
+
);
|
95
|
+
|
96
|
+
// console.log('eligible_discounts', eligible_discounts)
|
97
|
+
|
98
|
+
// now replace discounts relation
|
99
|
+
replacement._relations = replacement._relations ?? {};
|
100
|
+
replacement._relations.discounts = {
|
101
|
+
ids: eligible_discounts.map(d => d._id),
|
102
|
+
entries: Object.fromEntries(
|
103
|
+
eligible_discounts.map(d => [d._id.toString(), d])
|
104
|
+
)
|
105
|
+
}
|
106
|
+
|
107
|
+
// SEARCH
|
108
|
+
add_search_terms_relation_on(
|
109
|
+
replacement, union(
|
110
|
+
[
|
111
|
+
search_terms,
|
112
|
+
eligible_discounts.map(d => `discount:${d.handle}`),
|
113
|
+
eligible_discounts.map(d => `discount:${d.id}`),
|
114
|
+
]
|
115
|
+
)
|
116
|
+
);
|
117
|
+
|
118
|
+
delete_keys(
|
119
|
+
'collections', 'variants', 'discounts', 'related_products', 'search'
|
120
|
+
)(replacement);
|
121
|
+
|
122
|
+
// Now update other relations, that point to me
|
123
|
+
|
124
|
+
////
|
125
|
+
// Related Products -> PRODUCTS RELATION (explicit)
|
126
|
+
////
|
127
|
+
await update_entry_on_all_connection_of_relation(
|
128
|
+
driver, 'products', 'related_products', objid, replacement, session
|
129
|
+
);
|
130
|
+
|
131
|
+
////
|
132
|
+
// VARIANTS RELATION
|
133
|
+
////
|
134
|
+
|
135
|
+
if(`parent_handle` in data) { // is variant ?
|
136
|
+
// TODO: stronger validation of variant identification
|
137
|
+
|
138
|
+
const isValid = (
|
139
|
+
data.parent_handle && data.parent_id && data.variant_hint
|
140
|
+
);
|
141
|
+
|
142
|
+
if(isValid) {
|
143
|
+
// update parent product
|
144
|
+
await update_specific_connection_of_relation(
|
145
|
+
driver, 'products', 'variants', to_objid(data.parent_id),
|
146
|
+
objid, replacement, session
|
147
|
+
);
|
148
|
+
|
149
|
+
}
|
150
|
+
|
151
|
+
} else { // i am a parent
|
152
|
+
// let's fetch existing relations if any
|
153
|
+
const existing = await await col(driver).findOne(
|
154
|
+
{ _id: objid }
|
155
|
+
);
|
156
|
+
if(existing && existing?._relations?.variants) {
|
157
|
+
replacement._relations = replacement._relations ?? {};
|
158
|
+
replacement._relations.variants = existing._relations.variants;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
////
|
163
|
+
// STOREFRONTS -> PRODUCTS RELATION
|
164
|
+
////
|
165
|
+
await update_entry_on_all_connection_of_relation(
|
166
|
+
driver, 'storefronts', 'products', objid, replacement, session
|
167
|
+
);
|
168
|
+
|
169
|
+
////
|
170
|
+
// REPORT IMAGES USAGE
|
171
|
+
////
|
172
|
+
await report_document_media(driver)(replacement, session);
|
173
|
+
|
174
|
+
// SAVE ME
|
175
|
+
await save_me(driver, 'products', objid, replacement, session);
|
176
|
+
|
177
|
+
}
|
178
|
+
);
|
179
|
+
} catch (e) {
|
180
|
+
console.log(e);
|
181
|
+
|
182
|
+
return false;
|
183
|
+
} finally {
|
184
|
+
await session.endSession();
|
185
|
+
}
|
186
|
+
|
187
|
+
return true;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
|
192
|
+
/**
|
193
|
+
*
|
194
|
+
* @param {MongoDB} driver
|
195
|
+
*/
|
196
|
+
const get = (driver) => get_regular(driver, col(driver));
|
197
|
+
|
198
|
+
/**
|
199
|
+
* @param {MongoDB} driver
|
200
|
+
*
|
201
|
+
*
|
202
|
+
* @returns {db_col["remove"]}
|
203
|
+
*/
|
204
|
+
const remove = (driver) => {
|
205
|
+
return async (id) => {
|
206
|
+
// todo: transaction
|
207
|
+
|
208
|
+
const item = await col(driver).findOne(handle_or_id(id));
|
209
|
+
|
210
|
+
if(!item) return;
|
211
|
+
|
212
|
+
const objid = to_objid(item.id);
|
213
|
+
const session = driver.mongo_client.startSession();
|
214
|
+
|
215
|
+
try {
|
216
|
+
await session.withTransaction(
|
217
|
+
async () => {
|
218
|
+
|
219
|
+
////
|
220
|
+
// PRODUCTS -> VARIANTS RELATION
|
221
|
+
////
|
222
|
+
const is_variant = `parent_handle` in item;
|
223
|
+
|
224
|
+
if(is_variant) {
|
225
|
+
// TODO: stronger validation
|
226
|
+
const isValid = (
|
227
|
+
item.parent_handle && item.parent_id && item.variant_hint
|
228
|
+
);
|
229
|
+
|
230
|
+
if(isValid) {
|
231
|
+
// remove me from parent
|
232
|
+
await remove_specific_connection_of_relation(
|
233
|
+
driver, 'products', 'variants', to_objid(item.parent_id),
|
234
|
+
objid, session
|
235
|
+
);
|
236
|
+
|
237
|
+
}
|
238
|
+
|
239
|
+
} else {
|
240
|
+
// I am a parent, let's delete all of the children variants
|
241
|
+
const ids = item?._relations?.variants?.ids;
|
242
|
+
if(ids) {
|
243
|
+
await driver.resources.products._col.deleteMany(
|
244
|
+
{ _id: { $in: ids } },
|
245
|
+
{ session }
|
246
|
+
);
|
247
|
+
}
|
248
|
+
}
|
249
|
+
|
250
|
+
////
|
251
|
+
// STOREFRONTS --> PRODUCTS RELATION
|
252
|
+
////
|
253
|
+
await remove_entry_from_all_connection_of_relation(
|
254
|
+
driver, 'storefronts', 'products', objid, session
|
255
|
+
);
|
256
|
+
|
257
|
+
////
|
258
|
+
// PRODUCTS --> RELATED PRODUCTS RELATION
|
259
|
+
////
|
260
|
+
await remove_entry_from_all_connection_of_relation(
|
261
|
+
driver, 'products', 'related_products', objid, session
|
262
|
+
);
|
263
|
+
|
264
|
+
// DELETE ME
|
265
|
+
await delete_me(
|
266
|
+
driver, 'products', objid, session
|
267
|
+
);
|
268
|
+
|
269
|
+
}
|
270
|
+
);
|
271
|
+
} catch (e) {
|
272
|
+
console.log(e);
|
273
|
+
return false;
|
274
|
+
} finally {
|
275
|
+
await session.endSession();
|
276
|
+
}
|
277
|
+
|
278
|
+
return true;
|
279
|
+
}
|
280
|
+
|
281
|
+
}
|
282
|
+
|
283
|
+
/**
|
284
|
+
* @param {MongoDB} driver
|
285
|
+
*/
|
286
|
+
const list = (driver) => list_regular(driver, col(driver));
|
287
|
+
|
288
|
+
/**
|
289
|
+
* @param {MongoDB} driver
|
290
|
+
*/
|
291
|
+
const count = (driver) => count_regular(driver, col(driver));
|
292
|
+
|
293
|
+
|
294
|
+
/**
|
295
|
+
* For now and because each product is related to very few
|
296
|
+
* collections, I will not expose the query api, and use aggregate
|
297
|
+
* instead.
|
298
|
+
*
|
299
|
+
*
|
300
|
+
* @param {MongoDB} driver
|
301
|
+
*
|
302
|
+
*
|
303
|
+
* @returns {db_col["list_product_collections"]}
|
304
|
+
*/
|
305
|
+
const list_product_collections = (driver) => {
|
306
|
+
return async (product) => {
|
307
|
+
/** @type {import('@storecraft/core/v-database').RegularGetOptions} */
|
308
|
+
const options = {
|
309
|
+
expand: ['collections']
|
310
|
+
};
|
311
|
+
|
312
|
+
// We have collections embedded in products, so let's use it
|
313
|
+
const item = await get_regular(driver, col(driver))(product, options);
|
314
|
+
|
315
|
+
return sanitize_array(item?.collections ?? []);
|
316
|
+
}
|
317
|
+
}
|
318
|
+
|
319
|
+
/**
|
320
|
+
* For now and because each product is related to very few
|
321
|
+
* collections, I will not expose the query api, and use aggregate
|
322
|
+
* instead.
|
323
|
+
*
|
324
|
+
*
|
325
|
+
* @param {MongoDB} driver
|
326
|
+
*
|
327
|
+
*
|
328
|
+
* @returns {db_col["list_product_variants"]}
|
329
|
+
*/
|
330
|
+
const list_product_variants = (driver) => {
|
331
|
+
return async (product) => {
|
332
|
+
/** @type {import('@storecraft/core/v-database').RegularGetOptions} */
|
333
|
+
const options = {
|
334
|
+
expand: ['variants']
|
335
|
+
};
|
336
|
+
|
337
|
+
// We have collections embedded in products, so let's use it
|
338
|
+
const item = await get_regular(driver, col(driver))(product, options);
|
339
|
+
|
340
|
+
if(item && ('variants' in item)) {
|
341
|
+
|
342
|
+
return sanitize_array(item?.variants ?? []);
|
343
|
+
}
|
344
|
+
|
345
|
+
return [];
|
346
|
+
}
|
347
|
+
}
|
348
|
+
|
349
|
+
/**
|
350
|
+
* For now and because each product is related to very few
|
351
|
+
* collections, I will not expose the query api, and use aggregate
|
352
|
+
* instead.
|
353
|
+
*
|
354
|
+
*
|
355
|
+
* @param {MongoDB} driver
|
356
|
+
*
|
357
|
+
*
|
358
|
+
* @returns {db_col["list_related_products"]}
|
359
|
+
*/
|
360
|
+
const list_related_products = (driver) => {
|
361
|
+
return async (product) => {
|
362
|
+
/** @type {import('@storecraft/core/v-database').RegularGetOptions} */
|
363
|
+
const options = {
|
364
|
+
expand: ['related_products']
|
365
|
+
};
|
366
|
+
|
367
|
+
// We have collections embedded in products, so let's use it
|
368
|
+
const item = await get_regular(driver, col(driver))(product, options);
|
369
|
+
|
370
|
+
return sanitize_array(item?.related_products ?? []);
|
371
|
+
}
|
372
|
+
}
|
373
|
+
|
374
|
+
|
375
|
+
/**
|
376
|
+
* @param {MongoDB} driver
|
377
|
+
*
|
378
|
+
*
|
379
|
+
* @returns {db_col["list_product_discounts"]}
|
380
|
+
*/
|
381
|
+
const list_product_discounts = (driver) => {
|
382
|
+
return async (product) => {
|
383
|
+
/** @type {import('@storecraft/core/v-database').RegularGetOptions} */
|
384
|
+
const options = {
|
385
|
+
expand: ['discounts']
|
386
|
+
};
|
387
|
+
|
388
|
+
// We have collections embedded in products, so let's use it
|
389
|
+
const item = await get_regular(driver, col(driver))(product, options);
|
390
|
+
|
391
|
+
return sanitize_array(item?.discounts ?? []);
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* @param {MongoDB} driver
|
397
|
+
*
|
398
|
+
*
|
399
|
+
* @returns {db_col["add_product_to_collection"]}
|
400
|
+
*/
|
401
|
+
const add_product_to_collection = (driver) => {
|
402
|
+
return async (product_id_or_handle, collection_handle_or_id) => {
|
403
|
+
|
404
|
+
const coll = await driver.resources.collections._col.findOne(
|
405
|
+
handle_or_id(collection_handle_or_id)
|
406
|
+
);
|
407
|
+
|
408
|
+
if(!coll)
|
409
|
+
return;
|
410
|
+
|
411
|
+
const objid = to_objid(coll.id);
|
412
|
+
|
413
|
+
await update_specific_connection_of_relation_with_filter(
|
414
|
+
driver, 'products', 'collections',
|
415
|
+
handle_or_id(product_id_or_handle),
|
416
|
+
objid, coll, undefined,
|
417
|
+
[
|
418
|
+
`col:${coll.handle}`, `col:${coll.id}`
|
419
|
+
]
|
420
|
+
);
|
421
|
+
|
422
|
+
}
|
423
|
+
}
|
424
|
+
|
425
|
+
|
426
|
+
/**
|
427
|
+
* @param {MongoDB} driver
|
428
|
+
*
|
429
|
+
*
|
430
|
+
* @returns {db_col["remove_product_from_collection"]}
|
431
|
+
*/
|
432
|
+
const remove_product_from_collection = (driver) => {
|
433
|
+
return async (product_id_or_handle, collection_handle_or_id) => {
|
434
|
+
|
435
|
+
const coll = await driver.resources.collections._col.findOne(
|
436
|
+
handle_or_id(collection_handle_or_id)
|
437
|
+
);
|
438
|
+
|
439
|
+
if(!coll)
|
440
|
+
return;
|
441
|
+
|
442
|
+
const objid = to_objid(coll.id);
|
443
|
+
|
444
|
+
await remove_specific_connection_of_relation_with_filter(
|
445
|
+
driver, 'products', 'collections',
|
446
|
+
handle_or_id(product_id_or_handle),
|
447
|
+
objid, undefined,
|
448
|
+
[
|
449
|
+
`col:${coll.handle}`, `col:${coll.id}`
|
450
|
+
]
|
451
|
+
);
|
452
|
+
}
|
453
|
+
}
|
454
|
+
|
455
|
+
/**
|
456
|
+
* @param {MongoDB} driver
|
457
|
+
*
|
458
|
+
*
|
459
|
+
* @returns {db_col["changeStockOfBy"]}
|
460
|
+
*/
|
461
|
+
const changeStockOfBy = (driver) => {
|
462
|
+
return async (product_ids_or_handles, deltas) => {
|
463
|
+
|
464
|
+
/**
|
465
|
+
* @type {import('mongodb').AnyBulkWriteOperation<
|
466
|
+
* import('./utils.relations.js').WithRelations<
|
467
|
+
* import('@storecraft/core/v-api').ProductType |
|
468
|
+
* import('@storecraft/core/v-api').VariantType
|
469
|
+
* >
|
470
|
+
* >[]}
|
471
|
+
*/
|
472
|
+
let ops = []
|
473
|
+
|
474
|
+
product_ids_or_handles.forEach(
|
475
|
+
(id, ix) => {
|
476
|
+
ops.push(
|
477
|
+
{
|
478
|
+
updateOne: {
|
479
|
+
filter: handle_or_id(id),
|
480
|
+
update: {
|
481
|
+
$inc: { qty: Math.round(deltas[ix]) }
|
482
|
+
}
|
483
|
+
}
|
484
|
+
}
|
485
|
+
);
|
486
|
+
|
487
|
+
ops.push(
|
488
|
+
{
|
489
|
+
updateOne: {
|
490
|
+
filter: handle_or_id(id),
|
491
|
+
update: {
|
492
|
+
$max: { qty: 0 }
|
493
|
+
}
|
494
|
+
}
|
495
|
+
}
|
496
|
+
);
|
497
|
+
|
498
|
+
}
|
499
|
+
);
|
500
|
+
|
501
|
+
if(!ops.length)
|
502
|
+
return;
|
503
|
+
|
504
|
+
await driver.resources.products._col.bulkWrite(
|
505
|
+
ops
|
506
|
+
);
|
507
|
+
|
508
|
+
}
|
509
|
+
}
|
510
|
+
|
511
|
+
|
512
|
+
/**
|
513
|
+
* @param {MongoDB} driver
|
514
|
+
*
|
515
|
+
*
|
516
|
+
* @return {db_col & { _col: ReturnType<col> }}
|
517
|
+
*/
|
518
|
+
export const impl = (driver) => {
|
519
|
+
|
520
|
+
return {
|
521
|
+
_col: col(driver),
|
522
|
+
changeStockOfBy: changeStockOfBy(driver),
|
523
|
+
get: get(driver),
|
524
|
+
getBulk: get_bulk(driver, col(driver)),
|
525
|
+
upsert: upsert(driver),
|
526
|
+
remove: remove(driver),
|
527
|
+
list: list(driver),
|
528
|
+
add_product_to_collection: add_product_to_collection(driver),
|
529
|
+
remove_product_from_collection: remove_product_from_collection(driver),
|
530
|
+
list_product_collections: list_product_collections(driver),
|
531
|
+
list_product_variants: list_product_variants(driver),
|
532
|
+
list_related_products: list_related_products(driver),
|
533
|
+
list_product_discounts: list_product_discounts(driver),
|
534
|
+
count: count(driver)
|
535
|
+
}
|
536
|
+
}
|
537
|
+
|
@@ -0,0 +1,162 @@
|
|
1
|
+
import { MongoDB } from '../index.js'
|
2
|
+
import { query_to_mongo } from './utils.query.js';
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @typedef {import('@storecraft/core/v-database').search} db_col
|
6
|
+
*/
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
/**
|
11
|
+
* @type {(keyof import('@storecraft/core/v-database').db_driver["resources"])[]}
|
12
|
+
*/
|
13
|
+
const tables = [
|
14
|
+
'tags',
|
15
|
+
'collections',
|
16
|
+
'customers',
|
17
|
+
'products',
|
18
|
+
'storefronts',
|
19
|
+
'images',
|
20
|
+
'posts',
|
21
|
+
'shipping_methods',
|
22
|
+
'notifications',
|
23
|
+
'discounts',
|
24
|
+
'orders',
|
25
|
+
'templates'
|
26
|
+
]
|
27
|
+
|
28
|
+
/**
|
29
|
+
* @type {Record<string, keyof import('@storecraft/core/v-database').db_driver["resources"]>}
|
30
|
+
*/
|
31
|
+
const prefix_to_resource = {
|
32
|
+
'au': 'auth_users',
|
33
|
+
'col': 'collections',
|
34
|
+
'cus': 'customers',
|
35
|
+
'dis': 'discounts',
|
36
|
+
'img': 'images',
|
37
|
+
'not': 'notifications',
|
38
|
+
'order': 'orders',
|
39
|
+
'pr': 'products',
|
40
|
+
'ship': 'shipping_methods',
|
41
|
+
'sf': 'storefronts',
|
42
|
+
'tag': 'tags',
|
43
|
+
'template': 'templates',
|
44
|
+
'post': 'posts',
|
45
|
+
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
*
|
50
|
+
* @param {string} id
|
51
|
+
*
|
52
|
+
* @returns {keyof import('@storecraft/core/v-database').db_driver["resources"]}
|
53
|
+
*/
|
54
|
+
export const id_to_resource = id => {
|
55
|
+
let result = undefined;
|
56
|
+
try {
|
57
|
+
const prefix = id.split('_').at(0);
|
58
|
+
result = prefix_to_resource[prefix];
|
59
|
+
} catch(e) {
|
60
|
+
|
61
|
+
} finally {
|
62
|
+
return result;
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* @param {MongoDB} driver
|
68
|
+
*
|
69
|
+
*
|
70
|
+
* @returns {db_col["quicksearch"]}
|
71
|
+
*/
|
72
|
+
export const quicksearch = (driver) => {
|
73
|
+
return async (query) => {
|
74
|
+
|
75
|
+
const { filter, sort, reverse_sign } = query_to_mongo(query);
|
76
|
+
const expand = query.expand ?? ['*'];
|
77
|
+
const tables_filtered = tables.filter(t => expand.includes('*') || expand.includes(t));
|
78
|
+
|
79
|
+
if(tables_filtered.length==0)
|
80
|
+
return {};
|
81
|
+
|
82
|
+
const pipeline = [
|
83
|
+
{
|
84
|
+
"$match": filter
|
85
|
+
},
|
86
|
+
{
|
87
|
+
$sort: sort
|
88
|
+
},
|
89
|
+
{
|
90
|
+
$limit: query.limit ?? 5
|
91
|
+
},
|
92
|
+
{
|
93
|
+
$project: {
|
94
|
+
title: 1,
|
95
|
+
handle: 1,
|
96
|
+
// '_relations.search': 1,
|
97
|
+
id: 1,
|
98
|
+
_id: 0
|
99
|
+
}
|
100
|
+
}
|
101
|
+
];
|
102
|
+
|
103
|
+
const db = driver.mongo_client.db();
|
104
|
+
|
105
|
+
/** @type {import('@storecraft/core/v-api').QuickSearchResource[]} */
|
106
|
+
const items = await db.collection(tables_filtered[0]).aggregate(
|
107
|
+
[
|
108
|
+
...pipeline,
|
109
|
+
...tables_filtered.slice(1).map(
|
110
|
+
t => (
|
111
|
+
{
|
112
|
+
$unionWith: {
|
113
|
+
coll: t,
|
114
|
+
pipeline: pipeline
|
115
|
+
}
|
116
|
+
}
|
117
|
+
)
|
118
|
+
)
|
119
|
+
],
|
120
|
+
{
|
121
|
+
|
122
|
+
}
|
123
|
+
).toArray();
|
124
|
+
|
125
|
+
|
126
|
+
/** @type {import('@storecraft/core/v-api').QuickSearchResult} */
|
127
|
+
const result = {};
|
128
|
+
|
129
|
+
items.reduce(
|
130
|
+
(p, c) => {
|
131
|
+
const resource = id_to_resource(c.id);
|
132
|
+
const resource_meta = p[resource];
|
133
|
+
|
134
|
+
if(resource_meta) {
|
135
|
+
resource_meta.push(c);
|
136
|
+
} else {
|
137
|
+
p[resource] = [
|
138
|
+
c
|
139
|
+
]
|
140
|
+
}
|
141
|
+
|
142
|
+
return p;
|
143
|
+
},
|
144
|
+
result
|
145
|
+
);
|
146
|
+
|
147
|
+
return result;
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
151
|
+
/**
|
152
|
+
* @param {MongoDB} driver
|
153
|
+
*
|
154
|
+
*
|
155
|
+
* @return {db_col}
|
156
|
+
* */
|
157
|
+
export const impl = (driver) => {
|
158
|
+
|
159
|
+
return {
|
160
|
+
quicksearch: quicksearch(driver)
|
161
|
+
}
|
162
|
+
}
|