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

✨ Introduce new custom where condition construction #66

Merged
merged 10 commits into from
Dec 2, 2021

Conversation

s0
Copy link
Contributor

@s0 s0 commented Dec 2, 2021

As discussed earlier today, this PR introduces a new abstraction over where conditions (with support for nested conjunctions (AND) and disjunctions (OR)), to allow for better type-safety with data used for constructing queries, and to make our most common use-cases easier to write.

The queries generated by this are unit-tested in hpc_service: UN-OCHA/hpc_service#2529. (This is needed until #26 is merged)

This PR makes use of query builders in the where clauses type errors, using query builders in conditions is still possible, but now requires us to explicitly use [Cond.BUILDER]. This should help us catch and rewrite all instances of usage of the old builder when not neccesary.

(this will cause type-error conflict with #57, so whichever is merged last will need to be rebased and fixed)

@s0 s0 requested a review from a team as a code owner December 2, 2021 16:04
@s0 s0 assigned czmj and Pl217 Dec 2, 2021
@s0 s0 added the ready for review All comments have been addressed, and the Pull Request is ready for review label Dec 2, 2021
@s0 s0 force-pushed the improved-conditions branch 3 times, most recently from 7f98dc4 to 8ad6769 Compare December 2, 2021 16:26
s0 added 7 commits December 2, 2021 17:03
Given a knex query builder, and one of our own custom conditions,
return the prepared condition to be used directly by knex.
There are a few reasons we do this rather than simply using knex query
builder directly:

* the query builder does not offer good type-safety (for example whereIn
  does not require the type of the values to match the type of the column)
* we find ourselves needing to use query builders often for very simple
  conditions that could be abstracted + simplified

However, we retain the ability to use a query builder when needed.
Some of our unit tests iterate through every table (e.g. to check the
schema for each table). Combining tables along with util properties
made this more complicated, so we also need to provide a way to easily
get all tables.

Beyond this, we can tidy up the types a bit more.
Copy link
Contributor

@Pl217 Pl217 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another brilliant piece! 👏

My inline comments are mostly on expanding this, because I would like some more operators added.

*/
export namespace ConditionSymbols {
export const BUILDER = Symbol('builder');
export const AND = Symbol('and');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly would be the benefit of using AND, besides completeness with having OR? When properties are supplied to where, AND is the default. Is there some construct where this would be useful, that I am not seeing?

If not, I would like to drop AND because I would not like to keep telling people during reviews: "Hey, don't use AND since it is a default behavior anyway, let's simplify this`

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, in cases where we want to use OR or a query builder in one of the AND clauses, or if we want to combine some query builder stuff with some normal property stuff. Something like:

(A or B) and (B or C) or (A or B) and C.

something like this in the unit tests: https://github.com/UN-OCHA/hpc_service/blob/1a0166ffb0d5fe47934de8a5cf6b51566a65f8c5/test/hpc-api-core/src/db/util/conditions.spec.ts#L129-L136

Or:

{
  [Cond.AND]: [
    {
      id: 123,
    },
    {
      [Cond.BUILDER]: (qb) => qb.whereNotNull('id'),
    },
  ],
}

But yeah, in the cases where we're ONLY using AND, and not using an OR or BUILDER inside of an AND clause, we'd want to use the simpler / cleaner construction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I understand. I didn't realize immediately that

if (OverallConditions.isQueryBuilderCondition(condition)) {
  builder.where(condition[Cond.BUILDER]);
}

swallows "plain" conditions, so I thought you could write the same thing as:

{
  id: 123,
  [models.v4.Cond.BUILDER]: (qb) => qb.whereNotNull('id'),
}

src/db/util/conditions.ts Show resolved Hide resolved
src/db/util/conditions.ts Show resolved Hide resolved
@Pl217 Pl217 removed their assignment Dec 2, 2021
@Pl217 Pl217 added needs minor changes There are review or issue comments to address and removed ready for review All comments have been addressed, and the Pull Request is ready for review labels Dec 2, 2021
@s0 s0 added ready for review All comments have been addressed, and the Pull Request is ready for review and removed needs minor changes There are review or issue comments to address labels Dec 2, 2021
@s0 s0 assigned Pl217 Dec 2, 2021
};

export type IsNullCondition = {
[Op.IS_NULL]: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that we should use {propertyName: null} for propertyName IS NULL and introduce [Op.IS_NOT_NULL]: null or [Op.NE]: null, but I am fine with what you used.

It is saying that NULL should be treated as a special case, and even though there are two ways to write IS NULL now ({propertyName: null} is more likely to be used), let's not complicate any further and use what you suggested.

@Pl217 Pl217 merged commit 88b7be9 into develop Dec 2, 2021
@Pl217 Pl217 deleted the improved-conditions branch December 2, 2021 18:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready for review All comments have been addressed, and the Pull Request is ready for review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants