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

Fix: hasManyThrough inclusion performance #8251

Merged

Conversation

0x0aNL
Copy link
Contributor

@0x0aNL 0x0aNL commented Jan 19, 2022

Fixes #8250

As described in my proposal in #8250, this PR changes the hasManyThrough inclusion resolver from performing at least one query per through model, to performing a single query per inclusion level.

Changes in this PR:
hasManyThrough inclusion resolver:

  • collect all target model keys from the through models
  • perform a single findByForeignKeys call
  • do some magic to adhere to scope limit and order
  • "rearrange" the result in the proper format and return

repository-tests:

Possible issues:

  • scope magic: limit and fields are emulated and as such may not work properly or keep working properly
  • unforeseen scope issues: I feel uncertain that limit and fields, even if properly emulated, are enough to satisfy all scope cases, despite all tests passing
  • repository tests: I fear I may have broken some guidelines. Perhaps the commit should be discarded

Notes:

  • Doesn't fix the "issue" of re-querying an Item in an inclusion of depth 3 with bi-directional hasManyThrough (e.g., when finding Item with relation Category with relation Item, all Items will be queried twice)

Checklist

  • DCO (Developer Certificate of Origin) signed in all commits
  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide (not 100% sure)
  • API Documentation in code was updated
  • Documentation in /docs/site was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in examples/* were updated

Copy link
Member

@achrinza achrinza left a comment

Choose a reason for hiding this comment

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

The changes LGTM; If possible, I'd like another maintainer's approval before merging.

@dhmlau @raymondfeng

Copy link
Contributor

@nabdelgadir nabdelgadir left a comment

Choose a reason for hiding this comment

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

LGTM, just some minor comments

@@ -185,19 +231,27 @@ export function hasManyThroughInclusionResolverAcceptance(
expect(toJSON(result)).to.deepEqual(toJSON(expected));
});

it('honours limit scope when returning a model', async () => {
it('honours limit and order scope when returning a model', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

question - how is this test showing it's honouring the order?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are 3 customers of which only 2 are selected due to the limit. The two that are expected to be returned are Zelda and Voldemort (in that order). Since that order does not correlate to either the creation sequence of the customers nor the linking sequence of the carts to the customers, it can only be explained by successful usage of the order clause. (Although it could technically be caused by unexpected random order returned by the memory connnector ..)

Perhaps I should add a comment with this explanation to the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I just noticed my response was about a different section of the changeset.

In this test, I added an order clause which should put the second cart item (shield) as the first of the included cart items. The last expect tests whether that is the case.


// get target ids from the through entities by foreign key
const allIds = _.uniq(
throughResult.map(x => x?.map(entity => entity[throughKeyTo])).flat(),
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: would it be possible to use descriptive variables instead of x for here and below?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll change x to throughEntity.

const pickFieldList = Array.isArray(scope?.fields)
? scope?.fields
: undefined;
const omitFieldList = scope?.fields
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice 👍 I think it might be nice to include a comment explaining what this does, but not necessary.

  • scope.fields is undefined -> []
  • scope.fields is an array of properties e.g. ["id", "name"] -> []
  • scope.fields is an object = {id: true, name: false} -> ['name']

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add a comment or two.

@0x0aNL
Copy link
Contributor Author

0x0aNL commented Feb 3, 2022

Questions about updating the PR:

  • Should I commit changes in a single commit with a message like "fix: add explanatory comments"?
  • Alternatively, should I squash with existing commits?
  • Should I rebase on master?

@nabdelgadir
Copy link
Contributor

Questions about updating the PR:

  • Should I commit changes in a single commit with a message like "fix: add explanatory comments"?
  • Alternatively, should I squash with existing commits?

During the review process, it is easier for us if you do separate commits to address comments so we can see the difference. But since this is a small PR, you can just squash it right away to the relevant commit.

  • Should I rebase on master?

I would recommend rebasing, but the final commits will be rebased when it's merged even if you don't rebase locally.

Here's some docs for reference https://loopback.io/doc/en/lb4/submitting_a_pr.html#5-pr-review-process

@0x0aNL 0x0aNL force-pushed the fix/hasManyThrough-inclusion-performance branch from 6b050bd to d84bc11 Compare March 9, 2022 12:08
@0x0aNL
Copy link
Contributor Author

0x0aNL commented Mar 9, 2022

Finally gotten around to update this PR. I did rebase and squash however, so I'll describe the changes:

Next to adding several comments and renaming some variables based on the review so far, there is one bigger change: instead of manually building a pickFieldList and omitFieldList and emulating the field filter, the field filter is now normalized using FilterBuilder. Only the targetKey field is manipulated: making sure it is in the result set, and making sure it is removed from the final response if it is supposed to be omitted. Readability and functionality should be improved!

@samarpanB
Copy link
Contributor

@0x0aNL Can you please rebase this PR so that we can revive this PR and merge it then ?

@0x0aNL 0x0aNL force-pushed the fix/hasManyThrough-inclusion-performance branch from d84bc11 to d47e056 Compare April 7, 2023 12:33
@0x0aNL
Copy link
Contributor Author

0x0aNL commented Apr 7, 2023

@0x0aNL Can you please rebase this PR so that we can revive this PR and merge it then ?

I have rebased the PR!

Signed-off-by: Roderik van Heijst <rvanheijst@0x0a.nl>
…ation inclusion

Signed-off-by: Roderik van Heijst <rvanheijst@0x0a.nl>
@0x0aNL 0x0aNL force-pushed the fix/hasManyThrough-inclusion-performance branch from d47e056 to 765157c Compare August 3, 2023 11:37
@0x0aNL
Copy link
Contributor Author

0x0aNL commented Aug 3, 2023

Rebased once again and fixed performance in cases where no Through models were found.

@coveralls
Copy link

Pull Request Test Coverage Report for Build 5750231698

  • 2 of 21 (9.52%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage decreased (-0.2%) to 69.385%

Changes Missing Coverage Covered Lines Changed/Added Lines %
packages/repository/src/relations/has-many/has-many-through.inclusion-resolver.ts 2 21 9.52%
Totals Coverage Status
Change from base Build 5749758464: -0.2%
Covered Lines: 9498
Relevant Lines: 12384

💛 - Coveralls

@dhmlau dhmlau merged commit 001d970 into loopbackio:master Oct 16, 2023
13 checks passed
@dhmlau
Copy link
Member

dhmlau commented Oct 16, 2023

It seems like this PR has been approved for a while. Merging it now.

@0x0aNL, thanks for your contribution. Your PR has been landed! 🎉

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

Successfully merging this pull request may close these issues.

Query performance issue in inclusion resolver for hasManyThrough
6 participants