@naturalcycles/datastore-lib
Version:
Opinionated library to work with Google Datastore, implements CommonDB
68 lines (54 loc) • 1.94 kB
text/typescript
import type { PropertyFilter, Query } from '@google-cloud/datastore'
import type { DBQuery, DBQueryFilterOperator } from '@naturalcycles/db-lib'
import type { ObjectWithId, StringMap } from '@naturalcycles/js-lib/types'
const FNAME_MAP: StringMap = {
id: '__key__',
}
const OP_MAP: Partial<Record<DBQueryFilterOperator, string>> = {
'==': '=',
in: 'IN',
'not-in': 'NOT_IN',
}
export function dbQueryToDatastoreQuery<ROW extends ObjectWithId>(
dbQuery: Readonly<DBQuery<ROW>>,
emptyQuery: Query,
propertyFilterClass: typeof PropertyFilter,
): Query {
let q = emptyQuery
// filter
for (const f of dbQuery._filters) {
// keeping "previous syntax" commented out
// (q, f) => q.filter(f.name as string, OP_MAP[f.op] || (f.op as any), f.val),
// Datastore doesn't allow `undefined` as filter value.
// We don't want to throw on it, so instead we'll replace it with valid value of `null`.
// `a > null` will return anything that's indexed
// `a < null` should return nothing
// `a == null` will return just that - rows with null values
let { op, val } = f
if (val === undefined) val = null
q = q.filter(new propertyFilterClass(f.name as string, OP_MAP[op] || (op as any), val))
}
// limit
q = q.limit(dbQuery._limitValue || 0)
// order
for (const ord of dbQuery._orders) {
q = q.order(ord.name as string, { descending: ord.descending })
}
// select
if (dbQuery._selectedFieldNames) {
const fields = (dbQuery._selectedFieldNames as string[]).map(f => FNAME_MAP[f] || f)
// Datastore requires you to specify at least one column, so if empty array is passed - it'll include __key__ at least
if (!fields.length) {
fields.push('__key__')
}
q = q.select(fields)
}
// cursor
if (dbQuery._startCursor) {
q = q.start(dbQuery._startCursor)
}
if (dbQuery._endCursor) {
q = q.end(dbQuery._endCursor)
}
return q
}