Skip to content

Commit

Permalink
feat(mongo): support field level logical operations
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jan 18, 2022
1 parent 1829154 commit 3fae404
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 25 deletions.
71 changes: 53 additions & 18 deletions plugins/database/mongo/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
import { Query, Random, valueMap } from 'koishi'
import { Filter, FilterOperators } from 'mongodb'

function transformFieldQuery(query: Query.FieldQuery, key: string, exprs: any[]) {
function createFieldFilter(query: Query.FieldQuery, key: string) {
const filters: Filter<any>[] = []
const result: Filter<any> = {}
const child = transformFieldQuery(query, key, filters)
if (child === false) return false
if (child !== true) result[key] = child
if (filters.length) result.$and = filters
if (Object.keys(result).length) return result
return true
}

function transformFieldQuery(query: Query.FieldQuery, key: string, filters: Filter<any>[]) {
// shorthand syntax
if (typeof query === 'string' || typeof query === 'number' || query instanceof Date) {
return { $eq: query }
} else if (Array.isArray(query)) {
if (!query.length) return
if (!query.length) return false
return { $in: query }
} else if (query instanceof RegExp) {
return { $regex: query }
Expand All @@ -15,28 +26,52 @@ function transformFieldQuery(query: Query.FieldQuery, key: string, exprs: any[])
// query operators
const result: FilterOperators<any> = {}
for (const prop in query) {
if (prop === '$el') {
result.$elemMatch = transformFieldQuery(query[prop], key, exprs)
if (prop === '$and') {
for (const item of query[prop]) {
const child = createFieldFilter(item, key)
if (child === false) return false
if (child !== true) filters.push(child)
}
} else if (prop === '$or') {
const $or: Filter<any>[] = []
if (!query[prop].length) return false
const always = query[prop].some((item) => {
const child = createFieldFilter(item, key)
if (typeof child === 'boolean') return child
$or.push(child)
})
if (!always) filters.push({ $or })
} else if (prop === '$not') {
const child = createFieldFilter(query[prop], key)
if (child === true) return false
if (child !== false) filters.push({ $nor: [child] })
} else if (prop === '$el') {
const child = transformFieldQuery(query[prop], key, filters)
if (child === false) return false
if (child !== true) result.$elemMatch = child
} else if (prop === '$regexFor') {
exprs.push({
$function: {
body: function (data: string, value: string) {
return new RegExp(data, 'i').test(value)
}.toString(),
args: ['$' + key, query.$regexFor],
lang: 'js',
filters.push({
$expr: {
$function: {
body: function (data: string, value: string) {
return new RegExp(data, 'i').test(value)
}.toString(),
args: ['$' + key, query.$regexFor],
lang: 'js',
},
},
})
} else {
result[prop] = query[prop]
}
}
if (!Object.keys(result).length) return true
return result
}

export function transformQuery(query: Query.Expr) {
const filter: Filter<any> = {}
const exprs: any[] = []
const additional: Filter<any>[] = []
for (const key in query) {
const value = query[key]
if (key === '$and' || key === '$or') {
Expand All @@ -55,15 +90,15 @@ export function transformQuery(query: Query.Expr) {
const query = transformQuery(value)
if (query) filter.$nor = [query]
} else if (key === '$expr') {
exprs.push(transformEval(value))
additional.push({ $expr: transformEval(value) })
} else {
const query = transformFieldQuery(value, key, exprs)
if (!query) return
if (Object.keys(query).length) filter[key] = query
const query = transformFieldQuery(value, key, additional)
if (query === false) return
if (query !== true) filter[key] = query
}
}
if (exprs.length) {
(filter.$and ||= []).push(...exprs.map($expr => ({ $expr })))
if (additional.length) {
(filter.$and ||= []).push(...additional)
}
return filter
}
Expand Down
8 changes: 1 addition & 7 deletions plugins/database/mongo/tests/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ for (const port of mongoPorts ? mongoPorts.split(',') : []) {
port: +port,
})

tests.database(app, {
query: {
logical: {
fieldLevel: false,
},
},
})
tests.database(app)
})
}

0 comments on commit 3fae404

Please sign in to comment.