Skip to content

Commit

Permalink
fix(query-builder): respect case-insensitive regexp flag
Browse files Browse the repository at this point in the history
Closes #3801
  • Loading branch information
B4nan committed Nov 29, 2022
1 parent 5ba1af1 commit 1a1d381
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 5 deletions.
10 changes: 9 additions & 1 deletion packages/core/src/platforms/Platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,18 @@ export abstract class Platform {
return 'time' + (length ? `(${length})` : '');
}

getRegExpOperator(): string {
getRegExpOperator(val?: unknown, flags?: string): string {
return 'regexp';
}

getRegExpValue(val: RegExp): { $re: string; $flags?: string } {
if (val.flags.includes('i')) {
return { $re: `(?i)${val.source}` };
}

return { $re: val.source };
}

isAllowedTopLevelOperator(operator: string) {
return operator === '$not';
}
Expand Down
10 changes: 7 additions & 3 deletions packages/knex/src/query/QueryBuilderHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class QueryBuilderHelper {
}

if (value instanceof RegExp) {
value = { $re: value.source };
value = this.platform.getRegExpValue(value);
}

if (Utils.isOperator(key, false) && Utils.isPlainObject(value)) {
Expand Down Expand Up @@ -340,6 +340,10 @@ export class QueryBuilderHelper {
return false;
}

if (re.flags.includes('i')) {
return false;
}

// when including the opening bracket/paren we consider it complex
return !re.source.match(/[{[(]/);
}
Expand Down Expand Up @@ -463,7 +467,7 @@ export class QueryBuilderHelper {
}

if (value instanceof RegExp) {
value = { $re: value.source };
value = this.platform.getRegExpValue(value);
}

// operators
Expand Down Expand Up @@ -515,7 +519,7 @@ export class QueryBuilderHelper {
}

if (op === '$re') {
replacement = this.platform.getRegExpOperator();
replacement = this.platform.getRegExpOperator(value[op], value.$flags);
}

return replacement;
Expand Down
14 changes: 13 additions & 1 deletion packages/postgresql/src/PostgreSqlPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,22 @@ export class PostgreSqlPlatform extends AbstractSqlPlatform {
return `create index ${quotedIndexName} on ${quotedTableName} using gin(to_tsvector('simple', ${quotedColumnNames.join(` || ' ' || `)}))`;
}

getRegExpOperator(): string {
getRegExpOperator(val?: unknown, flags?: string): string {
if ((val instanceof RegExp && val.flags.includes('i')) || flags?.includes('i')) {
return '~*';
}

return '~';
}

getRegExpValue(val: RegExp): { $re: string; $flags?: string } {
if (val.flags.includes('i')) {
return { $re: val.source, $flags: val.flags };
}

return { $re: val.source };
}

isBigIntProperty(prop: EntityProperty): boolean {
return super.isBigIntProperty(prop) || (['bigserial', 'int8'].includes(prop.columnTypes?.[0]));
}
Expand Down
44 changes: 44 additions & 0 deletions tests/QueryBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,16 @@ describe('QueryBuilder', () => {
qb.select('*').where({ name: { $re: '^c.o.*l-te.*st.c.m$' } });
expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` where `e0`.`name` regexp ?');
expect(qb.getParams()).toEqual(['^c.o.*l-te.*st.c.m$']);

qb = orm.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: new RegExp('^c.o.*l-te.*st.c.m$', 'i') });
expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` where `e0`.`name` regexp ?');
expect(qb.getParams()).toEqual(['(?i)^c.o.*l-te.*st.c.m$']);

qb = orm.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: /^c.o.*l-te.*st.c.m$/i });
expect(qb.getQuery()).toEqual('select `e0`.* from `publisher2` as `e0` where `e0`.`name` regexp ?');
expect(qb.getParams()).toEqual(['(?i)^c.o.*l-te.*st.c.m$']);
});

test('$exists operator', async () => {
Expand Down Expand Up @@ -2602,6 +2612,40 @@ describe('QueryBuilder', () => {
expect(qb.getQuery()).toEqual(sql);
expect(qb.getParams()).toEqual(['tag name', 20, 1]);


// select by regexp operator
{
let qb = pg.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: { $re: 'test' } });
expect(qb.getQuery()).toEqual('select "p0".* from "publisher2" as "p0" where "p0"."name" ~ $1');
expect(qb.getParams()).toEqual(['test']);

qb = pg.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: { $re: '^test' } });
expect(qb.getQuery()).toEqual('select "p0".* from "publisher2" as "p0" where "p0"."name" ~ $1');
expect(qb.getParams()).toEqual(['^test']);

qb = pg.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: { $re: 't.st$' } });
expect(qb.getQuery()).toEqual('select "p0".* from "publisher2" as "p0" where "p0"."name" ~ $1');
expect(qb.getParams()).toEqual(['t.st$']);

qb = pg.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: { $re: '^c.o.*l-te.*st.c.m$' } });
expect(qb.getQuery()).toEqual('select "p0".* from "publisher2" as "p0" where "p0"."name" ~ $1');
expect(qb.getParams()).toEqual(['^c.o.*l-te.*st.c.m$']);

qb = pg.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: new RegExp('^c.o.*l-te.*st.c.m$', 'i') });
expect(qb.getQuery()).toEqual('select "p0".* from "publisher2" as "p0" where "p0"."name" ~* $1');
expect(qb.getParams()).toEqual(['^c.o.*l-te.*st.c.m$']);

qb = pg.em.createQueryBuilder(Publisher2);
qb.select('*').where({ name: /^c.o.*l-te.*st.c.m$/i });
expect(qb.getQuery()).toEqual('select "p0".* from "publisher2" as "p0" where "p0"."name" ~* $1');
expect(qb.getParams()).toEqual(['^c.o.*l-te.*st.c.m$']);
}

await pg.close(true);
});

Expand Down

0 comments on commit 1a1d381

Please sign in to comment.