mirror of
https://github.com/flarum/core.git
synced 2025-08-06 16:36:47 +02:00
Refactor sticky order clause into a subquery
Based on some limited testing, using a subquery seems to outperform a join in this case (the join was invoking a temporary table, which is always a bad sign). This also adds logic to fix a bug where sticky discussions would remain at the top even when marked as read using the "mark all as read" button. I thought we had an open issue for this somewhere, but I can't seem to find one. ref #988 #1003
This commit is contained in:
@@ -45,10 +45,17 @@ class PinStickiedDiscussionsToTop
|
|||||||
$search = $event->search;
|
$search = $event->search;
|
||||||
$query = $search->getQuery();
|
$query = $search->getQuery();
|
||||||
|
|
||||||
|
// TODO: ideally we might like to consider an event in core that is
|
||||||
|
// fired before the sort criteria is applied to the query (ie.
|
||||||
|
// immediately after gambits are applied) so that we can add the
|
||||||
|
// following order logic to the start without using array_unshift.
|
||||||
|
|
||||||
if (! is_array($query->orders)) {
|
if (! is_array($query->orders)) {
|
||||||
$query->orders = [];
|
$query->orders = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we are viewing a specific tag, then pin all stickied
|
||||||
|
// discussions to the top no matter what.
|
||||||
foreach ($search->getActiveGambits() as $gambit) {
|
foreach ($search->getActiveGambits() as $gambit) {
|
||||||
if ($gambit instanceof TagGambit) {
|
if ($gambit instanceof TagGambit) {
|
||||||
array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']);
|
array_unshift($query->orders, ['column' => 'is_sticky', 'direction' => 'desc']);
|
||||||
@@ -57,21 +64,28 @@ class PinStickiedDiscussionsToTop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$query->leftJoin('users_discussions', function ($join) use ($search) {
|
// Otherwise, if we are viewing "all discussions" or similar, only
|
||||||
$join->on('users_discussions.discussion_id', '=', 'discussions.id')
|
// pin stickied discussions to the top if they are unread. To do
|
||||||
->where('discussions.is_sticky', '=', true)
|
// this we construct an order clause containing a subquery which
|
||||||
->where('users_discussions.user_id', '=', $search->getActor()->id);
|
// determines whether or not the discussion is unread.
|
||||||
});
|
$subquery = $query->newQuery()
|
||||||
|
->selectRaw(1)
|
||||||
// TODO: Might be quicker to do a subquery in the order clause than a join?
|
->from('users_discussions as sticky')
|
||||||
$grammar = $query->getGrammar();
|
->whereRaw('sticky.discussion_id = discussions.id')
|
||||||
$readNumber = $grammar->wrap('users_discussions.read_number');
|
->where('sticky.user_id', '=', $search->getActor()->id)
|
||||||
$lastPostNumber = $grammar->wrap('discussions.last_post_number');
|
->where(function ($query) {
|
||||||
|
$query->whereNull('sticky.read_number')
|
||||||
|
->orWhereRaw('discussions.last_post_number > sticky.read_number');
|
||||||
|
})
|
||||||
|
->where('discussions.last_time', '>', $search->getActor()->read_time ?: 0);
|
||||||
|
|
||||||
array_unshift($query->orders, [
|
array_unshift($query->orders, [
|
||||||
'type' => 'raw',
|
'type' => 'raw',
|
||||||
'sql' => "(is_sticky AND ($readNumber IS NULL OR $lastPostNumber > $readNumber)) desc"
|
'sql' => "(is_sticky and exists ({$subquery->toSql()})) desc"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$orderBindings = $query->getRawBindings()['order'];
|
||||||
|
$query->setBindings(array_merge($subquery->getBindings(), $orderBindings), 'order');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user