UNPKG

@wener/miniquery

Version:

SQL Where like **safe** filter expression for ORM.

78 lines (75 loc) 2.04 kB
import { arrayOfMaybeArray, deepEqual, type MaybeArray } from '@wener/utils'; import { match } from 'ts-pattern'; import type { Expr, SearchExpr } from './types'; export function optimizeSearchExpr(expr: SearchExpr): SearchExpr { const NEG = { eq: 'ne', ne: 'eq', gt: 'lte', lt: 'gte', gte: 'lt', lte: 'gt' } as const; const _expr = (e: Expr): MaybeArray<Expr> => { // merge Exprs to AND ? return ( match(e) // (EXPR) -> EXPR // TODO (EXPR EXPR) -> EXPR AND EXPR .with({ type: 'parentheses' }, (expr) => { // unwrap if (expr.value.length < 2) { return expr.value[0]; } expr.value = expr.value.flatMap(_expr); return expr; }) .with({ type: 'comment' }, (expr) => { // remove empty comment if (!expr.value.length) { return []; } return expr; }) // NOT .with({ type: 'not' }, (expr) => { let out = arrayOfMaybeArray(_expr(expr.value)); if (!out.length) { return []; } else if (out.length === 1) { expr.value = out[0]; } else { throw new Error('NOT should have only one value'); } return ( match(expr.value) // NOT NOT EXPR -> EXPR .with({ type: 'not' }, (expr) => expr.value) // NOT EXPR -> -EXPR .with({ type: 'compare' }, (expr) => { return { ...expr, negative: !expr.negative }; }) .with({ type: 'keyword' }, (expr) => { return { ...expr, negative: !expr.negative }; }) .otherwise(() => expr) ); }) .with({ type: 'compare' }, (expr) => { // negative by swap operator if (expr.negative) { const ne = NEG[expr.operator as keyof typeof NEG]; if (ne) { expr.operator = ne; expr.negative = false; } } if (expr.operator === 'range') { } return expr; }) .otherwise((e) => e) ); }; let last = expr; while (true) { let next = structuredClone(last).flatMap(_expr); if (deepEqual(last, next)) { return last; } last = next; } }