mirror of
https://github.com/shlinkio/shlink.git
synced 2025-03-29 13:03:52 +03:00
Improved performance when filtering out shortUrls which reached their limit by using a sub-query
This commit is contained in:
parent
d832133410
commit
8807a78463
1 changed files with 10 additions and 26 deletions
|
@ -18,6 +18,7 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier;
|
||||||
use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
|
use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
|
||||||
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsCountFiltering;
|
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsCountFiltering;
|
||||||
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsListFiltering;
|
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsListFiltering;
|
||||||
|
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
||||||
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
|
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
|
||||||
|
|
||||||
use function array_column;
|
use function array_column;
|
||||||
|
@ -36,18 +37,13 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||||
->setMaxResults($filtering->limit)
|
->setMaxResults($filtering->limit)
|
||||||
->setFirstResult($filtering->offset);
|
->setFirstResult($filtering->offset);
|
||||||
|
|
||||||
// Some DB engines (postgres and ms) require aggregates in the select for ordering and filtering
|
|
||||||
if ($filtering->excludeMaxVisitsReached || $filtering->orderBy->field === 'visits') {
|
|
||||||
$qb->addSelect('COUNT(DISTINCT v)');
|
|
||||||
}
|
|
||||||
|
|
||||||
// In case the ordering has been specified, the query could be more complex. Process it
|
// In case the ordering has been specified, the query could be more complex. Process it
|
||||||
if ($filtering->orderBy->hasOrderField()) {
|
if ($filtering->orderBy->hasOrderField()) {
|
||||||
$this->processOrderByForList($qb, $filtering);
|
$this->processOrderByForList($qb, $filtering);
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $qb->getQuery()->getResult();
|
$result = $qb->getQuery()->getResult();
|
||||||
if ($filtering->excludeMaxVisitsReached || $filtering->orderBy->field === 'visits') {
|
if ($filtering->orderBy->field === 'visits') {
|
||||||
return array_column($result, 0);
|
return array_column($result, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,12 +58,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||||
if ($fieldName === 'visits') {
|
if ($fieldName === 'visits') {
|
||||||
// FIXME This query is inefficient.
|
// FIXME This query is inefficient.
|
||||||
// Diagnostic: It might need to use a sub-query, as done with the tags list query.
|
// Diagnostic: It might need to use a sub-query, as done with the tags list query.
|
||||||
if (! $filtering->excludeMaxVisitsReached) {
|
$qb->addSelect('COUNT(DISTINCT v)')
|
||||||
// Left join only if this was not true, otherwise this left join already happened
|
->leftJoin('s.visits', 'v')
|
||||||
$this->leftJoinShortUrlsWithVisits($qb);
|
->groupBy('s')
|
||||||
}
|
->orderBy('COUNT(DISTINCT v)', $order);
|
||||||
|
|
||||||
$qb->orderBy('COUNT(DISTINCT v)', $order);
|
|
||||||
} elseif (contains(['longUrl', 'shortCode', 'dateCreated', 'title'], $fieldName)) {
|
} elseif (contains(['longUrl', 'shortCode', 'dateCreated', 'title'], $fieldName)) {
|
||||||
$qb->orderBy('s.' . $fieldName, $order);
|
$qb->orderBy('s.' . $fieldName, $order);
|
||||||
} else {
|
} else {
|
||||||
|
@ -80,18 +74,8 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||||
{
|
{
|
||||||
$qb = $this->createListQueryBuilder($filtering);
|
$qb = $this->createListQueryBuilder($filtering);
|
||||||
$qb->select('COUNT(DISTINCT s)');
|
$qb->select('COUNT(DISTINCT s)');
|
||||||
$query = $qb->getQuery();
|
|
||||||
|
|
||||||
// TODO This is crap...
|
return (int) $qb->getQuery()->getSingleScalarResult();
|
||||||
return $filtering->excludeMaxVisitsReached
|
|
||||||
? count($query->getSingleColumnResult())
|
|
||||||
: (int) $query->getSingleScalarResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function leftJoinShortUrlsWithVisits(QueryBuilder $qb): void
|
|
||||||
{
|
|
||||||
$qb->leftJoin('s.visits', 'v')
|
|
||||||
->groupBy('s');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createListQueryBuilder(ShortUrlsCountFiltering $filtering): QueryBuilder
|
private function createListQueryBuilder(ShortUrlsCountFiltering $filtering): QueryBuilder
|
||||||
|
@ -152,10 +136,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($filtering->excludeMaxVisitsReached) {
|
if ($filtering->excludeMaxVisitsReached) {
|
||||||
$this->leftJoinShortUrlsWithVisits($qb);
|
$visitEntity = Visit::class;
|
||||||
$qb->having($qb->expr()->orX(
|
$qb->andWhere($qb->expr()->orX(
|
||||||
$qb->expr()->isNull('s.maxVisits'),
|
$qb->expr()->isNull('s.maxVisits'),
|
||||||
$qb->expr()->gt('s.maxVisits', 'COUNT(DISTINCT v)'),
|
$qb->expr()->gt('s.maxVisits', "(SELECT COUNT(innerV.id) FROM $visitEntity as innerV WHERE innerV.shortUrl=s)"),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue