From 1138191342afdccfb02563dda6be863377dec369 Mon Sep 17 00:00:00 2001 From: Cyrille Bollu Date: Wed, 2 Oct 2019 16:34:16 +0200 Subject: [PATCH 1/8] FIX: Removes obsolete code that creates twin entries in composer's tribute component. Signed-off-by: Cyrille Bollu --- src/components/Composer.vue | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/components/Composer.vue b/src/components/Composer.vue index 1a4e680d..5f7ccd53 100644 --- a/src/components/Composer.vue +++ b/src/components/Composer.vue @@ -446,15 +446,6 @@ export default { cb(users) } this.remoteSearchAccounts(text).then((result) => { - if (result.data.result.exact) { - let user = result.data.result.exact - users.push({ - key: user.preferredUsername, - value: user.account, - url: user.url, - avatar: user.local ? OC.generateUrl(`/avatar/${user.preferredUsername}/32`) : ''// TODO: use real avatar from server - }) - } for (var i in result.data.result.accounts) { let user = result.data.result.accounts[i] users.push({ From d1f84b78e8de993c4424b7e8a13d07d1daf697fe Mon Sep 17 00:00:00 2001 From: Cyrille Bollu Date: Wed, 2 Oct 2019 17:06:02 +0200 Subject: [PATCH 2/8] FIX: Do not store current user in followers/following lists. Signed-off-by: Cyrille Bollu --- src/store/account.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/store/account.js b/src/store/account.js index e54d8c82..0c27f40e 100644 --- a/src/store/account.js +++ b/src/store/account.js @@ -45,11 +45,13 @@ const mutations = { let users = [] for (var index in data) { const actor = data[index].actor_info - users.push(actor.id) - addAccount(state, { - actorId: actor.id, - data: actor - }) + if (typeof actor !== 'undefined' && account !== actor.account) { + users.push(actor.id) + addAccount(state, { + actorId: actor.id, + data: actor + }) + } } Vue.set(state.accounts[_getActorIdForAccount(account)], 'followersList', users) }, @@ -57,7 +59,7 @@ const mutations = { let users = [] for (var index in data) { const actor = data[index].actor_info - if (typeof actor !== 'undefined') { + if (typeof actor !== 'undefined' && account !== actor.account) { users.push(actor.id) addAccount(state, { actorId: actor.id, From 729b10e5df9e01df009e1782726f1042a3aea6b9 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Wed, 2 Oct 2019 15:40:38 -0100 Subject: [PATCH 3/8] count only 'Follow' Signed-off-by: Maxence Lange --- lib/Db/FollowsRequest.php | 10 ++++++---- lib/Db/FollowsRequestBuilder.php | 6 +++--- lib/Service/AccountService.php | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/Db/FollowsRequest.php b/lib/Db/FollowsRequest.php index cf2b19d3..ec46401f 100644 --- a/lib/Db/FollowsRequest.php +++ b/lib/Db/FollowsRequest.php @@ -169,8 +169,9 @@ class FollowsRequest extends FollowsRequestBuilder { */ public function countFollowers(string $actorId): int { $qb = $this->countFollowsSelectSql(); - $this->limitToObjectId($qb, $actorId); - $this->limitToAccepted($qb, true); + $qb->limitToObjectId($actorId); + $qb->limitToType(Follow::TYPE); + $qb->limitToAccepted(true); $cursor = $qb->execute(); $data = $cursor->fetch(); @@ -187,8 +188,9 @@ class FollowsRequest extends FollowsRequestBuilder { */ public function countFollowing(string $actorId): int { $qb = $this->countFollowsSelectSql(); - $this->limitToActorId($qb, $actorId); - $this->limitToAccepted($qb, true); + $qb->limitToActorId($actorId); + $qb->limitToType(Follow::TYPE); + $qb->limitToAccepted(true); $cursor = $qb->execute(); $data = $cursor->fetch(); diff --git a/lib/Db/FollowsRequestBuilder.php b/lib/Db/FollowsRequestBuilder.php index a41e7114..550dd278 100644 --- a/lib/Db/FollowsRequestBuilder.php +++ b/lib/Db/FollowsRequestBuilder.php @@ -97,10 +97,10 @@ class FollowsRequestBuilder extends CoreRequestBuilder { /** * Base of the Sql Select request for Shares * - * @return IQueryBuilder + * @return SocialQueryBuilder */ - protected function countFollowsSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + protected function countFollowsSelectSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count') ->from(self::TABLE_FOLLOWS, 'f'); diff --git a/lib/Service/AccountService.php b/lib/Service/AccountService.php index 94b7bce0..70ce2714 100644 --- a/lib/Service/AccountService.php +++ b/lib/Service/AccountService.php @@ -284,8 +284,8 @@ class AccountService { */ public function addLocalActorDetailCount(Person &$actor) { $count = [ - 'followers' => $this->followsRequest->countFollowers($actor->getId()) - 1, - 'following' => $this->followsRequest->countFollowing($actor->getId()) - 1, + 'followers' => $this->followsRequest->countFollowers($actor->getId()), + 'following' => $this->followsRequest->countFollowing($actor->getId()), 'post' => $this->streamRequest->countNotesFromActorId($actor->getId()) ]; $actor->setDetailArray('count', $count); From 94d78d12a15e20d63476c738711b2da24f6d60f1 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 3 Oct 2019 02:46:22 +0000 Subject: [PATCH 4/8] [tx-robot] updated from transifex --- l10n/ca.js | 1 + l10n/ca.json | 1 + 2 files changed, 2 insertions(+) diff --git a/l10n/ca.js b/l10n/ca.js index 89f5131a..f46c3807 100644 --- a/l10n/ca.js +++ b/l10n/ca.js @@ -33,6 +33,7 @@ OC.L10N.register( "Post to followers only" : "Publica només pels seguidors", "Direct" : "Dirrecte", "Post to mentioned users only" : "Publica només als usuaris mencionats", + "Error while trying to post your message: Could not find any valid recipients." : "Error mentre s'intentava enviar el missatge. No s'ha pogut trobar cap destinatari vàlid.", "Unfollow" : "Deixa de seguir", "Follow" : "Segueix", "posts" : "publicacions", diff --git a/l10n/ca.json b/l10n/ca.json index eaa48b41..0d709ba7 100644 --- a/l10n/ca.json +++ b/l10n/ca.json @@ -31,6 +31,7 @@ "Post to followers only" : "Publica només pels seguidors", "Direct" : "Dirrecte", "Post to mentioned users only" : "Publica només als usuaris mencionats", + "Error while trying to post your message: Could not find any valid recipients." : "Error mentre s'intentava enviar el missatge. No s'ha pogut trobar cap destinatari vàlid.", "Unfollow" : "Deixa de seguir", "Follow" : "Segueix", "posts" : "publicacions", From 6ba6d86fa74f54b9264afe12dd26b54ecb355d38 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Tue, 1 Oct 2019 12:18:20 +0200 Subject: [PATCH 5/8] quick optimisation of the timeline tag Signed-off-by: Maxence Lange --- lib/Controller/LocalController.php | 2 +- lib/Db/StreamRequest.php | 27 ++++++++++++--------------- lib/Service/StreamService.php | 5 ++--- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/lib/Controller/LocalController.php b/lib/Controller/LocalController.php index 82c747f7..89976cd1 100644 --- a/lib/Controller/LocalController.php +++ b/lib/Controller/LocalController.php @@ -491,7 +491,7 @@ class LocalController extends Controller { public function streamTag(string $hashtag, int $since = 0, int $limit = 5): DataResponse { try { $this->initViewer(true); - $posts = $this->streamService->getStreamLocalTag($this->viewer, $hashtag, $since, $limit); + $posts = $this->streamService-> getStreamLocalTag($hashtag, $since, $limit); return $this->success($posts); } catch (Exception $e) { diff --git a/lib/Db/StreamRequest.php b/lib/Db/StreamRequest.php index a4d1ab0f..a5ea0dbc 100644 --- a/lib/Db/StreamRequest.php +++ b/lib/Db/StreamRequest.php @@ -369,7 +369,6 @@ class StreamRequest extends StreamRequestBuilder { $qb->andWhere($qb->exprLimitToDBField('type', SocialAppNotification::TYPE, false)); $qb->limitToViewer('sd', 'f', false); - $qb->andWhere($expr->eq('f.object_id_prim', 'ca.id_prim')); $qb->leftJoinStreamAction('sa'); $qb->filterDuplicate(); @@ -539,7 +538,6 @@ class StreamRequest extends StreamRequestBuilder { * - direct message related to a tag (not yet) * - message to followers related to a tag (not yet) * - * @param Person $actor * @param string $hashtag * @param int $since * @param int $limit @@ -547,23 +545,22 @@ class StreamRequest extends StreamRequestBuilder { * @return Stream[] * @throws DateTimeException */ - public function getTimelineTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5 - ): array { + public function getTimelineTag(string $hashtag, int $since = 0, int $limit = 5): array { $qb = $this->getStreamSelectSql(); - // TODO - rewrite the whole method ? - $on = $this->exprJoinFollowing($qb, $actor); - $on->add($this->exprLimitToRecipient($qb, ACore::CONTEXT_PUBLIC, false)); - $on->add($this->exprLimitToRecipient($qb, $actor->getId(), true)); - $qb->join($this->defaultSelectAlias, CoreRequestBuilder::TABLE_FOLLOWS, 'f', $on); - - $qb->andWhere($this->exprValueWithinJsonFormat($qb, 'hashtags', '' . $hashtag)); - + $expr = $qb->expr(); + $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); $qb->limitPaginate($since, $limit); -// $this->filterHiddenOnTimeline($qb); - $this->leftJoinCacheActors($qb, 'attributed_to'); - $this->leftJoinStreamAction($qb); + $qb->andWhere($qb->exprLimitToDBField('type', Note::TYPE)); + + $qb->limitToViewer('sd', 'f', true); + $qb->andWhere($expr->eq('s.attributed_to_prim', 'ca.id_prim')); + + $qb->leftJoinStreamAction('sa'); + + // TODO: Sql optimisation - Create a table like stream_dest for to link 'hashtag' to 'stream_id' + $qb->andWhere($this->exprValueWithinJsonFormat($qb, 'hashtags', '' . $hashtag)); return $this->getStreamsFromRequest($qb); } diff --git a/lib/Service/StreamService.php b/lib/Service/StreamService.php index 14a2c104..5dd5bdd6 100644 --- a/lib/Service/StreamService.php +++ b/lib/Service/StreamService.php @@ -478,9 +478,8 @@ class StreamService { * @return Note[] * @throws Exception */ - public function getStreamLocalTag(Person $actor, string $hashtag, int $since = 0, int $limit = 5 - ): array { - return $this->streamRequest->getTimelineTag($actor, $hashtag, $since, $limit); + public function getStreamLocalTag(string $hashtag, int $since = 0, int $limit = 5): array { + return $this->streamRequest->getTimelineTag($hashtag, $since, $limit); } From 1130d7ffbdd6d1a81051890586a8a8884321be03 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Tue, 1 Oct 2019 13:53:26 +0200 Subject: [PATCH 6/8] migrate data to a specific table Signed-off-by: Maxence Lange --- lib/Command/CacheRefresh.php | 1 - lib/Db/CoreRequestBuilder.php | 2 + lib/Db/SocialCrossQueryBuilder.php | 42 ++++- lib/Db/SocialLimitsQueryBuilder.php | 4 +- lib/Db/StreamDestRequest.php | 2 +- lib/Db/StreamDestRequestBuilder.php | 8 +- lib/Db/StreamRequest.php | 37 ++-- lib/Db/StreamTagsRequest.php | 78 ++++++++ lib/Db/StreamTagsRequestBuilder.php | 105 +++++++++++ .../Version0002Date20190916000001.php | 2 +- .../Version0002Date20190925000001.php | 2 +- .../Version0002Date20191001000001.php | 177 ++++++++++++++++++ 12 files changed, 427 insertions(+), 33 deletions(-) create mode 100644 lib/Db/StreamTagsRequest.php create mode 100644 lib/Db/StreamTagsRequestBuilder.php create mode 100644 lib/Migration/Version0002Date20191001000001.php diff --git a/lib/Command/CacheRefresh.php b/lib/Command/CacheRefresh.php index 71165df2..349edc4f 100644 --- a/lib/Command/CacheRefresh.php +++ b/lib/Command/CacheRefresh.php @@ -128,6 +128,5 @@ class CacheRefresh extends Base { $output->writeLn($result . ' hashtags updated'); } - } diff --git a/lib/Db/CoreRequestBuilder.php b/lib/Db/CoreRequestBuilder.php index 4ddd6ece..dc347c64 100644 --- a/lib/Db/CoreRequestBuilder.php +++ b/lib/Db/CoreRequestBuilder.php @@ -65,6 +65,7 @@ class CoreRequestBuilder { const TABLE_ACTORS = 'social_a2_actors'; const TABLE_STREAM = 'social_a2_stream'; const TABLE_STREAM_DEST = 'social_a2_stream_dest'; + const TABLE_STREAM_TAGS = 'social_a2_stream_tags'; const TABLE_STREAM_QUEUE = 'social_a2_stream_queue'; const TABLE_STREAM_ACTIONS = 'social_a2_stream_action'; @@ -87,6 +88,7 @@ class CoreRequestBuilder { self::TABLE_CACHE_DOCUMENTS, self::TABLE_STREAM_QUEUE, self::TABLE_STREAM_DEST, + self::TABLE_STREAM_TAGS, self::TABLE_STREAM_ACTIONS ]; diff --git a/lib/Db/SocialCrossQueryBuilder.php b/lib/Db/SocialCrossQueryBuilder.php index bfd99de7..4cd50c5d 100644 --- a/lib/Db/SocialCrossQueryBuilder.php +++ b/lib/Db/SocialCrossQueryBuilder.php @@ -66,7 +66,24 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder { * @param string $alias * @param string $link */ - public function innerJoinCacheActors(string $alias = 'ca', string $link = '') { + public function linkToStreamTags(string $alias = 'st', string $link = '') { + if ($this->getType() !== QueryBuilder::SELECT) { + return; + } + + $this->from(CoreRequestBuilder::TABLE_STREAM_TAGS, $alias); + if ($link !== '') { + $expr = $this->expr(); + $this->andWhere($expr->eq($alias . '.stream_id', $link)); + } + } + + + /** + * @param string $alias + * @param string $link + */ + public function linkToCacheActors(string $alias = 'ca', string $link = '') { if ($this->getType() !== QueryBuilder::SELECT) { return; } @@ -173,15 +190,14 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder { /** * @param string $type - * @param string $subType * @param string $field * @param string $aliasDest * @param string $alias */ - public function innerJoinDest( + public function innerJoinSteamDest( string $type, string $field = 'id_prim', string $aliasDest = 'sd', string $alias = '' ) { - $this->andWhere($this->exprInnerJoinDest($type, $field, $aliasDest, $alias)); + $this->andWhere($this->exprInnerJoinStreamDest($type, $field, $aliasDest, $alias)); } @@ -194,7 +210,7 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder { * * @return ICompositeExpression */ - public function exprInnerJoinDest( + public function exprInnerJoinStreamDest( string $type, string $field = 'id_prim', string $aliasDest = 'sd', string $alias = '' ): ICompositeExpression { $expr = $this->expr(); @@ -207,12 +223,22 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder { } - public function innerJoinDestFollowing( + /** + * @param string $actorId + * @param string $type + * @param string $field + * @param string $aliasDest + * @param string $aliasFollowing + * @param string $alias + */ + public function innerJoinStreamDestFollowing( string $actorId, string $type, string $field = 'id_prim', string $aliasDest = 'sd', string $aliasFollowing = 'f', string $alias = '' ) { $this->andWhere( - $this->exprInnerJoinDestFollowing($actorId, $type, $field, $aliasDest, $aliasFollowing, $alias) + $this->exprInnerJoinStreamDestFollowing( + $actorId, $type, $field, $aliasDest, $aliasFollowing, $alias + ) ); } @@ -227,7 +253,7 @@ class SocialCrossQueryBuilder extends SocialCoreQueryBuilder { * * @return ICompositeExpression */ - public function exprInnerJoinDestFollowing( + public function exprInnerJoinStreamDestFollowing( string $actorId, string $type, string $field = 'id_prim', string $aliasDest = 'sd', string $aliasFollowing = 'f', string $alias = '' ): ICompositeExpression { diff --git a/lib/Db/SocialLimitsQueryBuilder.php b/lib/Db/SocialLimitsQueryBuilder.php index f4c21bb5..e3cb83ae 100644 --- a/lib/Db/SocialLimitsQueryBuilder.php +++ b/lib/Db/SocialLimitsQueryBuilder.php @@ -365,7 +365,7 @@ class SocialLimitsQueryBuilder extends SocialCrossQueryBuilder { ) { if (!$this->hasViewer()) { $this->selectDestFollowing($aliasDest); - $this->innerJoinDest('recipient', 'id_prim', 'sd', 's'); + $this->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); $this->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', '', $aliasDest); return; @@ -376,7 +376,7 @@ class SocialLimitsQueryBuilder extends SocialCrossQueryBuilder { $orX = $expr->orX(); $actor = $this->getViewer(); - $following = $this->exprInnerJoinDestFollowing( + $following = $this->exprInnerJoinStreamDestFollowing( $actor->getId(), 'recipient', 'id_prim', $aliasDest, $aliasFollowing ); $orX->add($following); diff --git a/lib/Db/StreamDestRequest.php b/lib/Db/StreamDestRequest.php index 8ea6908d..78194f0d 100644 --- a/lib/Db/StreamDestRequest.php +++ b/lib/Db/StreamDestRequest.php @@ -87,7 +87,7 @@ class StreamDestRequest extends StreamDestRequestBuilder { $qb->execute(); } catch (UniqueConstraintViolationException $e) { \OC::$server->getLogger() - ->log(3, 'Social - Duplicate recipient on Stream ' . json_encode($stream)); + ->log(1, 'Social - Duplicate recipient on Stream ' . json_encode($stream)); } } } diff --git a/lib/Db/StreamDestRequestBuilder.php b/lib/Db/StreamDestRequestBuilder.php index 9bb60738..986e0215 100644 --- a/lib/Db/StreamDestRequestBuilder.php +++ b/lib/Db/StreamDestRequestBuilder.php @@ -64,7 +64,7 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { * @return IQueryBuilder */ protected function getStreamDestUpdateSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->update(self::TABLE_STREAM_DEST); return $qb; @@ -77,7 +77,7 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { * @return IQueryBuilder */ protected function getStreamDestSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); /** @noinspection PhpMethodParametersCountMismatchInspection */ $qb->select('sd.actor_id', 'sd.stream_id', 'sd.type') @@ -95,7 +95,7 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { * @return IQueryBuilder */ protected function getStreamDestDeleteSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->delete(self::TABLE_STREAM_DEST); return $qb; @@ -108,7 +108,7 @@ class StreamDestRequestBuilder extends CoreRequestBuilder { * @return IQueryBuilder */ protected function countStreamDestSelectSql(): IQueryBuilder { - $qb = $this->dbConnection->getQueryBuilder(); + $qb = $this->getQueryBuilder(); $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count') ->from(self::TABLE_STREAM_DEST, 'sd'); diff --git a/lib/Db/StreamRequest.php b/lib/Db/StreamRequest.php index a5ea0dbc..c321f2a5 100644 --- a/lib/Db/StreamRequest.php +++ b/lib/Db/StreamRequest.php @@ -62,6 +62,9 @@ class StreamRequest extends StreamRequestBuilder { /** @var StreamDestRequest */ private $streamDestRequest; + /** @var StreamTagsRequest */ + private $streamTagsRequest; + /** * StreamRequest constructor. @@ -74,11 +77,12 @@ class StreamRequest extends StreamRequestBuilder { */ public function __construct( IDBConnection $connection, ILogger $logger, StreamDestRequest $streamDestRequest, - ConfigService $configService, MiscService $miscService + StreamTagsRequest $streamTagsRequest, ConfigService $configService, MiscService $miscService ) { parent::__construct($connection, $logger, $configService, $miscService); $this->streamDestRequest = $streamDestRequest; + $this->streamTagsRequest = $streamTagsRequest; } @@ -101,6 +105,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->execute(); $this->streamDestRequest->generateStreamDest($stream); + $this->streamTagsRequest->generateStreamTags($stream); } catch (UniqueConstraintViolationException $e) { } } @@ -231,7 +236,7 @@ class StreamRequest extends StreamRequestBuilder { $expr = $qb->expr(); $qb->limitToIdPrim($qb->prim($id)); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); if ($asViewer) { $qb->limitToViewer('sd', 'f', true); @@ -271,7 +276,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitPaginate($since, $limit); $expr = $qb->expr(); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); $qb->andWhere($expr->eq('s.attributed_to', 'ca.id_prim')); @@ -337,7 +342,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitToType(Note::TYPE); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', '', 'sd'); $cursor = $qb->execute(); @@ -364,7 +369,7 @@ class StreamRequest extends StreamRequestBuilder { $qb = $this->getStreamSelectSql(); $expr = $qb->expr(); - $qb->innerJoinCacheActors('ca', 'f.object_id_prim'); + $qb->linkToCacheActors('ca', 'f.object_id_prim'); $qb->limitPaginate($since, $limit); $qb->andWhere($qb->exprLimitToDBField('type', SocialAppNotification::TYPE, false)); @@ -401,7 +406,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitToDest($actor->getId(), 'recipient', '', 'sd'); $qb->limitToType(SocialAppNotification::TYPE); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); $qb->leftJoinStreamAction(); return $this->getStreamsFromRequest($qb); @@ -427,10 +432,10 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitToAttributedTo($actorId); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', '', 'sd'); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); $qb->leftJoinStreamAction(); return $this->getStreamsFromRequest($qb); @@ -455,10 +460,10 @@ class StreamRequest extends StreamRequestBuilder { $qb->filterType(SocialAppNotification::TYPE); $qb->limitPaginate($since, $limit); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest($actor->getId(), 'recipient', '', 'sd'); $qb->filterDest(ACore::CONTEXT_PUBLIC); @@ -488,11 +493,11 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitToLocal($localOnly); $qb->limitToType(Note::TYPE); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); $qb->leftJoinStreamAction(); $qb->selectDestFollowing('sd', ''); - $qb->innerJoinDest('recipient', 'id_prim', 'sd', 's'); + $qb->innerJoinSteamDest('recipient', 'id_prim', 'sd', 's'); $qb->limitToDest(ACore::CONTEXT_PUBLIC, 'recipient', 'to', 'sd'); return $this->getStreamsFromRequest($qb); @@ -521,7 +526,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->limitPaginate($since, $limit); $expr = $qb->expr(); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); $qb->selectStreamActions('sa'); $qb->andWhere($expr->eq('sa.stream_id_prim', 's.id_prim')); @@ -549,10 +554,12 @@ class StreamRequest extends StreamRequestBuilder { $qb = $this->getStreamSelectSql(); $expr = $qb->expr(); - $qb->innerJoinCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToCacheActors('ca', 's.attributed_to_prim'); + $qb->linkToStreamTags('st', 's.id_prim'); $qb->limitPaginate($since, $limit); $qb->andWhere($qb->exprLimitToDBField('type', Note::TYPE)); + $qb->andWhere($qb->exprLimitToDBField('hashtag', $hashtag, true, false, 'st')); $qb->limitToViewer('sd', 'f', true); $qb->andWhere($expr->eq('s.attributed_to_prim', 'ca.id_prim')); @@ -560,7 +567,7 @@ class StreamRequest extends StreamRequestBuilder { $qb->leftJoinStreamAction('sa'); // TODO: Sql optimisation - Create a table like stream_dest for to link 'hashtag' to 'stream_id' - $qb->andWhere($this->exprValueWithinJsonFormat($qb, 'hashtags', '' . $hashtag)); +// $qb->andWhere($this->exprValueWithinJsonFormat($qb, 'hashtags', '' . $hashtag)); return $this->getStreamsFromRequest($qb); } diff --git a/lib/Db/StreamTagsRequest.php b/lib/Db/StreamTagsRequest.php new file mode 100644 index 00000000..4b80d34a --- /dev/null +++ b/lib/Db/StreamTagsRequest.php @@ -0,0 +1,78 @@ + + * @copyright 2018, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Social\Db; + + +use daita\MySmallPhpTools\Traits\TStringTools; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use OCA\Social\Model\ActivityPub\Stream; + + +/** + * Class StreamTagsRequest + * + * @package OCA\Social\Db + */ +class StreamTagsRequest extends StreamTagsRequestBuilder { + + + use TStringTools; + + + /** + * @param Stream $stream + */ + public function generateStreamTags(Stream $stream) { + $hashtags = $stream->getTags(); + + $this->miscService->log('$$$$ ' . json_encode($hashtags)); + foreach ($hashtags as $hashtag) { + $tag = $this->get('name', $hashtag); + if ($this->get('type', $hashtag) !== 'Hashtag' || $tag === '') { + continue; + } + + $tag = substr($tag, 1); + $qb = $this->getStreamTagsInsertSql(); + $streamId = $qb->prim($stream->getId()); + $qb->setValue('stream_id', $qb->createNamedParameter($streamId)); + $qb->setValue('hashtag', $qb->createNamedParameter($tag)); + try { + $qb->execute(); + } catch (UniqueConstraintViolationException $e) { + \OC::$server->getLogger() + ->log(1, 'Social - Duplicate hashtag on Stream ' . json_encode($stream)); + } + } + } + +} + diff --git a/lib/Db/StreamTagsRequestBuilder.php b/lib/Db/StreamTagsRequestBuilder.php new file mode 100644 index 00000000..25fe9f67 --- /dev/null +++ b/lib/Db/StreamTagsRequestBuilder.php @@ -0,0 +1,105 @@ + + * @copyright 2018, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Social\Db; + + +use daita\MySmallPhpTools\Traits\TArrayTools; +use OCP\DB\QueryBuilder\IQueryBuilder; + + +/** + * Class StreamDestRequestBuilder + * + * @package OCA\Social\Db + */ +class StreamTagsRequestBuilder extends CoreRequestBuilder { + + + use TArrayTools; + + + /** + * Base of the Sql Insert request + * + * @return SocialQueryBuilder + */ + protected function getStreamTagsInsertSql(): SocialQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->insert(self::TABLE_STREAM_TAGS); + + return $qb; + } + + + /** + * Base of the Sql Update request + * + * @return IQueryBuilder + */ + protected function getStreamTagsUpdateSql(): IQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->update(self::TABLE_STREAM_TAGS); + + return $qb; + } + + + /** + * Base of the Sql Select request for Shares + * + * @return IQueryBuilder + */ + protected function getStreamTagsSelectSql(): IQueryBuilder { + $qb = $this->getQueryBuilder(); + + /** @noinspection PhpMethodParametersCountMismatchInspection */ + $qb->select('st.stream_id', 'st.hashtag') + ->from(self::TABLE_STREAM_TAGS, 'st'); + + $this->defaultSelectAlias = 'st'; + + return $qb; + } + + + /** + * Base of the Sql Delete request + * + * @return IQueryBuilder + */ + protected function getStreamTagsDeleteSql(): IQueryBuilder { + $qb = $this->getQueryBuilder(); + $qb->delete(self::TABLE_STREAM_TAGS); + + return $qb; + } + +} + diff --git a/lib/Migration/Version0002Date20190916000001.php b/lib/Migration/Version0002Date20190916000001.php index 14894b6d..04bf2c5e 100644 --- a/lib/Migration/Version0002Date20190916000001.php +++ b/lib/Migration/Version0002Date20190916000001.php @@ -390,7 +390,7 @@ class Version0002Date20190916000001 extends SimpleMigrationStep { $insert->execute(); } catch (UniqueConstraintViolationException $e) { \OC::$server->getLogger() - ->log(3, 'Social - Duplicate recipient on Stream ' . json_encode($data)); + ->log(1, 'Social - Duplicate recipient on Stream ' . json_encode($data)); } } } diff --git a/lib/Migration/Version0002Date20190925000001.php b/lib/Migration/Version0002Date20190925000001.php index 50ea07bf..fc33b320 100644 --- a/lib/Migration/Version0002Date20190925000001.php +++ b/lib/Migration/Version0002Date20190925000001.php @@ -154,7 +154,7 @@ class Version0002Date20190925000001 extends SimpleMigrationStep { $update->set('stream_id_prim', $update->createNamedParameter(hash('sha512', $streamId))); $update->set('liked', $update->createNamedParameter($liked)); $update->set('boosted', $update->createNamedParameter($boosted)); - $update->setValue('replied', $update->createNamedParameter($replied)); + $update->set('replied', $update->createNamedParameter($replied)); $expr = $update->expr(); $update->where($expr->eq('id', $update->createNamedParameter($id))); diff --git a/lib/Migration/Version0002Date20191001000001.php b/lib/Migration/Version0002Date20191001000001.php new file mode 100644 index 00000000..86888a13 --- /dev/null +++ b/lib/Migration/Version0002Date20191001000001.php @@ -0,0 +1,177 @@ + + * @copyright 2018, Maxence Lange + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Social\Migration; + + +use Closure; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; +use Doctrine\DBAL\Schema\SchemaException; +use Exception; +use OCP\DB\ISchemaWrapper; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + + +/** + * Class Version0002Date20191001000001 + * + * @package OCA\Social\Migration + */ +class Version0002Date20191001000001 extends SimpleMigrationStep { + + + /** @var IDBConnection */ + private $connection; + + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @return ISchemaWrapper + * @throws SchemaException + */ + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options + ): ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable('social_a2_stream_tags')) { + $table = $schema->createTable('social_a2_stream_tags'); + + $table->addColumn( + 'stream_id', 'string', + [ + 'notnull' => true, + 'length' => 128, + ] + ); + $table->addColumn( + 'hashtag', 'string', + [ + 'notnull' => false, + 'length' => 127, + ] + ); + + if (!$table->hasIndex('sh')) { + $table->addUniqueIndex(['stream_id', 'hashtag'], 'sh'); + } + } + + return $schema; + } + + + /** + * @param IOutput $output + * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * + * @throws Exception + */ + public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $this->fillTableStreamHashtags($schema); + } + + /** + * @param ISchemaWrapper $schema + */ + private function fillTableStreamHashtags(ISchemaWrapper $schema) { + + $start = 0; + $limit = 1000; + while (true) { + $qb = $this->connection->getQueryBuilder(); + $qb->select('id', 'hashtags') + ->from('social_a2_stream') + ->setMaxResults(1000) + ->setFirstResult($start); + + $cursor = $qb->execute(); + $count = 0; + while ($data = $cursor->fetch()) { + $count++; + + $this->updateStreamTags($data); + } + $cursor->closeCursor(); + + $start += $count; + if ($count < $limit) { + break; + } + } + } + + + /** + * @param array $data + */ + private function updateStreamTags(array $data) { + if ($data['hashtags'] === '' || $data['hashtags'] === null) { + return; + } + + $id = $data['id']; + $tags = json_decode($data['hashtags'], true); + + foreach ($tags as $tag) { + if ($tag === '') { + continue; + } + $insert = $this->connection->getQueryBuilder(); + $insert->insert('social_a2_stream_tags'); + + $insert->setValue('stream_id', $insert->createNamedParameter(hash('sha512', $id))); + $insert->setValue('hashtag', $insert->createNamedParameter($tag)); + try { + $insert->execute(); + } catch (UniqueConstraintViolationException $e) { + } + } + + } + +} From 1b6b79927bdd89f14ad4e9c5fb857cd079e4dc56 Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Tue, 1 Oct 2019 14:15:47 +0200 Subject: [PATCH 7/8] remove old code Signed-off-by: Maxence Lange --- lib/Db/StreamRequest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Db/StreamRequest.php b/lib/Db/StreamRequest.php index c321f2a5..225d060e 100644 --- a/lib/Db/StreamRequest.php +++ b/lib/Db/StreamRequest.php @@ -566,9 +566,6 @@ class StreamRequest extends StreamRequestBuilder { $qb->leftJoinStreamAction('sa'); - // TODO: Sql optimisation - Create a table like stream_dest for to link 'hashtag' to 'stream_id' -// $qb->andWhere($this->exprValueWithinJsonFormat($qb, 'hashtags', '' . $hashtag)); - return $this->getStreamsFromRequest($qb); } From 34446aa1177989f1020a8bdec8e99678c88b044e Mon Sep 17 00:00:00 2001 From: Maxence Lange Date: Thu, 3 Oct 2019 11:13:39 -0100 Subject: [PATCH 8/8] composer Signed-off-by: Maxence Lange --- composer.lock | 20 ++++++++++---------- lib/Db/CacheActorsRequestBuilder.php | 1 - 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/composer.lock b/composer.lock index b93ca72a..0836bdf8 100644 --- a/composer.lock +++ b/composer.lock @@ -12,12 +12,12 @@ "source": { "type": "git", "url": "https://github.com/daita/my-small-php-tools.git", - "reference": "4f96fd4cf4d87cc79c79ea5af3d6a4f133a09e2e" + "reference": "2252b8e425e68331e70d4cfad412274af12e5c6f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/4f96fd4cf4d87cc79c79ea5af3d6a4f133a09e2e", - "reference": "4f96fd4cf4d87cc79c79ea5af3d6a4f133a09e2e", + "url": "https://api.github.com/repos/daita/my-small-php-tools/zipball/2252b8e425e68331e70d4cfad412274af12e5c6f", + "reference": "2252b8e425e68331e70d4cfad412274af12e5c6f", "shasum": "" }, "require": { @@ -40,7 +40,7 @@ } ], "description": "My small PHP Tools", - "time": "2019-09-16T10:53:15+00:00" + "time": "2019-10-03T11:59:56+00:00" }, { "name": "friendica/json-ld", @@ -544,22 +544,22 @@ }, { "name": "phpspec/prophecy", - "version": "1.8.1", + "version": "1.9.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76" + "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76", - "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203", + "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, @@ -603,7 +603,7 @@ "spy", "stub" ], - "time": "2019-06-13T12:50:23+00:00" + "time": "2019-10-03T11:07:50+00:00" }, { "name": "phpunit/php-code-coverage", diff --git a/lib/Db/CacheActorsRequestBuilder.php b/lib/Db/CacheActorsRequestBuilder.php index fe0e5313..07e6b9e7 100644 --- a/lib/Db/CacheActorsRequestBuilder.php +++ b/lib/Db/CacheActorsRequestBuilder.php @@ -123,6 +123,5 @@ class CacheActorsRequestBuilder extends CoreRequestBuilder { return $actor; } - }