@storecraft/database-sqlite 1.0.1 → 1.0.3

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,497 +0,0 @@
1
- import { enums } from '@storecraft/core/v-api'
2
- import { SQL } from '../index.js'
3
- import { delete_entity_values_of_by_entity_id_or_handle, delete_me, delete_media_of,
4
- delete_search_of, delete_tags_of,
5
- insert_entity_values_of, insert_media_of, insert_search_of,
6
- insert_tags_of, regular_upsert_me,
7
- where_id_or_handle_table, products_with_collections,
8
- with_tags, with_media,
9
- delete_entity_values_by_value_or_reporter,
10
- products_with_discounts,
11
- products_with_variants,
12
- count_regular,
13
- products_with_related_products} from './con.shared.js'
14
- import { sanitize_array } from './utils.funcs.js'
15
- import { query_to_eb, query_to_sort } from './utils.query.js'
16
- import { Transaction } from 'kysely'
17
- import { report_document_media } from './con.images.js'
18
-
19
-
20
- /**
21
- * @typedef {import('@storecraft/core/v-database').db_products} db_col
22
- */
23
- export const table_name = 'products'
24
-
25
- /**
26
- *
27
- * @param {db_col["$type_upsert"]} item
28
- */
29
- const is_variant = item => {
30
- if(item && ('variant_hint' in item)) {
31
- return item.parent_handle && item.parent_id && item.variant_hint;
32
- }
33
-
34
- return false;
35
- }
36
-
37
- /**
38
- * @param {SQL} driver
39
- *
40
- *
41
- * @returns {db_col["upsert"]}
42
- */
43
- const upsert = (driver) => {
44
- return async (item, search_terms) => {
45
- const c = driver.client;
46
-
47
- try {
48
- // The product has changed, it's discounts eligibility may have changed.
49
- // get all automatic + active discounts
50
- const discounts = await driver.client
51
- .selectFrom('discounts')
52
- .selectAll()
53
- .where(
54
- eb => eb.and(
55
- [
56
- eb('active', '=', 1),
57
- eb('_application_id', '=', enums.DiscountApplicationEnum.Auto.id),
58
- ]
59
- )
60
- ).execute();
61
-
62
- const eligible_discounts = discounts.filter(
63
- d => driver.app.api.pricing.test_product_filters_against_product(d.info.filters, item)
64
- );
65
-
66
- const t = await driver.client.transaction().execute(
67
- async (trx) => {
68
-
69
- // entities
70
- await insert_tags_of(trx, item.tags, item.id, item.handle, table_name);
71
- await insert_search_of(trx, search_terms, item.id, item.handle, table_name);
72
- await insert_media_of(trx, item.media, item.id, item.handle, table_name);
73
- await report_document_media(driver)(item, trx);
74
- // main
75
- await regular_upsert_me(trx, table_name, {
76
- created_at: item.created_at,
77
- updated_at: item.updated_at,
78
- id: item.id,
79
- handle: item.handle,
80
- isbn: item.isbn,
81
- active: item.active ? 1 : 0,
82
- attributes: JSON.stringify(item.attributes),
83
- description: item.description,
84
- title: item.title,
85
- compare_at_price: item.compare_at_price,
86
- price: item.price,
87
- video: item.video,
88
- qty: item.qty,
89
- variants_options: 'variants_options' in item ? JSON.stringify(item.variants_options) : undefined,
90
- // no variants yet
91
- parent_handle: 'parent_handle' in item ? item.parent_handle : undefined,
92
- parent_id: 'parent_id' in item ? item.parent_id : undefined,
93
- variant_hint: 'variant_hint' in item ? JSON.stringify(item.variant_hint) : undefined,
94
- });
95
-
96
- // PRODUCTS => VARIANTS
97
- if(item && 'variant_hint' in item && is_variant(item)) {
98
- // remove previous
99
- await delete_entity_values_by_value_or_reporter('products_to_variants')(
100
- trx, item.id, item.handle
101
- );
102
- // add
103
- await insert_entity_values_of('products_to_variants')(
104
- trx, [{ value: item.id, reporter: item.handle }],
105
- item.parent_id, item.parent_handle,
106
- );
107
- }
108
-
109
- //
110
- // Explicit PRODUCTS => COLLECTIONS
111
- //
112
- // remove this product's old collections connections
113
- await delete_entity_values_of_by_entity_id_or_handle('products_to_collections')(
114
- trx, item.id, item.handle
115
- );
116
- if(item.collections) {
117
- // add this product's new collections connections
118
- await insert_entity_values_of('products_to_collections')(
119
- trx, item.collections.map(c => ({ value: c.id, reporter: c.handle })),
120
- item.id, item.handle,
121
- );
122
- }
123
-
124
- //
125
- // Explicit PRODUCTS => Related Products
126
- //
127
- // remove this product's old collections connections
128
- await delete_entity_values_of_by_entity_id_or_handle('products_to_related_products')(
129
- trx, item.id, item.handle
130
- );
131
- if(item.related_products) {
132
- // add this product's new `related_products` connections
133
- await insert_entity_values_of('products_to_related_products')(
134
- trx,
135
- item.related_products.map(c => ({ value: c.id, reporter: c.handle })),
136
- item.id, item.handle,
137
- );
138
- }
139
-
140
- // PRODUCTS => DISCOUNTS
141
- // remove this product's older connections to discounts
142
- await delete_entity_values_of_by_entity_id_or_handle('products_to_discounts')(
143
- trx, item.id, item.handle,
144
- );
145
- if(eligible_discounts) {
146
- // insert new connections to discounts
147
- await insert_entity_values_of('products_to_discounts')(
148
- trx, eligible_discounts.map(c => ({ value: c.id, reporter: c.handle})),
149
- item.id, item.handle,
150
- );
151
- }
152
- }
153
- );
154
- } catch(e) {
155
- console.log(e);
156
- return false;
157
- }
158
- return true;
159
- }
160
- }
161
-
162
-
163
- /**
164
- * @param {SQL} driver
165
- *
166
- *
167
- * @returns {db_col["get"]}
168
- */
169
- const get = (driver) => {
170
- return (id_or_handle, options) => {
171
-
172
- const expand = options?.expand ?? ['*'];
173
- const expand_collections = expand.includes('*') || expand.includes('collections');
174
- const expand_discounts = expand.includes('*') || expand.includes('discounts');
175
- const expand_variants = expand.includes('*') || expand.includes('variants');
176
- const expand_related_products = expand.includes('*') || expand.includes('related_products');
177
- const dtype = driver.config.dialect_type;
178
-
179
- return driver.client
180
- .selectFrom(table_name)
181
- .selectAll('products')
182
- .select(
183
- eb => [
184
- with_tags(eb, id_or_handle, dtype),
185
- with_media(eb, id_or_handle, dtype),
186
- expand_collections && products_with_collections(eb, id_or_handle, dtype),
187
- expand_discounts && products_with_discounts(eb, id_or_handle, dtype),
188
- expand_variants && products_with_variants(eb, id_or_handle, dtype),
189
- expand_related_products && products_with_related_products(eb, id_or_handle, dtype)
190
- ].filter(Boolean)
191
- )
192
- .where(where_id_or_handle_table(id_or_handle))
193
- // .compile()
194
- .executeTakeFirst();
195
- }
196
- }
197
-
198
- /**
199
- * @param {SQL} driver
200
- *
201
- *
202
- * @returns {db_col["getBulk"]}
203
- */
204
- const getBulk = (driver) => {
205
- return async (ids, options) => {
206
-
207
- const expand = options?.expand ?? ['*'];
208
- const expand_collections = expand.includes('*') || expand.includes('collections');
209
- const expand_discounts = expand.includes('*') || expand.includes('discounts');
210
- const expand_variants = expand.includes('*') || expand.includes('variants');
211
- const expand_related_products = expand.includes('*') || expand.includes('related_products');
212
- const dtype = driver.config.dialect_type;
213
-
214
- const r = await driver.client
215
- .selectFrom(table_name)
216
- .selectAll('products')
217
- .select(
218
- eb => [
219
- with_tags(eb, eb.ref('products.id'), dtype),
220
- with_media(eb, eb.ref('products.id'), dtype),
221
- expand_collections && products_with_collections(eb, eb.ref('products.id'), dtype),
222
- expand_discounts && products_with_discounts(eb, eb.ref('products.id'), dtype),
223
- expand_variants && products_with_variants(eb, eb.ref('products.id'), dtype),
224
- expand_related_products && products_with_related_products(eb, eb.ref('products.id'), dtype)
225
- ].filter(Boolean)
226
- )
227
- .where(
228
- (eb) => eb.or(
229
- [
230
- eb('id', 'in', ids),
231
- eb('handle', 'in', ids),
232
- ]
233
- )
234
- )
235
- // .compile()
236
- .execute();
237
-
238
- return ids.map(
239
- id => r.find(s => s.id===id || s?.handle===id)
240
- );
241
- }
242
- }
243
-
244
-
245
- /**
246
- * @param {SQL} driver
247
- */
248
- const remove_internal = (driver) => {
249
- /**
250
- * @param {import('@storecraft/core/v-api').ProductType |
251
- * import('@storecraft/core/v-api').VariantType
252
- * } product
253
- * @param {Transaction<import('../index.js').Database>} trx
254
- */
255
- return async (product, trx) => {
256
- // entities
257
- await delete_tags_of(trx, product.id);
258
- await delete_search_of(trx, product.id);
259
- await delete_media_of(trx, product.id);
260
- // PRODUCTS => COLLECTIONS
261
- await delete_entity_values_of_by_entity_id_or_handle('products_to_collections')(
262
- trx, product.id, product.handle
263
- );
264
- // PRODUCTS => DISCOUNTS
265
- await delete_entity_values_of_by_entity_id_or_handle('products_to_discounts')(
266
- trx, product.id, product.handle
267
- );
268
- // STOREFRONT => PRODUCT
269
- await delete_entity_values_by_value_or_reporter('storefronts_to_other')(
270
- trx, product.id, product.handle
271
- );
272
- // PRODUCTS => RELATED PRODUCT
273
- await delete_entity_values_by_value_or_reporter('products_to_related_products')(
274
- trx, product.id, product.handle
275
- );
276
- // PRODUCT => VARIANTS
277
- // delete all of it's variants
278
- {
279
- if(is_variant(product)) {
280
- // delete my reported connections
281
- await delete_entity_values_by_value_or_reporter('products_to_variants')(
282
- trx, product.id, product.handle
283
- );
284
- } else if(product && 'variants' in product) { // parent
285
- await Promise.all(
286
- (product.variants ?? []).map(v => remove_internal(driver)(v, trx))
287
- );
288
- // if I am a parent product, delete my relations to all previous variants
289
- await delete_entity_values_of_by_entity_id_or_handle('products_to_variants')(
290
- trx, product.id, product.handle
291
- );
292
- }
293
- }
294
-
295
- // delete me
296
- await delete_me(trx, table_name, product.id);
297
- }
298
- }
299
-
300
-
301
- /**
302
- * @param {SQL} driver
303
- * @returns {db_col["remove"]}
304
- */
305
- const remove = (driver) => {
306
- return async (id_or_handle) => {
307
- try {
308
- const product = await get(driver)(id_or_handle, { expand: ['variants'] });
309
-
310
- if(!product)
311
- return true;
312
-
313
- await driver.client.transaction().execute(
314
- async (trx) => {
315
- await remove_internal(driver)(product, trx);
316
- }
317
- );
318
-
319
- } catch(e) {
320
- console.log(e);
321
- return false;
322
- }
323
- return true;
324
- }
325
- }
326
-
327
- /**
328
- * @param {SQL} driver
329
- *
330
- *
331
- * @returns {db_col["list"]}
332
- */
333
- const list = (driver) => {
334
- return async (query) => {
335
-
336
- const expand = query.expand ?? ['*'];
337
- const expand_collections = expand.includes('*') || expand.includes('collections');
338
- const expand_discounts = expand.includes('*') || expand.includes('discounts');
339
- const expand_variants = expand.includes('*') || expand.includes('variants');
340
- const expand_related_products = expand.includes('*') || expand.includes('related_products');
341
-
342
- const items = await driver.client
343
- .selectFrom(table_name)
344
- .selectAll()
345
- .select(
346
- eb => [
347
- with_tags(eb, eb.ref('products.id'), driver.dialectType),
348
- with_media(eb, eb.ref('products.id'), driver.dialectType),
349
- expand_collections && products_with_collections(eb, eb.ref('products.id'), driver.dialectType),
350
- expand_discounts && products_with_discounts(eb, eb.ref('products.id'), driver.dialectType),
351
- expand_variants && products_with_variants(eb, eb.ref('products.id'), driver.dialectType),
352
- expand_related_products && products_with_related_products(eb, eb.ref('products.id'), driver.dialectType)
353
- ].filter(Boolean)
354
- )
355
- .where(
356
- (eb) => {
357
- return query_to_eb(eb, query, table_name);
358
- }
359
- )
360
- .orderBy(query_to_sort(query))
361
- .limit(query.limitToLast ?? query.limit ?? 10)
362
- .execute();
363
-
364
- if(query.limitToLast) items.reverse();
365
- // .compile();
366
- // console.log(items)
367
-
368
- return sanitize_array(items);
369
- }
370
- }
371
-
372
- /**
373
- * @param {SQL} driver
374
- * @returns {db_col["list_product_collections"]}
375
- */
376
- const list_product_collections = (driver) => {
377
- return async (product_id_or_handle) => {
378
- // we don't expect many collections per products,
379
- // therefore we use the simple `get` method instead of a query
380
- const item = await get(driver)(
381
- product_id_or_handle, { expand: ['collections'] }
382
- );
383
- return item?.collections ?? []
384
- }
385
- }
386
-
387
- /**
388
- * @param {SQL} driver
389
- * @returns {db_col["list_product_discounts"]}
390
- */
391
- const list_product_discounts = (driver) => {
392
- return async (product_id_or_handle) => {
393
- // we don't expect many discounts per products,
394
- // therefore we use the simple `get` method instead of a query
395
- const item = await get(driver)(
396
- product_id_or_handle, { expand: ['discounts'] }
397
- );
398
- return item?.discounts ?? []
399
- }
400
- }
401
-
402
- /**
403
- * @param {SQL} driver
404
- *
405
- * @returns {db_col["list_product_variants"]}
406
- */
407
- const list_product_variants = (driver) => {
408
- return async (product_id_or_handle) => {
409
- // we don't expect many discounts per products,
410
- // therefore we use the simple `get` method instead of a query
411
- const item = await get(driver)(
412
- product_id_or_handle, { expand: ['variants'] }
413
- );
414
-
415
- if(item && (`variants` in item))
416
- return item.variants ?? [];
417
-
418
- return [];
419
- }
420
- }
421
-
422
- /**
423
- * @param {SQL} driver
424
- *
425
- * @returns {db_col["list_related_products"]}
426
- */
427
- const list_related_products = (driver) => {
428
- return async (product_id_or_handle) => {
429
- // we don't expect many discounts per products,
430
- // therefore we use the simple `get` method instead of a query
431
- const item = await get(driver)(
432
- product_id_or_handle, { expand: ['related_products'] }
433
- );
434
-
435
- return item?.related_products ?? [];
436
- }
437
- }
438
-
439
-
440
- /**
441
- * @param {SQL} driver
442
- *
443
- * @returns {db_col["changeStockOfBy"]}
444
- */
445
- const changeStockOfBy = (driver) => {
446
- return async (product_ids_or_handles, deltas) => {
447
-
448
- await driver.client.transaction().execute(
449
- async (trx) => {
450
- for(let ix=0; ix < product_ids_or_handles.length; ix++ ) {
451
- const id = product_ids_or_handles[ix];
452
- const delta = deltas[ix];
453
-
454
- await trx
455
- .updateTable('products')
456
- .set(
457
- eb => (
458
- {
459
- qty: eb('qty', '+', delta)
460
- }
461
- )
462
- )
463
- .where(
464
- where_id_or_handle_table(id)
465
- )
466
- .execute()
467
-
468
- }
469
- }
470
- );
471
-
472
- }
473
-
474
- }
475
-
476
-
477
- /**
478
- * @param {SQL} driver
479
- *
480
- * @return {db_col}}
481
- * */
482
- export const impl = (driver) => {
483
-
484
- return {
485
- changeStockOfBy: changeStockOfBy(driver),
486
- get: get(driver),
487
- getBulk: getBulk(driver),
488
- upsert: upsert(driver),
489
- remove: remove(driver),
490
- list: list(driver),
491
- list_product_collections: list_product_collections(driver),
492
- list_product_discounts: list_product_discounts(driver),
493
- list_product_variants: list_product_variants(driver),
494
- list_related_products: list_related_products(driver),
495
- count: count_regular(driver, table_name),
496
- }
497
- }
package/src/con.search.js DELETED
@@ -1,148 +0,0 @@
1
- import { SQL } from '../index.js'
2
- import { jsonArrayFrom } from './con.helpers.json.js';
3
- import { query_to_eb, query_to_sort } from './utils.query.js';
4
-
5
- /**
6
- * @typedef {import('@storecraft/core/v-database').search} db_col
7
- */
8
-
9
-
10
-
11
- /**
12
- * @type {(keyof import('@storecraft/core/v-database').db_driver["resources"])[]}
13
- */
14
- const tables = [
15
- 'tags',
16
- 'collections',
17
- 'customers',
18
- 'products',
19
- 'storefronts',
20
- 'images',
21
- 'posts',
22
- 'shipping_methods',
23
- 'notifications',
24
- 'discounts',
25
- 'orders',
26
- 'templates'
27
- ]
28
-
29
- /**
30
- * @type {Record<string, keyof import('@storecraft/core/v-database').db_driver["resources"]>}
31
- */
32
- const prefix_to_resource = {
33
- 'au': 'auth_users',
34
- 'col': 'collections',
35
- 'cus': 'customers',
36
- 'dis': 'discounts',
37
- 'img': 'images',
38
- 'not': 'notifications',
39
- 'order': 'orders',
40
- 'pr': 'products',
41
- 'ship': 'shipping_methods',
42
- 'sf': 'storefronts',
43
- 'tag': 'tags',
44
- 'template': 'templates',
45
- 'post': 'posts',
46
-
47
- }
48
-
49
- /**
50
- * @type {Record<keyof import('@storecraft/core/v-database').db_driver["resources"], string[]>}
51
- */
52
- const resource_to_props = {
53
- 'auth_users': ['id', 'handle'],
54
- 'collections': ['id', 'handle'],
55
- 'customers': ['id', 'handle'],
56
- 'discounts': ['id', 'handle', 'title'],
57
- 'images': ['id', 'handle', 'name'],
58
- 'orders': ['id'],
59
- 'products': ['id', 'handle', 'title'],
60
- 'shipping_methods': ['id', 'handle', 'title'],
61
- 'storefronts': ['id', 'handle', 'title'],
62
- 'tags': ['id', 'handle'],
63
- 'templates': ['id', 'handle', 'title'],
64
- 'posts': ['id', 'handle', 'title'],
65
- }
66
-
67
- /**
68
- *
69
- * @param {string} id
70
- *
71
- * @returns {keyof import('@storecraft/core/v-database').db_driver["resources"]}
72
- */
73
- export const id_to_resource = id => {
74
- let result = undefined;
75
- try {
76
- const prefix = id.split('_').at(0);
77
- result = prefix_to_resource[prefix];
78
- } catch(e) {
79
-
80
- } finally {
81
- return result;
82
- }
83
- }
84
-
85
- /**
86
- * @param {SQL} driver
87
- *
88
- *
89
- * @returns {db_col["quicksearch"]}
90
- */
91
- export const quicksearch = (driver) => {
92
- return async (query) => {
93
- const db = driver.client;
94
- const expand = query.expand ?? ['*'];
95
- const all = expand.includes('*');
96
-
97
- const sts = db
98
- .selectNoFrom(
99
- eb => Object
100
- .entries(resource_to_props)
101
- .filter(t => all || expand.includes(t[0]))
102
- .map(
103
- ([table_name, props]) => {
104
- // console.log(table_name, props)
105
- return jsonArrayFrom(
106
- eb
107
- .selectFrom(table_name)
108
- .select(props)
109
- .where(
110
- (eb) => {
111
- return query_to_eb(eb, query, table_name);
112
- }
113
- )
114
- .orderBy(query_to_sort(query))
115
- .limit(query.limit ?? 5),
116
- driver.dialectType
117
- ).as(table_name)
118
- }
119
- )
120
- )
121
-
122
- /** @type {import('@storecraft/core/v-api').QuickSearchResult} */
123
- const items = await sts.executeTakeFirst();
124
-
125
- const sanitized = Object.fromEntries(
126
- Object.entries(items).filter(
127
- ([key, value]) => Boolean(value?.length)
128
- )
129
- );
130
-
131
- // console.log('sanitized', JSON.stringify(sanitized, null, 2))
132
-
133
- return sanitized;
134
- }
135
- }
136
-
137
- /**
138
- * @param {SQL} driver
139
- *
140
- *
141
- * @return {db_col}
142
- * */
143
- export const impl = (driver) => {
144
-
145
- return {
146
- quicksearch: quicksearch(driver)
147
- }
148
- }