type-arango
Version:
ArangoDB Foxx decorators and utilities for TypeScript
100 lines (85 loc) • 3.06 kB
text/typescript
import {isFoxx, toArray} from '../utils'
import {QueryOpt} from '../types'
import {logger} from '../index'
const is = isFoxx()
const orders = ['ASC','DESC']
export const arango = is ? require('@arangodb') : null
export const db = is ? arango.db : null
export const operators = ['==', '!=', '<', '<=', '>', '>=', 'IN', 'NOT IN', 'LIKE', 'NOT LIKE', '=~', '!~', 'HAS']
function escape(val: any){
if(val === 'true') return true
if(val === 'false') return false
if(val === 'null') return null
if(Array.isArray(val)) return '["'+clean(val).join('","')+'"]'
switch(typeof val){
case 'string': return '"'+val+'"'
case 'number':
case 'boolean': return val
default: throw {message:'INVALID_ESCAPING',val}
}
}
function clean(val: any): any {
if(Array.isArray(val)) return val.map(v => clean(v))
switch(typeof val){
default:
case 'number': return val
case 'string':
const indexOf = val.indexOf(' ')
return (indexOf > -1 ? val.substr(0, indexOf) : val).replace(/[^a-zA-Z0-9-_.\[*\]]/g, '')
}
}
export function queryBuilder(collection: string, {filter,sort,limit,aggregate,keep,unset}: QueryOpt){
let q = ['FOR i IN '+collection]
if(filter){
for(const f of toArray(filter)){
const entries = Object.entries(f).filter(([key]) => key !== '$')
if(!entries.length) continue
q.push(
`FILTER (${entries.map(([key, value]: any) => (
// ['HAS', value] => FILTER value IN TO_ARRAY(i.key)
Array.isArray(value) && value[0] === 'HAS'
? `${escape(value[1])} IN TO_ARRAY(i.${clean(key)})`
// ['!=', value] => FILTER i.key != value
: Array.isArray(value) && operators.includes(value[0])
? `i.${clean(key)} ${value[0]} ${escape(value[1])}`
// ['value1','value2'] => FILTER i.key IN [...values]
: Array.isArray(value)
? `i.${clean(key)} IN ${escape(value)}`
// value => FILTER i.key == value
: `i.${clean(key)} == ${escape(value)}`
)
).join(` ${f.$ || '&&'} `)})`
)
}
}
if(sort){
q.push('SORT i.'+(Array.isArray(sort) ? sort.map(s => {
if(s.includes(' ')){
let [a,o] = s.split(' ')
if(!orders.includes(o.toUpperCase()))
o = orders[0]
return clean(a)+' '+o
}
return clean(s) + ' ' + orders[0]
}).join(', i.') : clean(sort)))
}
if(limit){
limit = clean(limit)
q.push('LIMIT '+(Array.isArray(limit) ? limit.join(',') : limit))
}
let ret = 'i'
if(keep){
keep = clean(keep)
ret = `KEEP(i, "${keep!.join('","')}")`
} else if(unset){
unset = clean(unset)
ret = `UNSET(i, "${unset!.join('","')}")`
}
if(aggregate){
q.push(`COLLECT AGGREGATE sum = SUM(${typeof aggregate === 'string' ? aggregate : 1})`)
q.push(`RETURN sum`)
} else
q.push('RETURN '+ret)
logger.info('Query %o', q.join(' '))
return q.join('\n')
}