diff --git a/packages/mongo/src/utils.ts b/packages/mongo/src/utils.ts index 1c362f7b..ba992793 100644 --- a/packages/mongo/src/utils.ts +++ b/packages/mongo/src/utils.ts @@ -55,12 +55,9 @@ function transformFieldQuery(query: Query.FieldQuery, key: string, filters: Filt } else if (prop === '$regexFor') { filters.push({ $expr: { - $function: { - body: function (data: string, value: string) { - return new RegExp(data, 'i').test(value) - }.toString(), - args: ['$' + key, query.$regexFor], - lang: 'js', + $regexMatch: { + input: query[prop], + regex: '$' + key, }, }, }) @@ -105,6 +102,8 @@ export class Transformer { $array: (arg, group) => this.transformEvalExpr(arg), $object: (arg, group) => this.transformEvalExpr(arg), + $regex: (arg, group) => ({ $regexMatch: { input: this.eval(arg[0], group), regex: this.eval(arg[1], group) } }), + $length: (arg, group) => ({ $size: this.eval(arg, group) }), $nin: (arg, group) => ({ $not: { $in: arg.map(val => this.eval(val, group)) } }), diff --git a/packages/mysql/src/index.ts b/packages/mysql/src/index.ts index 4396fedf..5a07fd31 100644 --- a/packages/mysql/src/index.ts +++ b/packages/mysql/src/index.ts @@ -152,6 +152,8 @@ class MySQLBuilder extends Builder { escape(value: any, field?: Field) { if (value instanceof Date) { value = Time.template('yyyy-MM-dd hh:mm:ss', value) + } else if (value instanceof RegExp) { + value = value.source } else if (!field && !!value && typeof value === 'object') { return `json_extract(${this.quote(JSON.stringify(value))}, '$')` } diff --git a/packages/postgres/src/index.ts b/packages/postgres/src/index.ts index aaf2bdb8..b0096b0f 100644 --- a/packages/postgres/src/index.ts +++ b/packages/postgres/src/index.ts @@ -212,6 +212,8 @@ class PostgresBuilder extends Builder { return `coalesce(${args.map(arg => this.parseEval(arg, type)).join(', ')})` }, + $regex: ([key, value]) => `${this.parseEval(key)} ~ ${this.parseEval(value)}`, + // number $add: (args) => `(${args.map(arg => this.parseEval(arg, 'double precision')).join(' + ')})`, $multiply: (args) => `(${args.map(arg => this.parseEval(arg, 'double precision')).join(' * ')})`, @@ -291,7 +293,7 @@ class PostgresBuilder extends Builder { parseEval(expr: any, outtype: boolean | string = false): string { this.state.sqlType = 'raw' - if (typeof expr === 'string' || typeof expr === 'number' || typeof expr === 'boolean' || expr instanceof Date) { + if (typeof expr === 'string' || typeof expr === 'number' || typeof expr === 'boolean' || expr instanceof Date || expr instanceof RegExp) { return this.escape(expr) } return outtype ? this.jsonUnquote(this.parseEvalExpr(expr), false, typeof outtype === 'string' ? outtype : undefined) : this.parseEvalExpr(expr) @@ -373,6 +375,8 @@ class PostgresBuilder extends Builder { escape(value: any, field?: Field) { if (value instanceof Date) { value = formatTime(value) + } else if (value instanceof RegExp) { + value = value.source } else if (!field && !!value && typeof value === 'object') { return `${this.quote(JSON.stringify(value))}::jsonb` } diff --git a/packages/sql-utils/src/index.ts b/packages/sql-utils/src/index.ts index e0b359d8..c28ee1da 100644 --- a/packages/sql-utils/src/index.ts +++ b/packages/sql-utils/src/index.ts @@ -127,6 +127,7 @@ export class Builder { // string $concat: (args) => `concat(${args.map(arg => this.parseEval(arg)).join(', ')})`, + $regex: ([key, value]) => `${this.parseEval(key)} regexp ${this.parseEval(value)}`, // logical $or: (args) => this.logicalOr(args.map(arg => this.parseEval(arg))), @@ -390,7 +391,7 @@ export class Builder { parseEval(expr: any, unquote: boolean = true): string { this.state.sqlType = 'raw' - if (typeof expr === 'string' || typeof expr === 'number' || typeof expr === 'boolean' || expr instanceof Date) { + if (typeof expr === 'string' || typeof expr === 'number' || typeof expr === 'boolean' || expr instanceof Date || expr instanceof RegExp) { return this.escape(expr) } return unquote ? this.jsonUnquote(this.parseEvalExpr(expr)) : this.parseEvalExpr(expr) diff --git a/packages/sqlite/src/index.ts b/packages/sqlite/src/index.ts index 69701e35..fd59dcab 100644 --- a/packages/sqlite/src/index.ts +++ b/packages/sqlite/src/index.ts @@ -100,6 +100,7 @@ class SQLiteBuilder extends Builder { escape(value: any, field?: Field) { if (value instanceof Date) value = +value + else if (value instanceof RegExp) value = value.source return super.escape(value, field) } diff --git a/packages/tests/src/query.ts b/packages/tests/src/query.ts index b96e2b3f..d2772857 100644 --- a/packages/tests/src/query.ts +++ b/packages/tests/src/query.ts @@ -219,6 +219,14 @@ namespace QueryOperators { text: /^.*foo$/, })).eventually.to.have.length(1).with.nested.property('[0].text').equal('awesome foo') }) + + regexBy && regexFor && it('$.regex', async () => { + await expect(database.get('temp1', row => $.regex('foo bar', row.regex))).eventually.to.have.length(2) + await expect(database.get('temp1', row => $.regex('baz', row.regex))).eventually.to.have.length(1) + await expect(database.get('temp1', row => $.regex(row.text, /^.*foo.*$/))).eventually.to.have.length(2) + await expect(database.get('temp1', row => $.regex(row.text, /^.*bar.*$/))).eventually.to.have.length(2) + await expect(database.get('temp1', row => $.regex(row.text, row.regex))).eventually.to.have.length(2) + }) } export const bitwise = function Bitwise(database: Database) {