UNPKG

stackpress

Version:

Incept is a content management framework.

152 lines (151 loc) 7.03 kB
import Nest from '@stackpress/lib/Nest'; import { toSqlString, toSqlFloat, toSqlInteger, toSqlBoolean, toSqlDate, toResponse, toErrorResponse, getColumns, getColumnInfo, stringable, floatable, dateable, boolable, intable } from '../helpers.js'; export default async function search(model, engine, query = {}, seed) { let { q, columns = ['*'], filter = {}, span = {}, sort = {}, skip = 0, take = 50, total: useTotal = true } = query; columns = columns.map(column => getColumns(column, model)).flat(); const info = columns .map(column => getColumnInfo(column, model)) .filter(column => column.path.length > 0); const selectors = info.map(selector => { const q = engine.dialect.q; const column = selector.table.length > 0 ? `${q}${selector.table}${q}.${q}${selector.column}${q}` : `${q}${model.snake}${q}.${q}${selector.column}${q}`; return `${column} AS ${q}${selector.alias}${q}`; }); const select = engine.select(selectors).from(model.snake); const count = engine .select('COUNT(*) as total') .from(model.snake); const joins = {}; info.forEach(selector => Object.assign(joins, selector.joins)); Object.values(joins).forEach(({ table, from, to, alias }) => { const q = engine.dialect.q; select.join('inner', table, from.replaceAll('.', `${q}.${q}`), to.replaceAll('.', `${q}.${q}`), alias); count.join('inner', table, from.replaceAll('.', `${q}.${q}`), to.replaceAll('.', `${q}.${q}`), alias); }); if (skip) { select.offset(skip); } if (take) { select.limit(take); } if (q && model.searchables.length > 0) { select.where(model.searchables.map(column => `${column.snake} ILIKE ?`).join(' OR '), model.searchables.map(_ => `%${q}%`)); count.where(model.searchables.map(column => `${column.snake} ILIKE ?`).join(' OR '), model.searchables.map(_ => `%${q}%`)); } if (model.active) { if (typeof filter[model.active.name] === 'undefined') { filter[model.active.name] = true; } else if (filter[model.active.name] == -1) { delete filter[model.active.name]; } } Object.entries(filter).forEach(([key, value]) => { const info = getColumnInfo(key, model).last; if (!info) return; const q = engine.dialect.q; const selector = `${q}${info.model.snake}${q}.${q}${info.column.snake}${q}`; value = info.column.serialize(value, undefined, seed); const serialized = stringable.includes(info.column.type) ? toSqlString(value) : floatable.includes(info.column.type) ? toSqlFloat(value) : intable.includes(info.column.type) ? toSqlInteger(value) : boolable.includes(info.column.type) ? toSqlBoolean(value) : dateable.includes(info.column.type) ? toSqlDate(value)?.toISOString() : String(value); if (typeof serialized !== 'undefined' && serialized !== '') { select.where(`${selector} = ?`, [serialized]); count.where(`${selector} = ?`, [serialized]); } }); Object.entries(span).forEach(([key, values]) => { const info = getColumnInfo(key, model).last; if (!info) return; const q = engine.dialect.q; const selector = `${q}${info.model.snake}${q}.${q}${info.column.snake}${q}`; if (typeof values[0] !== 'undefined' && values[0] !== null && values[0] !== '') { const value = info.column.unserialize(values[0], undefined, seed); const serialized = stringable.includes(info.column.type) ? toSqlString(value) : floatable.includes(info.column.type) ? toSqlFloat(value) : intable.includes(info.column.type) ? toSqlInteger(value) : boolable.includes(info.column.type) ? toSqlBoolean(value) : dateable.includes(info.column.type) ? toSqlDate(value)?.toISOString() : String(value); if (typeof serialized !== 'undefined' && serialized !== '') { select.where(`${selector} >= ?`, [serialized]); count.where(`${selector} >= ?`, [serialized]); } } if (typeof values[1] !== 'undefined' && values[1] !== null && values[1] !== '') { const value = info.column.unserialize(values[1], undefined, seed); const serialized = stringable.includes(info.column.type) ? toSqlString(value) : floatable.includes(info.column.type) ? toSqlFloat(value) : intable.includes(info.column.type) ? toSqlInteger(value) : boolable.includes(info.column.type) ? toSqlBoolean(value) : dateable.includes(info.column.type) ? toSqlDate(value)?.toISOString() : String(value); if (typeof serialized !== 'undefined') { select.where(`${selector} <= ?`, [serialized]); count.where(`${selector} <= ?`, [serialized]); } } }); Object.entries(sort).forEach(([key, value]) => { const direction = typeof value === 'string' ? value : ''; if (direction.toLowerCase() !== 'asc' && direction.toLowerCase() !== 'desc') { return; } const info = getColumnInfo(key, model).last; if (!info) return; const q = engine.dialect.q; const selector = `${info.model.snake}${q}.${q}${info.column.snake}`; select.order(selector, direction.toUpperCase()); }); try { const results = await select; const total = useTotal ? await count : [{ total: 0 }]; const rows = []; results.forEach(row => { const nest = new Nest(); Object.entries(row).forEach(([alias, value]) => { const selector = info.find(selector => selector.alias === alias); if (!selector) { return nest.withPath.set(alias.trim() .replaceAll('__', '.') .replace(/([a-z])_([a-z0-9])/ig, (_, a, b) => a + b.toUpperCase()), value); } nest.withPath.set(selector.name, selector.last.column.unserialize(value, undefined, seed)); }); rows.push(nest.get()); }); return toResponse(rows, Number(total[0].total) || undefined); } catch (e) { return toErrorResponse(e); } } ;