Skip to content

Do not use DISTINCT where there are no joins#2611

Merged
teohhanhui merged 1 commit into
api-platform:2.4from
soyuka:micro-optimization-doctrine-distinct
Mar 14, 2019
Merged

Do not use DISTINCT where there are no joins#2611
teohhanhui merged 1 commit into
api-platform:2.4from
soyuka:micro-optimization-doctrine-distinct

Conversation

@soyuka

@soyuka soyuka commented Mar 13, 2019

Copy link
Copy Markdown
Member
Q A
Bug fix? yes ?
New feature? no
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #2552
License MIT
Doc PR na

@teohhanhui would this work?

Comment thread src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php Outdated
Comment thread src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php
Comment thread src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php Outdated
@soyuka
soyuka force-pushed the micro-optimization-doctrine-distinct branch 3 times, most recently from c31a649 to db98be3 Compare March 13, 2019 14:50
Comment thread src/Bridge/Doctrine/Orm/Extension/PaginationExtension.php Outdated
@soyuka
soyuka force-pushed the micro-optimization-doctrine-distinct branch from db98be3 to 1806c48 Compare March 13, 2019 14:53
Comment thread tests/Bridge/Doctrine/Orm/Extension/PaginationExtensionTest.php

@meyerbaptiste meyerbaptiste left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Wonderful, I stopped counting the number of times I had to do this in projects! 🎉

@teohhanhui
teohhanhui merged commit 20ae0f2 into api-platform:2.4 Mar 14, 2019
@teohhanhui

Copy link
Copy Markdown
Contributor

Thanks @soyuka! 🎉

@bendavies

bendavies commented Mar 14, 2019

Copy link
Copy Markdown
Contributor

having tried this, i think it's hard to take advantage of this because of eager loading.

to improve, left joins could be removed from the count query, i think? i think that's harder than it sounds though.

@soyuka
soyuka deleted the micro-optimization-doctrine-distinct branch March 14, 2019 16:27
@teohhanhui

Copy link
Copy Markdown
Contributor

@bendavies But eager loading only kicks in if you use those relations in your serializer groups, no? So, DISTINCT is indeed necessary for such a query. (Or did you mean rewriting the query for COUNT?)

@bendavies

Copy link
Copy Markdown
Contributor

ah, it's because i have one property with @ApiProperty(fetchEager=true)

@bendavies

Copy link
Copy Markdown
Contributor

yes, i meant writing the query for count, as left joins don't affect the final count.

@teohhanhui

Copy link
Copy Markdown
Contributor

yes, i meant writing the query for count, as left joins don't affect the final count.

You'll have to propose that in Doctrine ORM. ;)

@bendavies

bendavies commented Mar 18, 2019

Copy link
Copy Markdown
Contributor

there is an improvement to be made here.
the count query has the distinct removed, but the next query to get the list of IDs does not
image

@soyuka

soyuka commented Mar 19, 2019

Copy link
Copy Markdown
Member Author

Mhhh, I'm not sure that we have access to another query where we could set the hint?!

@teohhanhui

Copy link
Copy Markdown
Contributor

@bendavies Same response as above: #2611 (comment)

You'll have to propose that in Doctrine ORM. ;)

@er1z

er1z commented Mar 19, 2019

Copy link
Copy Markdown
Contributor

You'll have to propose that in Doctrine ORM. ;)

Good luck. :D

@bendavies

Copy link
Copy Markdown
Contributor

ah, that's not our query? oh well

@meyerbaptiste

meyerbaptiste commented Mar 20, 2019

Copy link
Copy Markdown
Member

@teohhanhui

Copy link
Copy Markdown
Contributor

Hmm... But fetchJoinCollection should be set to false when there are no joins, right?

So, I think we could improve the logic here:

/**
* Determines whether the Paginator should fetch join collections, if the root entity uses composite identifiers it should not.
*
* @see https://github.com/doctrine/doctrine2/issues/2910
*/
private function useFetchJoinCollection(QueryBuilder $queryBuilder, string $resourceClass = null, string $operationName = null): bool
{
if (QueryChecker::hasRootEntityWithCompositeIdentifier($queryBuilder, $this->managerRegistry)) {
return false;
}
if (null === $resourceClass) {
return true;
}
$resourceMetadata = $this->resourceMetadataFactory->create($resourceClass);
return $resourceMetadata->getCollectionOperationAttribute($operationName, 'pagination_fetch_join_collection', true, true);
}

@teohhanhui

Copy link
Copy Markdown
Contributor

By default the pagination extension does the following steps to compute the correct result:

- Perform a Count query using `DISTINCT` keyword.
- Perform a Limit Subquery with `DISTINCT` to find all ids of the entity in from on the current page.
- Perform a WHERE IN query to get all results for the current page.

This behavior is only necessary if you actually fetch join a to-many collection. You can disable this behavior by setting the $fetchJoinCollection flag to false; in that case only 2 instead of the 3 queries described are executed. We hope to automate the detection for this in the future.

https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/tutorials/pagination.html

I don't know how I've missed this important optimization... 🙈

@teohhanhui

Copy link
Copy Markdown
Contributor

Note to self: we probably have to take care of fetch eager associations.

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.

6 participants