Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Select with function in where condition #802

Closed
twavv opened this issue Sep 2, 2020 · 5 comments
Closed

Select with function in where condition #802

twavv opened this issue Sep 2, 2020 · 5 comments
Labels
enhancement New feature or request

Comments

@twavv
Copy link

twavv commented Sep 2, 2020

Is your feature request related to a problem? Please describe.
I'd like some way to query using a function as part of the where condition. Concretely, I have a unique index on UPPER(payload) for some table where the payload is case-insensitive, and I want to query using WHERE UPPER(payload) = UPPER($1) (obviously, the UPPER($1) can be done in the client side, so that's less of an issue here).

Describe the solution you'd like
Unclear what the API would be.

Describe alternatives you've considered
There is a querybuilder way of doing this, but it's not what I'm looking for here.

test('select with custom expression', async () => {
const qb1 = orm.em.createQueryBuilder(Book2);
qb1.select('*').where({ 'json_contains(`e0`.`meta`, ?)': [{ foo: 'bar' }] });
expect(qb1.getQuery()).toEqual('select `e0`.*, `e0`.price * 1.19 as `price_taxed` from `book2` as `e0` where json_contains(`e0`.`meta`, ?)');
expect(qb1.getParams()).toEqual(['{"foo":"bar"}']);

Additional context
Add any other context about the feature request here.

@twavv twavv added the enhancement New feature or request label Sep 2, 2020
@B4nan
Copy link
Member

B4nan commented Sep 2, 2020

You can do the same with EM api, you just need to type cast as this dynamic key won't pass the strict FilterQuery type.

em.find(Book, { 'json_contains(`e0`.`meta`, ?)': [{ foo: 'bar' }] } as any);

@B4nan
Copy link
Member

B4nan commented Sep 2, 2020

We could also have something like expr helper, that could allow doing this I guess:

const author = new Author2('name', 'email');
const b1 = new Book2('b1', author);
const b2 = new Book2('b2', author);
const b3 = new Book2('b3', author);
await orm.em.persistAndFlush([b1, b2, b3]);
orm.em.clear();

const expr: <T> (sql: string) => keyof T = sql => sql as any;
const books = await orm.em.find(Book2, { [expr('upper(title)')]: ['B1', 'B2'] }); // no TS errors
expect(books).toHaveLength(2);

(this test is passing with current version, so you can define such expr helper yourself right now)

WDYT?

edit: its even enough to return a string, so this works too:

const expr = (sql: string) => sql;
const books = await orm.em.find(Book2, { [expr('upper(title)')]: ['B1', 'B2'] }); // no TS errors

B4nan added a commit that referenced this issue Sep 2, 2020
Allows to get around strict `FilterQuery` typing when we need to use
some SQL functions or expressions.

```ts
em.find(Book2, { [expr('upper(title)')]: ['B1', 'B2'] }); // no TS errors
```

Related: #802
@B4nan B4nan closed this as completed in e6b9f0e Sep 2, 2020
@B4nan
Copy link
Member

B4nan commented Sep 2, 2020

In rc.6 it will be possible to do this:

const books = await em.find(Book, {
  [expr('upper(title)')]: em.getKnex().raw('upper(?)', ['b2']),
});

@B4nan B4nan mentioned this issue Sep 2, 2020
46 tasks
@danitrod
Copy link

danitrod commented Sep 5, 2020

I was facing this and managed to work around by creating a RegExp to query and match the case insensitive value. Don't know if it helps for your case, but here it is:

const caseInsensitive = (str: String): RegExp => {
  const len = str.length;
  let exp = '^';
  for (let i = 0; i < len; i++) {
    exp += '[' + str[i].toLowerCase() + str[i].toUpperCase() + ']';
  }
  exp += '$';
  return new RegExp(exp);
};

const book = await em.findOne(Book, { title: caseInsensitive(title) });

@KevinNovak
Copy link

KevinNovak commented Oct 21, 2022

@danitrod If you want to find a case insensitive value you can just use a RegExp with the 'i' flag like so:

const book = await em.findOne(Book, {
    title: new RegExp(`^${title}$`, 'i'),
});

Also make sure to escape any regex characters, especially if that title string is coming from the user:

title = title.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');

Hope this helps someone in the future!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants