Improve the performance of API timeline calls

This commit is contained in:
Michael 2023-10-18 08:11:36 +00:00
parent 2cd18fd4bf
commit 1c67be8db0
8 changed files with 147 additions and 26 deletions

View File

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2023.09-rc (Giant Rhubarb) -- Friendica 2023.09-rc (Giant Rhubarb)
-- DB_UPDATE_VERSION 1537 -- DB_UPDATE_VERSION 1538
-- ------------------------------------------ -- ------------------------------------------
@ -1991,6 +1991,51 @@ CREATE VIEW `circle-member-view` AS SELECT
INNER JOIN `contact` ON `group_member`.`contact-id` = `contact`.`id` INNER JOIN `contact` ON `group_member`.`contact-id` = `contact`.`id`
INNER JOIN `group` ON `group_member`.`gid` = `group`.`id`; INNER JOIN `group` ON `group_member`.`gid` = `group`.`id`;
--
-- VIEW post-timeline-view
--
DROP VIEW IF EXISTS `post-timeline-view`;
CREATE VIEW `post-timeline-view` AS SELECT
`post-user`.`uid` AS `uid`,
`post-user`.`uri-id` AS `uri-id`,
`post-user`.`gravity` AS `gravity`,
`post-user`.`created` AS `created`,
`post-user`.`edited` AS `edited`,
`post-thread-user`.`commented` AS `commented`,
`post-user`.`received` AS `received`,
`post-thread-user`.`changed` AS `changed`,
`post-user`.`private` AS `private`,
`post-user`.`visible` AS `visible`,
`post-user`.`deleted` AS `deleted`,
`post-user`.`origin` AS `origin`,
`post-user`.`global` AS `global`,
`post-user`.`network` AS `network`,
`post-user`.`protocol` AS `protocol`,
`post-user`.`vid` AS `vid`,
`post-user`.`contact-id` AS `contact-id`,
`contact`.`blocked` AS `contact-blocked`,
`contact`.`readonly` AS `contact-readonly`,
`contact`.`pending` AS `contact-pending`,
`contact`.`rel` AS `contact-rel`,
`contact`.`uid` AS `contact-uid`,
`contact`.`self` AS `self`,
`post-user`.`author-id` AS `author-id`,
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`gsid` AS `author-gsid`,
`post-user`.`owner-id` AS `owner-id`,
`owner`.`blocked` AS `owner-blocked`,
`owner`.`gsid` AS `owner-gsid`,
`post-user`.`causer-id` AS `causer-id`,
`causer`.`blocked` AS `causer-blocked`,
`causer`.`gsid` AS `causer-gsid`
FROM `post-user`
LEFT JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-user`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-user`.`uid`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-user`.`contact-id`
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `post-user`.`author-id`
STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = `post-user`.`owner-id`
LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `post-user`.`causer-id`;
-- --
-- VIEW post-user-view -- VIEW post-user-view
-- --

View File

@ -497,6 +497,23 @@ class Post
return self::selectViewForUser('post-view', $uid, $selected, $condition, $params); return self::selectViewForUser('post-view', $uid, $selected, $condition, $params);
} }
/**
* Select rows from the post-timeline-view view for a given user
* This function is used for API calls.
*
* @param integer $uid User ID
* @param array $selected Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
*
* @return boolean|object
* @throws \Exception
*/
public static function selectTimelineForUser(int $uid, array $selected = [], array $condition = [], array $params = [])
{
return self::selectViewForUser('post-timeline-view', $uid, $selected, $condition, $params);
}
/** /**
* Select rows from the post-thread-user-view view for a given user * Select rows from the post-thread-user-view view for a given user
* *

View File

@ -74,8 +74,10 @@ class Statuses extends BaseApi
} elseif ($request['only_media']) { } elseif ($request['only_media']) {
$condition = ['author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED], 'type' => [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]]; $condition = ['author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED], 'type' => [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]];
} elseif (!$uid) { } elseif (!$uid) {
$condition = ['author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED], $condition = [
'uid' => 0, 'network' => Protocol::FEDERATED]; 'author-id' => $id, 'private' => [Item::PUBLIC, Item::UNLISTED],
'uid' => 0, 'network' => Protocol::FEDERATED
];
} else { } else {
$condition = ["`author-id` = ? AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))", $id, $uid]; $condition = ["`author-id` = ? AND (`uid` = 0 OR (`uid` = ? AND NOT `global`))", $id, $uid];
} }
@ -85,11 +87,15 @@ class Statuses extends BaseApi
if (!$request['pinned'] && !$request['only_media']) { if (!$request['pinned'] && !$request['only_media']) {
if ($request['exclude_replies']) { if ($request['exclude_replies']) {
$condition = DBA::mergeConditions($condition, ["(`gravity` = ? OR (`gravity` = ? AND `vid` = ? AND `protocol` != ?))", $condition = DBA::mergeConditions($condition, [
Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA]); "(`gravity` = ? OR (`gravity` = ? AND `vid` = ? AND `protocol` != ?))",
Item::GRAVITY_PARENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA
]);
} else { } else {
$condition = DBA::mergeConditions($condition, ["(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ? AND `protocol` != ?))", $condition = DBA::mergeConditions($condition, [
Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA]); "(`gravity` IN (?, ?) OR (`gravity` = ? AND `vid` = ? AND `protocol` != ?))",
Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA
]);
} }
} elseif ($request['exclude_replies']) { } elseif ($request['exclude_replies']) {
$condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]); $condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
@ -100,7 +106,7 @@ class Statuses extends BaseApi
} elseif ($request['only_media']) { } elseif ($request['only_media']) {
$items = DBA::select('media-view', ['uri-id'], $condition, $params); $items = DBA::select('media-view', ['uri-id'], $condition, $params);
} else { } else {
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params); $items = Post::selectTimelineForUser($uid, ['uri-id'], $condition, $params);
} }
$display_quotes = self::appSupportsQuotes(); $display_quotes = self::appSupportsQuotes();

View File

@ -22,7 +22,6 @@
namespace Friendica\Module\Api\Mastodon\Timelines; namespace Friendica\Module\Api\Mastodon\Timelines;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Item; use Friendica\Model\Item;
@ -67,8 +66,10 @@ class Home extends BaseApi
} }
if ($request['only_media']) { if ($request['only_media']) {
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))", $condition = DBA::mergeConditions($condition, [
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]); "`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO
]);
} }
if ($request['remote']) { if ($request['remote']) {
@ -79,7 +80,7 @@ class Home extends BaseApi
$condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]); $condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
} }
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params); $items = Post::selectTimelineForUser($uid, ['uri-id'], $condition, $params);
$display_quotes = self::appSupportsQuotes(); $display_quotes = self::appSupportsQuotes();

View File

@ -61,15 +61,19 @@ class ListTimeline extends BaseApi
'friendica_order' => TimelineOrderByTypes::ID, // Sort order options (defaults to ID) 'friendica_order' => TimelineOrderByTypes::ID, // Sort order options (defaults to ID)
], $request); ], $request);
$condition = ["`uid` = ? AND `gravity` IN (?, ?) AND `contact-id` IN (SELECT `contact-id` FROM `group_member` WHERE `gid` = ?)", $condition = [
$uid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, $this->parameters['id']]; "`uid` = ? AND `gravity` IN (?, ?) AND `contact-id` IN (SELECT `contact-id` FROM `group_member` WHERE `gid` = ?)",
$uid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, $this->parameters['id']
];
$condition = $this->addPagingConditions($request, $condition); $condition = $this->addPagingConditions($request, $condition);
$params = $this->buildOrderAndLimitParams($request); $params = $this->buildOrderAndLimitParams($request);
if ($request['only_media']) { if ($request['only_media']) {
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))", $condition = DBA::mergeConditions($condition, [
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]); "`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO
]);
} }
if ($request['exclude_replies']) { if ($request['exclude_replies']) {
@ -84,7 +88,7 @@ class ListTimeline extends BaseApi
$condition = DBA::mergeConditions($condition, ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin` AND `post-user`.`uri-id` = `post-user-view`.`uri-id`)"]); $condition = DBA::mergeConditions($condition, ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin` AND `post-user`.`uri-id` = `post-user-view`.`uri-id`)"]);
} }
$items = Post::selectForUser($uid, ['uri-id'], $condition, $params); $items = Post::selectTimelineForUser($uid, ['uri-id'], $condition, $params);
$display_quotes = self::appSupportsQuotes(); $display_quotes = self::appSupportsQuotes();

View File

@ -23,7 +23,6 @@ namespace Friendica\Module\Api\Mastodon\Timelines;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Item; use Friendica\Model\Item;
@ -57,30 +56,36 @@ class PublicTimeline extends BaseApi
'friendica_order' => TimelineOrderByTypes::ID, // Sort order options (defaults to ID) 'friendica_order' => TimelineOrderByTypes::ID, // Sort order options (defaults to ID)
], $request); ], $request);
$condition = ['gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT], 'private' => Item::PUBLIC, $condition = [
'network' => Protocol::FEDERATED, 'author-blocked' => false, 'author-hidden' => false]; 'gravity' => [Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT], 'private' => Item::PUBLIC,
'network' => Protocol::FEDERATED, 'author-blocked' => false, 'author-hidden' => false
];
$condition = $this->addPagingConditions($request, $condition); $condition = $this->addPagingConditions($request, $condition);
$params = $this->buildOrderAndLimitParams($request); $params = $this->buildOrderAndLimitParams($request);
if ($request['local']) { if ($request['local']) {
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)"]); $condition = DBA::mergeConditions($condition, ['origin' => true]);
} else {
$condition = DBA::mergeConditions($condition, ['uid' => 0]);
} }
if ($request['remote']) { if ($request['remote']) {
$condition = DBA::mergeConditions($condition, ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin` AND `post-user`.`uri-id` = `post-view`.`uri-id`)"]); $condition = DBA::mergeConditions($condition, ["NOT `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin` AND `post-user`.`uri-id` = `post-timeline-view`.`uri-id`)"]);
} }
if ($request['only_media']) { if ($request['only_media']) {
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))", $condition = DBA::mergeConditions($condition, [
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]); "`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO
]);
} }
if ($request['exclude_replies']) { if ($request['exclude_replies']) {
$condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]); $condition = DBA::mergeConditions($condition, ['gravity' => Item::GRAVITY_PARENT]);
} }
$items = Post::selectPostsForUser($uid, ['uri-id'], $condition, $params); $items = Post::selectTimelineForUser($uid, ['uri-id'], $condition, $params);
$display_quotes = self::appSupportsQuotes(); $display_quotes = self::appSupportsQuotes();

View File

@ -56,7 +56,7 @@ use Friendica\Database\DBA;
// This file is required several times during the test in DbaDefinition which justifies this condition // This file is required several times during the test in DbaDefinition which justifies this condition
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1537); define('DB_UPDATE_VERSION', 1538);
} }
return [ return [

View File

@ -87,6 +87,49 @@
INNER JOIN `contact` ON `group_member`.`contact-id` = `contact`.`id` INNER JOIN `contact` ON `group_member`.`contact-id` = `contact`.`id`
INNER JOIN `group` ON `group_member`.`gid` = `group`.`id`" INNER JOIN `group` ON `group_member`.`gid` = `group`.`id`"
], ],
"post-timeline-view" => [
"fields" => [
"uid" => ["post-user", "uid"],
"uri-id" => ["post-user", "uri-id"],
"gravity" => ["post-user", "gravity"],
"created" => ["post-user", "created"],
"edited" => ["post-user", "edited"],
"commented" => ["post-thread-user", "commented"],
"received" => ["post-user", "received"],
"changed" => ["post-thread-user", "changed"],
"private" => ["post-user", "private"],
"visible" => ["post-user", "visible"],
"deleted" => ["post-user", "deleted"],
"origin" => ["post-user", "origin"],
"global" => ["post-user", "global"],
"network" => ["post-user", "network"],
"protocol" => ["post-user", "protocol"],
"vid" => ["post-user", "vid"],
"contact-id" => ["post-user", "contact-id"],
"contact-blocked" => ["contact", "blocked"],
"contact-readonly" => ["contact", "readonly"],
"contact-pending" => ["contact", "pending"],
"contact-rel" => ["contact", "rel"],
"contact-uid" => ["contact", "uid"],
"self" => ["contact", "self"],
"author-id" => ["post-user", "author-id"],
"author-blocked" => ["author", "blocked"],
"author-hidden" => ["author", "hidden"],
"author-gsid" => ["author", "gsid"],
"owner-id" => ["post-user", "owner-id"],
"owner-blocked" => ["owner", "blocked"],
"owner-gsid" => ["owner", "gsid"],
"causer-id" => ["post-user", "causer-id"],
"causer-blocked" => ["causer", "blocked"],
"causer-gsid" => ["causer", "gsid"],
],
"query" => "FROM `post-user`
LEFT JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-user`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-user`.`uid`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-user`.`contact-id`
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `post-user`.`author-id`
STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = `post-user`.`owner-id`
LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `post-user`.`causer-id`"
],
"post-user-view" => [ "post-user-view" => [
"fields" => [ "fields" => [
"id" => ["post-user", "id"], "id" => ["post-user", "id"],