@starbemtech/star-db-query-builder
Version:
A query builder to be used with mysql or postgres
190 lines (167 loc) • 5.73 kB
text/typescript
import { Conditions, Condition, OrderBy, DBClients } from './types'
const arrayToStringWithQuotes = (
items: string[],
clientType: DBClients
): string => {
const itemsWithQuotes = items.map((item) =>
clientType === 'pg' ? `${item}` : `${item}`
)
return itemsWithQuotes.join(', ')
}
const pgPlaceholderGenerator = (index: number) => `$${index}`
const mysqlPlaceholderGenerator = () => `?`
export const createSelectFields = (
fields: string[] = [],
clientType: DBClients
): string => {
return fields && fields.length > 0
? arrayToStringWithQuotes(fields, clientType)
: '*'
}
export const generatePlaceholders = (
keys: any[],
clientType: DBClients
): string => {
return keys
.map((_, index) => (clientType === 'pg' ? `$${index + 1}` : '?'))
.join(', ')
}
export const generateSetClause = (
keys: any[],
clientType: DBClients
): string => {
return keys
.map((key, index) =>
clientType === 'pg' ? `${key} = $${index + 1}` : `${key} = ?`
)
.join(', ')
}
export const createWhereClause = <T>(
conditions: Conditions<T> = {},
startIndex = 1,
clientType: DBClients,
unaccent?: boolean
): [string, any[], number] => {
let index = startIndex
const whereParts: string[] = []
const values: any[] = []
const processCondition = (key: string, condition: Condition<T>) => {
if (typeof condition === 'object' && condition !== null) {
if ('operator' in condition && 'value' in condition) {
const { operator, value } = condition
if (operator === 'NOT EXISTS' && typeof value === 'string') {
whereParts.push(`NOT EXISTS (${value})`)
} else if (operator.includes('NULL')) {
whereParts.push(`${key} ${operator}`)
} else if (Array.isArray(value)) {
const placeholders = value
.map(() =>
clientType === 'pg'
? pgPlaceholderGenerator(index++)
: mysqlPlaceholderGenerator()
)
.join(', ')
if (operator === 'BETWEEN') {
whereParts.push(
`${key} ${operator} ${placeholders.replace(', ', ' AND ')}`
)
} else if (operator === 'IN') {
whereParts.push(`${key} ${operator} (${placeholders})`)
} else {
whereParts.push(`${key} ${operator} (${placeholders})`)
}
values.push(...value)
} else {
if (unaccent && clientType === 'pg') {
if (operator.toUpperCase() === 'ILIKE') {
whereParts.push(
`unaccent(${key}::text) ILIKE unaccent(${pgPlaceholderGenerator(index)})`
)
} else {
whereParts.push(
`unaccent(${key}::text) ${operator} unaccent(${pgPlaceholderGenerator(index)})`
)
}
} else {
whereParts.push(
clientType === 'pg'
? `${key} ${operator} ${pgPlaceholderGenerator(index)}`
: `${key} ${operator} ${mysqlPlaceholderGenerator()}`
)
}
index++
values.push(value)
}
}
}
}
if ('JOINS' in conditions) {
const logicalOperator = conditions.JOINS ? 'AND' : 'OR'
const compositeConditions = conditions.JOINS
if (Array.isArray(compositeConditions)) {
const subWhereParts = compositeConditions
.map((subCondition: any) => {
if (
typeof subCondition === 'object' &&
!Array.isArray(subCondition) &&
subCondition !== null
) {
const key = Object.keys(subCondition)[0]
const condition = subCondition[key]
// Adiciona o tratamento de unaccent nas condições de JOINS
processCondition(key, condition)
return whereParts.pop()
}
return ''
})
.filter((part) => part)
whereParts.push(`(${subWhereParts.join(` ${logicalOperator} `)})`)
}
} else {
Object.entries(conditions).forEach(([key, value]) =>
processCondition(key, value as Condition<T>)
)
}
if ('OR' in conditions || 'AND' in conditions) {
const logicalOperator = conditions.OR ? 'OR' : 'AND'
const compositeConditions = conditions.OR || conditions.AND
if (Array.isArray(compositeConditions)) {
const subWhereParts = compositeConditions
.map((subCondition: any) => {
if (
typeof subCondition === 'object' &&
!Array.isArray(subCondition) &&
subCondition !== null
) {
const key = Object.keys(subCondition)[0]
const condition = subCondition[key]
processCondition(key, condition) // Certifica que o unaccent é processado aqui também
return whereParts.pop()
}
return ''
})
.filter((part) => part)
whereParts.push(`(${subWhereParts.join(` ${logicalOperator} `)})`)
}
}
const whereClause =
whereParts.length > 0 ? ` WHERE ${whereParts.join(' AND ')}` : ''
return [whereClause, values, index]
}
export const createOrderByClause = (orderBy?: OrderBy) => {
if (!orderBy || orderBy.length === 0) return ''
const clause = orderBy.map((o) => `${o.field} ${o.direction}`).join(', ')
return ` ORDER BY ${clause}`
}
export const createGroupByClause = (groupBy?: string[]) => {
if (!groupBy || groupBy.length === 0) return ''
return ` GROUP BY ${groupBy.join(', ')}`
}
export const createLimitClause = (limit?: number) => {
if (!limit) return ''
return ` LIMIT ${limit}`
}
export const createOffsetClause = (offset?: number) => {
if (!offset) return ''
return ` OFFSET ${offset}`
}