@storecraft/database-sql-base
Version:
Official SQL Database driver for storecraft
446 lines (401 loc) • 14 kB
JavaScript
/**
* @import { db_discounts as db_col } from '@storecraft/core/database'
*/
import { enums } from '@storecraft/core/api'
import {
helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db,
helper_compute_product_extra_tags_because_of_discount_side_effect_for_db
} from '@storecraft/core/database'
import { SQL } from '../index.js'
import { discount_to_conjunctions, is_order_discount } from './con.discounts.utils.js'
import {
delete_entity_values_by_value_or_reporter_and_context,
delete_me, delete_media_of, delete_search_of,
delete_tags_of, insert_media_of, insert_search_of,
insert_tags_of, regular_upsert_me, where_id_or_handle_table,
with_media, with_tags, count_regular, with_search,
products_with_collections,
products_with_discounts,
products_with_variants,
products_with_related_products,
} from './con.shared.js'
import { sanitize, sanitize_array } from './utils.funcs.js'
import { query_to_eb, withQuery, withSort } from './utils.query.js'
import { report_document_media } from './con.images.js'
export const table_name = 'discounts'
/**
* @param {SQL} driver
* @returns {db_col["upsert"]}
*/
const upsert = (driver) => {
return async (item, search_terms) => {
const c = driver.client;
try {
const t = await c.transaction().execute(
async (trx) => {
/// ENTITIES
await insert_search_of(trx, search_terms, item.id, item.handle, table_name);
await insert_media_of(trx, item.media, item.id, item.handle, table_name);
await insert_tags_of(trx, item.tags, item.id, item.handle, table_name);
await report_document_media(driver)(item, trx);
//
// PRODUCTS => DISCOUNTS
//
// remove all products relation to this discount
await delete_entity_values_by_value_or_reporter_and_context('products_to_discounts')(
trx, item.id, item.handle);
const extra_search_for_products =
helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db(item);
const extra_tags_for_products =
helper_compute_product_extra_tags_because_of_discount_side_effect_for_db(item);
// maybe i should confine these to `context`=`products`
for(const extra_search of extra_search_for_products) {
await delete_entity_values_by_value_or_reporter_and_context('entity_to_search_terms')(
trx, extra_search);
}
for(const extra_tag of extra_tags_for_products) {
await delete_entity_values_by_value_or_reporter_and_context('entity_to_tags_projections')(
trx, extra_tag);
}
// make connections into `products_to_discounts`, only applicable for
// `product` discounts and automatic discounts
if(
item.active &&
item.application.id===enums.DiscountApplicationEnum.Auto.id &&
!is_order_discount(item)
) {
// make connections
await trx
.insertInto('products_to_discounts')
.columns(['entity_handle', 'entity_id', 'value', 'reporter'])
.expression(eb =>
eb.selectFrom('products')
.select(
eb => [
'handle as entity_handle',
'id as entity_id',
eb.val(item.id).as('value'),
eb.val(item.handle).as('reporter')
]
)
.where(
eb => eb.and(discount_to_conjunctions(eb, item))
)
).execute();
for(const extra_search of extra_search_for_products) {
await trx
.insertInto('entity_to_search_terms')
.columns(['entity_handle', 'entity_id', 'value', 'reporter', 'context'])
.expression(eb =>
eb.selectFrom('products')
.select(eb => [
'handle as entity_handle',
'id as entity_id',
eb.val(extra_search).as('value'),
eb.val(item.id).as('reporter'),
eb.val('products').as('context'),
]
)
.where(
eb => eb.and(discount_to_conjunctions(eb, item))
)
).execute();
}
for(const extra_tag of extra_tags_for_products) {
await trx
.insertInto('entity_to_tags_projections')
.columns(['entity_handle', 'entity_id', 'value', 'reporter', 'context'])
.expression(eb =>
eb.selectFrom('products')
.select(eb => [
'handle as entity_handle',
'id as entity_id',
eb.val(extra_tag).as('value'),
eb.val(item.id).as('reporter'),
eb.val('products').as('context'),
]
)
.where(
eb => eb.and(discount_to_conjunctions(eb, item))
)
).execute();
}
}
///
/// SAVE ME
///
await regular_upsert_me(trx, table_name, {
active: item.active ? 1 : 0,
attributes: JSON.stringify(item.attributes),
description: item.description,
created_at: item.created_at,
updated_at: item.updated_at,
id: item.id,
handle: item.handle,
title: item.title,
priority: item.priority ?? 0,
published: item.published,
application: JSON.stringify(item.application),
info: JSON.stringify(item.info),
_application_id: item.application.id,
_discount_type_id: item?.info?.details?.meta?.id ?? -1,
});
}
);
} catch(e) {
console.log(e);
return false;
}
return true;
}
}
/**
* @param {SQL} driver
* @returns {db_col["get"]}
*/
const get = (driver) => {
return (id_or_handle, options) => {
return driver.client
.selectFrom(table_name)
.selectAll()
.select(eb => [
with_media(eb, id_or_handle, driver.dialectType),
with_tags(eb, id_or_handle, driver.dialectType),
with_search(eb, id_or_handle, driver.dialectType),
].filter(Boolean))
.where(where_id_or_handle_table(id_or_handle))
.executeTakeFirst()
.then(sanitize);
}
}
/**
* @param {SQL} driver
* @returns {db_col["remove"]}
*/
const remove = (driver) => {
return async (id_or_handle) => {
try {
await driver.client.transaction().execute(
async (trx) => {
// entities
await delete_tags_of(trx, id_or_handle, id_or_handle, table_name);
await delete_search_of(trx, id_or_handle, id_or_handle, table_name);
await delete_media_of(trx, id_or_handle, id_or_handle, table_name);
// delete products -> discounts
// PRODUCTS => DISCOUNTS
await delete_entity_values_by_value_or_reporter_and_context('products_to_discounts')(
trx, id_or_handle, id_or_handle);
// STOREFRONT => DISCOUNTS
await delete_entity_values_by_value_or_reporter_and_context('storefronts_to_other')(
trx, id_or_handle, id_or_handle, table_name
);
// discount might have published search terms and tags for other products,
// so let's remove
const discount_handle_and_id = await trx
.selectFrom('discounts')
.select(['handle', 'id'])
.where(
(eb) => eb.or([
eb('discounts.handle', '=', id_or_handle),
eb('discounts.id', '=', id_or_handle)
])
)
.executeTakeFirst();
const extra_search_for_products =
helper_compute_product_extra_search_keywords_because_of_discount_side_effect_for_db(
discount_handle_and_id
);
const extra_tags_for_products =
helper_compute_product_extra_tags_because_of_discount_side_effect_for_db(
discount_handle_and_id
);
for(const extra_search of extra_search_for_products) {
await delete_entity_values_by_value_or_reporter_and_context('entity_to_search_terms')(
trx, extra_search);
}
for(const extra_tag of extra_tags_for_products) {
await delete_entity_values_by_value_or_reporter_and_context('entity_to_tags_projections')(
trx, extra_tag);
}
// delete me
await delete_me(trx, table_name, id_or_handle);
}
);
} catch(e) {
console.log(e);
return false;
}
return true;
}
}
/**
* @param {SQL} driver
* @returns {db_col["list"]}
*/
const list = (driver) => {
return async (query) => {
const items = await withQuery(
driver.client
.selectFrom(table_name)
.selectAll()
.select(eb => [
with_media(eb, eb.ref('discounts.id'), driver.dialectType),
with_tags(eb, eb.ref('discounts.id'), driver.dialectType),
with_search(eb, eb.ref('discounts.id'), driver.dialectType),
].filter(Boolean)),
query, table_name
).execute();
if(query.limitToLast)
items.reverse();
return sanitize_array(items);
}
}
/**
* @param {SQL} driver
* @returns {db_col["list_discount_products"]}
*/
const list_discount_products = (driver) => {
return async (handle_or_id, query={}) => {
const expand = query.expand ?? ['*'];
const expand_collections = expand.includes('*') ||
expand.includes('collections');
const expand_discounts = expand.includes('*') ||
expand.includes('discounts');
// @ts-ignore
const expand_variants = expand.includes('*') ||
expand.includes('variants');
const expand_related_products = expand.includes('*') ||
expand.includes('related_products');
const items = await withSort(
driver.client
.selectFrom('products')
.innerJoin(
'products_to_discounts',
'products_to_discounts.entity_id',
'products.id'
)
.selectAll('products')
.select(eb => [
with_media(eb, eb.ref('products.id'), driver.dialectType),
with_tags(eb, eb.ref('products.id'), driver.dialectType),
expand_collections &&
products_with_collections(
eb, eb.ref('products.id'), driver.dialectType
),
expand_discounts &&
products_with_discounts(
eb, eb.ref('products.id'), driver.dialectType
),
expand_variants &&
products_with_variants(
eb, eb.ref('products.id'), driver.dialectType
),
expand_related_products &&
products_with_related_products(
eb, eb.ref('products.id'), driver.dialectType
),
].filter(Boolean))
.where(
(eb) => eb.and(
[
query_to_eb(eb, query, 'products'),
eb.or(
[
eb('products_to_discounts.reporter', '=', handle_or_id),
eb('products_to_discounts.value', '=', handle_or_id)
]
)
].filter(Boolean)
)
)
.limit(query.limitToLast ?? query.limit ?? 10),
query, 'products'
).execute();
if(query.limitToLast)
items.reverse();
return sanitize_array(items);
}
}
/**
* @param {SQL} driver
* @returns {db_col["list_all_discount_products_tags"]}
*/
const list_all_discount_products_tags = (driver) => {
return async (handle_or_id) => {
const items = await driver.client
.selectFrom('products')
.innerJoin(
'products_to_discounts',
'products_to_discounts.entity_id',
'products.id'
)
.innerJoin(
'entity_to_tags_projections',
'entity_to_tags_projections.entity_id',
'products.id'
)
.select('entity_to_tags_projections.value as tag')
.where(
(eb) => eb.or(
[
eb('products_to_discounts.reporter', '=', handle_or_id),
eb('products_to_discounts.value', '=', handle_or_id)
]
)
)
.groupBy('tag')
.execute();
// .compile();
// console.log(items[0])
return items.map(e => e.tag);
}
}
/**
* @param {SQL} driver
* @returns {db_col["count_discount_products"]}
*/
const count_discount_products = (driver) => {
return async (handle_or_id, query={}) => {
const result = await driver.client
.selectFrom('products')
.innerJoin(
'products_to_discounts',
'products_to_discounts.entity_id',
'products.id'
)
.select(
(eb) => eb.fn.countAll().as('count')
)
.where(
(eb) => eb.and(
[
query_to_eb(eb, query, 'products'),
eb.or(
[
eb('products_to_discounts.reporter', '=', handle_or_id),
eb('products_to_discounts.value', '=', handle_or_id)
]
)
].filter(Boolean)
)
)
.executeTakeFirst();
return Number(result.count);
}
}
/**
* @param {SQL} driver
* @return {db_col}}
* */
export const impl = (driver) => {
return {
get: get(driver),
upsert: upsert(driver),
remove: remove(driver),
list: list(driver),
list_discount_products: list_discount_products(driver),
count_discount_products: count_discount_products(driver),
list_all_discount_products_tags: list_all_discount_products_tags(driver),
count: count_regular(driver, table_name),
}
}