Avoid ordering just by a timestamp

Try to also employ an id when possible.
Involves reworking some of the indices.
This commit is contained in:
Alexei Sorokin 2020-09-15 16:59:27 +03:00 committed by Diogo Peralta Cordeiro
parent ae4f3176b1
commit a0f72fe5c6
41 changed files with 633 additions and 656 deletions

View File

@ -122,20 +122,19 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction
{
$group = new User_group();
$offset = intval($this->page - 1) * intval($this->count);
$limit = intval($this->count);
$group->selectAdd();
$group->selectAdd('user_group.*');
$group->joinAdd(['id', 'local_group:group_id']);
$group->orderBy('user_group.created DESC, user_group.id DESC');
$group->query(
'SELECT user_group.* '.
'FROM user_group INNER JOIN local_group ' .
'ON user_group.id = local_group.group_id '.
'ORDER BY created DESC ' .
'LIMIT ' . $limit . ' OFFSET ' . $offset
);
$offset = ((int) $this->page - 1) * (int) $this->count;
$group->limit($offset, $this->count);
$groups = array();
while ($group->fetch()) {
$groups[] = clone($group);
$groups = [];
if ($group->find()) {
while ($group->fetch()) {
$groups[] = clone $group;
}
}
return $groups;

View File

@ -1,36 +1,31 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* StatusNet, the distributed open-source microblogging tool
*
* Latest groups information
*
* PHP version 5
*
* LICENCE: 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 <http://www.gnu.org/licenses/>.
*
* @category Personal
* @package StatusNet
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @author Sarven Capadisli <csarven@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
defined('GNUSOCIAL') || die();
require_once INSTALLDIR . '/lib/groups/grouplist.php';
@ -39,48 +34,48 @@ require_once INSTALLDIR . '/lib/groups/grouplist.php';
*
* Show the latest groups on the site
*
* @category Personal
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @category Personal
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class GroupsAction extends Action
{
var $page = null;
var $profile = null;
public $page = null;
public $profile = null;
function isReadOnly($args)
public function isReadOnly($args)
{
return true;
}
function title()
public function title()
{
if ($this->page == 1) {
// TRANS: Title for first page of the groups list.
return _m('TITLE',"Groups");
return _m('TITLE', 'Groups');
} else {
// TRANS: Title for all but the first page of the groups list.
// TRANS: %d is the page number.
return sprintf(_m('TITLE',"Groups, page %d"), $this->page);
return sprintf(_m('TITLE', 'Groups, page %d'), $this->page);
}
}
function prepare(array $args = array())
public function prepare(array $args = [])
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
return true;
}
function handle()
public function handle()
{
parent::handle();
$this->showPage();
}
function showPageNotice()
public function showPageNotice()
{
$notice =
// TRANS: Page notice of group list. %%%%site.name%%%% is the StatusNet site name,
@ -97,39 +92,45 @@ class GroupsAction extends Action
$this->elementEnd('div');
}
function showContent()
public function showContent()
{
if (common_logged_in()) {
$this->elementStart('p', array('id' => 'new_group'));
$this->element('a', array('href' => common_local_url('newgroup'),
'class' => 'more'),
// TRANS: Link to create a new group on the group list page.
_('Create a new group'));
$this->elementStart('p', ['id' => 'new_group']);
$this->element(
'a',
[
'href' => common_local_url('newgroup'),
'class' => 'more',
],
// TRANS: Link to create a new group on the group list page.
_('Create a new group')
);
$this->elementEnd('p');
}
$offset = ($this->page-1) * GROUPS_PER_PAGE;
$limit = GROUPS_PER_PAGE + 1;
$qry = 'SELECT user_group.* '.
'from user_group join local_group on user_group.id = local_group.group_id '.
'order by user_group.created desc '.
'limit ' . $limit . ' offset ' . $offset;
$groups = new User_group();
$cnt = 0;
$groups->query($qry);
$groups->selectAdd();
$groups->selectAdd('user_group.*');
$groups->joinAdd(['id', 'local_group:group_id']);
$groups->orderBy('user_group.created DESC, user_group.id DESC');
$groups->limit($offset, $limit);
$groups->find();
$gl = new GroupList($groups, null, $this);
$cnt = $gl->show();
$this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
$this->page, 'groups');
$this->pagination(
$this->page > 1,
$cnt > GROUPS_PER_PAGE,
$this->page,
'groups'
);
}
function showSections()
public function showSections()
{
$gbp = new GroupsByPostsSection($this);
$gbp->show();

View File

@ -32,9 +32,10 @@ defined('GNUSOCIAL') || die();
* @category Settings
* @package GNUsocial
* @author Zach Copley <zach@status.net>
* @copyright 2008-2009 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*
* @see SettingsAction
* @see SettingsAction
*/
class OauthappssettingsAction extends SettingsAction
@ -79,7 +80,7 @@ class OauthappssettingsAction extends SettingsAction
$application->owner = $this->scoped->getID();
$application->whereAdd("name <> 'anonymous'");
$application->limit($offset, $limit);
$application->orderBy('created DESC');
$application->orderBy('created DESC, id DESC');
$application->find();
$cnt = 0;

View File

@ -112,13 +112,13 @@ class PeopletagAction extends Action
$ptags = new Profile_list();
$ptags->tag = $this->tag;
$ptags->orderBy('profile_list.modified DESC, profile_list.tagged DESC');
$user = common_current_user();
if (empty($user)) {
$ckey = sprintf('profile_list:tag:%s', $this->tag);
$ptags->private = false;
$ptags->orderBy('profile_list.modified DESC');
$c = Cache::instance();
if ($offset+$limit <= PEOPLETAG_CACHE_WINDOW && !empty($c)) {
@ -146,7 +146,6 @@ class PeopletagAction extends Action
$user->getID()
));
$ptags->orderBy('profile_list.modified DESC');
$ptags->find();
}

View File

@ -92,10 +92,8 @@ class SelftagAction extends Action
{
$profile = new Profile();
$profile->_join .= "\n" . <<<'END'
INNER JOIN profile_list ON profile.id = profile_list.tagger
LEFT JOIN profile_role ON profile.id = profile_role.profile_id
END;
$profile->joinAdd(['id', 'profile_list:tagger']);
$profile->joinAdd(['id', 'profile_role:profile_id'], 'LEFT');
$profile->whereAdd(sprintf(
"profile_list.tag = '%s'",
@ -113,7 +111,7 @@ class SelftagAction extends Action
$profile->whereAdd('profile_list.private IS NOT TRUE');
}
$profile->orderBy('profile_list.modified DESC');
$profile->orderBy('profile_list.modified DESC, profile_list.id DESC');
$offset = ($this->page - 1) * PROFILES_PER_PAGE;
$limit = PROFILES_PER_PAGE + 1;

View File

@ -56,9 +56,8 @@ class Group_member extends Managed_DataObject
'group_member_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
),
'indexes' => array(
'group_member_created_idx' => array('created'),
'group_member_profile_id_created_idx' => array('profile_id', 'created'),
'group_member_group_id_created_idx' => array('group_id', 'created'),
'group_member_profile_id_created_group_id_idx' => array('profile_id', 'created', 'group_id'),
'group_member_group_id_created_profile_id_idx' => array('profile_id', 'created', 'group_id'),
),
);
}
@ -160,7 +159,7 @@ class Group_member extends Managed_DataObject
$membership->profile_id = $memberId;
$membership->orderBy('created DESC');
$membership->orderBy('created DESC, group_id DESC');
$membership->limit($offset, $limit);

View File

@ -1414,7 +1414,8 @@ class Notice extends Managed_DataObject
if (empty($this->reply_to)) {
$root = new Notice;
$root->conversation = $this->conversation;
$root->orderBy('notice.created ASC');
$root->orderBy('created, id');
$root->limit(0, 1);
$root->find(true); // true means "fetch first result"
$root->free();
return $root;

View File

@ -61,7 +61,7 @@ class Profile extends Managed_DataObject
),
'primary key' => array('id'),
'indexes' => array(
'profile_nickname_idx' => array('nickname'),
'profile_nickname_created_id_idx' => array('nickname', 'created', 'id'),
),
'fulltext indexes' => array(
'profile_fulltext_idx' => array('nickname', 'fullname', 'location', 'bio', 'homepage'),
@ -333,7 +333,7 @@ class Profile extends Managed_DataObject
$gm = new Group_member();
$gm->profile_id = $this->id;
$gm->orderBy('created DESC');
$gm->orderBy('created DESC, group_id DESC');
if ($gm->find()) {
while ($gm->fetch()) {
@ -518,7 +518,7 @@ class Profile extends Managed_DataObject
$qry .= 'AND cursor < ' . $upto . ' ';
}
$qry .= 'ORDER BY profile_tag.modified DESC ';
$qry .= 'ORDER BY profile_tag.modified DESC, profile_tag.tagged DESC ';
if ($offset >= 0 && !is_null($limit)) {
$qry .= sprintf('LIMIT %d OFFSET %d ', $limit, $offset);
@ -569,7 +569,6 @@ class Profile extends Managed_DataObject
public function getTagSubscriptions(int $offset = 0, ?int $limit = null, int $since = 0, int $upto = 0)
{
$lists = new Profile_list();
$subs = new Profile_tag_subscription();
$lists->joinAdd(['id', 'profile_tag_subscription:profile_tag_id']);
@ -597,7 +596,7 @@ class Profile extends Managed_DataObject
$lists->limit($offset, $limit);
}
$lists->orderBy('profile_tag_subscription.created DESC');
$lists->orderBy('profile_tag_subscription.created DESC, profile_list.id DESC');
$lists->find();
return $lists;
@ -676,46 +675,52 @@ class Profile extends Managed_DataObject
public function getTaggedSubscribers($tag, $offset = 0, $limit = null)
{
$qry =
'SELECT profile.* ' .
'FROM profile JOIN subscription ' .
'ON profile.id = subscription.subscriber ' .
'JOIN profile_tag ON (profile_tag.tagged = subscription.subscriber ' .
'AND profile_tag.tagger = subscription.subscribed) ' .
'WHERE subscription.subscribed = %d ' .
"AND profile_tag.tag = '%s' " .
'AND subscription.subscribed <> subscription.subscriber ' .
'ORDER BY subscription.created DESC ';
$profile = new Profile();
$qry = <<<END
SELECT profile.*
FROM profile
INNER JOIN subscription ON profile.id = subscription.subscriber
INNER JOIN profile_tag
ON profile_tag.tagged = subscription.subscriber
AND profile_tag.tagger = subscription.subscribed
WHERE subscription.subscribed = {$this->getID()}
AND profile_tag.tag = '{$profile->escape($tag)}'
AND subscription.subscribed <> subscription.subscriber
ORDER BY subscription.created DESC, subscription.subscriber DESC
END;
if ($offset) {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
}
$profile = new Profile();
$cnt = $profile->query(sprintf($qry, $this->id, $profile->escape($tag)));
$cnt = $profile->query($qry);
return $profile;
}
public function getTaggedSubscriptions($tag, $offset = 0, $limit = null)
{
$qry =
'SELECT profile.* ' .
'FROM profile JOIN subscription ' .
'ON profile.id = subscription.subscribed ' .
'JOIN profile_tag on (profile_tag.tagged = subscription.subscribed ' .
'AND profile_tag.tagger = subscription.subscriber) ' .
'WHERE subscription.subscriber = %d ' .
"AND profile_tag.tag = '%s' " .
'AND subscription.subscribed <> subscription.subscriber ' .
'ORDER BY subscription.created DESC ';
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
$profile = new Profile();
$profile->query(sprintf($qry, $this->id, $profile->escape($tag)));
$qry = <<<END
SELECT profile.*
FROM profile
INNER JOIN subscription ON profile.id = subscription.subscribed
INNER JOIN profile_tag
ON profile_tag.tagged = subscription.subscribed
AND profile_tag.tagger = subscription.subscriber
WHERE subscription.subscriber = {$this->getID()}
AND profile_tag.tag = '{$profile->escape($tag)}'
AND subscription.subscribed <> subscription.subscriber
ORDER BY subscription.created DESC, subscription.subscribed DESC
END;
if ($offset) {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
}
$profile->query($qry);
return $profile;
}
@ -734,7 +739,9 @@ class Profile extends Managed_DataObject
$subqueue->joinAdd(array('id', 'subscription_queue:subscriber'));
$subqueue->whereAdd(sprintf('subscription_queue.subscribed = %d', $this->getID()));
$subqueue->limit($offset, $limit);
$subqueue->orderBy('subscription_queue.created', 'DESC');
$subqueue->orderBy(
'subscription_queue.created DESC, subscription_queue.subscriber DESC'
);
if (!$subqueue->find()) {
throw new NoResultException($subqueue);
}

View File

@ -65,7 +65,7 @@ class Profile_list extends Managed_DataObject
'profile_list_tagger_fkey' => array('profile', array('tagger' => 'id')),
),
'indexes' => array(
'profile_list_modified_idx' => array('modified'),
'profile_list_modified_id_idx' => array('modified', 'id'),
'profile_list_tag_idx' => array('tag'),
'profile_list_tagged_count_idx' => array('tagged_count'),
'profile_list_subscriber_count_idx' => array('subscriber_count'),
@ -225,11 +225,11 @@ class Profile_list extends Managed_DataObject
$subs->whereAdd('cursor <= ' . $upto);
}
if ($limit != null) {
if (!is_null($limit)) {
$subs->limit($offset, $limit);
}
$subs->orderBy('profile_tag_subscription.created DESC');
$subs->orderBy('profile_tag_subscription.created DESC, profile.id DESC');
$subs->find();
return $subs;
@ -327,11 +327,11 @@ class Profile_list extends Managed_DataObject
$tagged->whereAdd('cursor <= ' . $upto);
}
if ($limit != null) {
if (!is_null($limit)) {
$tagged->limit($offset, $limit);
}
$tagged->orderBy('profile_tag.modified DESC');
$tagged->orderBy('profile_tag.modified DESC, profile_tag.tagged DESC');
$tagged->find();
return $tagged;

View File

@ -43,7 +43,7 @@ class Profile_tag extends Managed_DataObject
'profile_tag_tagged_fkey' => array('profile', array('tagged' => 'id')),
),
'indexes' => array(
'profile_tag_modified_idx' => array('modified'),
'profile_tag_modified_tagged_idx' => array('modified', 'tagged'),
'profile_tag_tagger_tag_idx' => array('tagger', 'tag'),
'profile_tag_tagged_idx' => array('tagged'),
),

View File

@ -50,9 +50,7 @@ class Profile_tag_subscription extends Managed_DataObject
'profile_tag_subscription_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
),
'indexes' => array(
// @fixme probably we want a (profile_id, created) index here?
'profile_tag_subscription_profile_id_idx' => array('profile_id'),
'profile_tag_subscription_created_idx' => array('created'),
'profile_tag_subscription_profile_id_created_profile_tag_id_idx' => array('profile_id', 'created', 'profile_tag_id'),
),
);
}

View File

@ -47,7 +47,7 @@ class Queue_item extends Managed_DataObject
),
'primary key' => array('id'),
'indexes' => array(
'queue_item_created_idx' => array('created'),
'queue_item_created_id_idx' => array('created', 'id'),
),
);
}
@ -78,7 +78,7 @@ class Queue_item extends Managed_DataObject
);
}
$qi->whereAdd('claimed IS NULL');
$qi->orderBy('created');
$qi->orderBy('created, id');
$qi->limit(1);

View File

@ -59,8 +59,8 @@ class Subscription extends Managed_DataObject
'subscription_uri_key' => array('uri'),
),
'indexes' => array(
'subscription_subscriber_created_idx' => array('subscriber', 'created'),
'subscription_subscribed_created_idx' => array('subscribed', 'created'),
'subscription_subscriber_created_subscribed_idx' => array('subscriber', 'created', 'subscribed'),
'subscription_subscribed_created_subscriber_idx' => array('subscribed', 'created', 'subscriber'),
'subscription_token_idx' => array('token'),
),
);
@ -407,7 +407,7 @@ class Subscription extends Managed_DataObject
$sub->$by_type = $profile_id;
$sub->selectAdd($get_type);
$sub->whereAdd($get_type . ' <> ' . $profile_id);
$sub->orderBy('created DESC');
$sub->orderBy("created DESC, {$get_type} DESC");
$sub->limit($queryoffset, $querylimit);
if (!$sub->find()) {

View File

@ -103,7 +103,7 @@ class User extends Managed_DataObject
),
'indexes' => array(
'user_carrier_idx' => array('carrier'),
'user_created_idx' => array('created'),
'user_created_id_idx' => array('created', 'id'),
'user_smsemail_idx' => array('smsemail'),
),
);
@ -734,7 +734,7 @@ class User extends Managed_DataObject
$pr = new Profile_role();
$pr->role = Profile_role::OWNER;
$pr->orderBy('created');
$pr->orderBy('created, profile_id');
$pr->limit(1);
if (!$pr->find(true)) {
@ -839,21 +839,23 @@ class User extends Managed_DataObject
*/
public function getConnectedApps($offset = 0, $limit = null)
{
$qry =
'SELECT u.* ' .
'FROM oauth_application_user AS u, oauth_application AS a ' .
'WHERE u.profile_id = %d ' .
'AND a.id = u.application_id ' .
'AND u.access_type > 0 ' .
'ORDER BY u.created DESC ';
if ($offset > 0) {
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
}
$apps = new Oauth_application_user();
$cnt = $apps->query(sprintf($qry, $this->id));
$apps->selectAdd();
$apps->selectAdd('oauth_application_user.*');
$apps->joinAdd(['application_id', 'oauth_application:id']);
$apps->profile_id = $this->getID();
$apps->whereAdd('oauth_application_user.access_type > 0');
$apps->orderBy('oauth_application_user.created DESC, oauth_application.id DESC');
if ($offset > 0) {
$apps->limit($offset, $limit);
}
$apps->find();
return $apps;
}

View File

@ -92,6 +92,7 @@ class User_group extends Managed_DataObject
),
'indexes' => array(
'user_group_nickname_idx' => array('nickname'),
'user_group_created_id_idx' => array('created', 'id'),
'user_group_profile_id_idx' => array('profile_id'), //make this unique in future
),
);
@ -202,7 +203,7 @@ class User_group extends Managed_DataObject
$gm->group_id = $this->id;
$gm->orderBy('created DESC');
$gm->orderBy('created DESC, profile_id DESC');
if (!is_null($limit)) {
$gm->limit($offset, $limit);
@ -304,7 +305,7 @@ class User_group extends Managed_DataObject
'group_member.group_id = %d AND group_member.is_admin IS TRUE',
$this->getID()
));
$admins->orderBy('group_member.modified ASC');
$admins->orderBy('group_member.modified, group_member.profile_id');
$admins->limit($offset, $limit);
$admins->find();
@ -317,7 +318,7 @@ class User_group extends Managed_DataObject
$blocked = new Profile();
$blocked->joinAdd(array('id', 'group_block:blocked'));
$blocked->whereAdd(sprintf('group_block.group_id = %u', $this->id));
$blocked->orderBy('group_block.modified DESC');
$blocked->orderBy('group_block.modified DESC, group_block.blocked DESC');
$blocked->limit($offset, $limit);
$blocked->find();

View File

@ -1,22 +1,29 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2010 StatusNet, Inc.
* Class for activity streams
*
* 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 <http://www.gnu.org/licenses/>.
* @package GNUsocial
* @copyright 2010 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
defined('GNUSOCIAL') || die();
/**
* Class for activity streams
*
@ -24,6 +31,9 @@
*
* We extend atomusernoticefeed since it does some nice setup for us.
*
* @package GNUsocial
* @copyright 2010 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class UserActivityStream extends AtomUserNoticeFeed
{
@ -37,14 +47,18 @@ class UserActivityStream extends AtomUserNoticeFeed
/**
*
* @param User $user
* @param boolean $indent
* @param boolean $outputMode: UserActivityStream::OUTPUT_STRING to return a string,
* or UserActivityStream::OUTPUT_RAW to go to raw output.
* Raw output mode will attempt to stream, keeping less
* data in memory but will leave $this->activities incomplete.
* @param bool $indent
* @param bool $outputMode: UserActivityStream::OUTPUT_STRING to return a string,
* or UserActivityStream::OUTPUT_RAW to go to raw output.
* Raw output mode will attempt to stream, keeping less
* data in memory but will leave $this->activities incomplete.
*/
function __construct($user, $indent = true, $outputMode = UserActivityStream::OUTPUT_STRING, $after = null)
{
public function __construct(
$user,
$indent = true,
$outputMode = UserActivityStream::OUTPUT_STRING,
$after = null
) {
parent::__construct($user, null, $indent);
$this->outputMode = $outputMode;
@ -56,7 +70,7 @@ class UserActivityStream extends AtomUserNoticeFeed
// Raw output... need to restructure from the stringer init.
$this->xw = new XMLWriter();
$this->xw->openURI('php://output');
if(is_null($indent)) {
if (is_null($indent)) {
$indent = common_config('site', 'indent');
}
$this->xw->setIndent($indent);
@ -102,7 +116,7 @@ class UserActivityStream extends AtomUserNoticeFeed
* Interleave the pre-sorted objects with the user's
* notices, all in reverse chron order.
*/
function renderEntries($format=Feed::ATOM, $handle=null)
public function renderEntries($format = Feed::ATOM, $handle = null)
{
$haveOne = false;
@ -223,7 +237,7 @@ class UserActivityStream extends AtomUserNoticeFeed
}
}
function compareObject($a, $b)
public function compareObject($a, $b)
{
$ac = strtotime((empty($a->created)) ? $a->modified : $a->created);
$bc = strtotime((empty($b->created)) ? $b->modified : $b->created);
@ -231,7 +245,7 @@ class UserActivityStream extends AtomUserNoticeFeed
return (($ac == $bc) ? 0 : (($ac < $bc) ? 1 : -1));
}
function getSubscriptions()
public function getSubscriptions()
{
$subs = array();
@ -254,7 +268,7 @@ class UserActivityStream extends AtomUserNoticeFeed
return $subs;
}
function getSubscribers()
public function getSubscribers()
{
$subs = array();
@ -283,9 +297,9 @@ class UserActivityStream extends AtomUserNoticeFeed
* @param int $end unix timestamp for latest
* @return array of Notice objects
*/
function getNoticesBetween($start=0, $end=0)
public function getNoticesBetween($start = 0, $end = 0)
{
$notices = array();
$notices = [];
$notice = new Notice();
@ -311,7 +325,7 @@ class UserActivityStream extends AtomUserNoticeFeed
$notice->whereAdd("created < '$tsend'");
}
$notice->orderBy('created DESC');
$notice->orderBy('created DESC, id DESC');
if ($notice->find()) {
while ($notice->fetch()) {
@ -322,7 +336,7 @@ class UserActivityStream extends AtomUserNoticeFeed
return $notices;
}
function getNotices()
public function getNotices()
{
if (!empty($this->after)) {
return $this->getNoticesBetween($this->after);
@ -331,7 +345,7 @@ class UserActivityStream extends AtomUserNoticeFeed
}
}
function getGroups()
public function getGroups()
{
$groups = array();
@ -352,12 +366,13 @@ class UserActivityStream extends AtomUserNoticeFeed
return $groups;
}
function createdAfter($item) {
public function createdAfter($item)
{
$created = strtotime((empty($item->created)) ? $item->modified : $item->created);
return ($created >= $this->after);
}
function writeJSON($handle)
public function writeJSON($handle)
{
require_once INSTALLDIR . '/lib/activitystreams/activitystreamjsondocument.php';
fwrite($handle, '{"items": [');

View File

@ -1,74 +1,70 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* StatusNet, the distributed open-source microblogging tool
*
* FIXME
*
* PHP version 5
*
* LICENCE: 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 <http://www.gnu.org/licenses/>.
*
* @category Widget
* @package StatusNet
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @copyright 2009 StatusNet, Inc.
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
defined('GNUSOCIAL') || die();
/**
* FIXME
*
* These are the widgets that show interesting data about a person * group, or site.
*
* @category Widget
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
* @category Widget
* @package GNUsocial
* @author Evan Prodromou <evan@status.net>
* @copyright 2009 StatusNet, Inc.
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class AttachmentNoticeSection extends NoticeSection
{
function showContent() {
public function showContent()
{
parent::showContent();
return false;
}
function getNotices()
public function getNotices()
{
$notice = new Notice;
$notice->joinAdd(array('id', 'file_to_post:post_id'));
$notice->whereAdd(sprintf('file_to_post.file_id = %d', $this->out->attachment->id));
$notice->orderBy('created desc');
$notice->selectAdd('post_id as id');
$notice->selectAdd('notice.id');
$notice->orderBy('notice.created DESC, notice.id DESC');
$notice->find();
return $notice;
}
function title()
public function title()
{
// TRANS: Title.
return _('Notices where this attachment appears');
}
function divId()
public function divId()
{
return 'attachment_section';
}

View File

@ -87,8 +87,7 @@ class RawConversationNoticeStream extends NoticeStream
self::filterVerbs($notice, $this->selectVerbs);
// ORDER BY
// currently imitates the previously used "_reverseChron" sorting
$notice->orderBy('notice.created DESC');
$notice->orderBy('notice.created DESC, notice.id DESC');
$notice->find();
return $notice->fetchAll('id');
}

View File

@ -39,12 +39,6 @@ class SearchEngine
public function set_sort_mode($mode)
{
switch ($mode) {
case 'chron':
return $this->target->orderBy('created DESC');
break;
case 'reverse_chron':
return $this->target->orderBy('created ASC');
break;
case 'nickname_desc':
if ($this->table != 'profile') {
throw new Exception(
@ -63,8 +57,12 @@ class SearchEngine
return $this->target->orderBy(sprintf('%1$s.nickname ASC', $this->table));
}
break;
case 'reverse_chron':
return $this->target->orderBy('created, id');
break;
case 'chron':
default:
return $this->target->orderBy('created DESC');
return $this->target->orderBy('created DESC, id DESC');
break;
}
}

View File

@ -51,19 +51,27 @@ class FeaturedUsersSection extends ProfileSection
return null;
}
$quoted = array();
$quoted_nicks = implode(
',',
array_map(
function (string $nick): string {
return "'{$nick}'";
},
$featured_nicks
)
);
foreach ($featured_nicks as $nick) {
$quoted[] = "'$nick'";
}
$table = common_database_tablename('user');
$user_table = common_database_tablename('user');
$limit = PROFILES_PER_SECTION + 1;
$qry = 'SELECT profile.* ' .
'FROM profile INNER JOIN ' . $table . ' ON profile.id = ' . $table . '.id ' .
'WHERE ' . $table . '.nickname IN (' . implode(',', $quoted) . ') ' .
'ORDER BY profile.created DESC LIMIT ' . $limit;
$qry = <<<END
SELECT profile.*
FROM profile
INNER JOIN {$user_table} ON profile.id = {$user_table}.id
WHERE profile.nickname IN ({$quoted_nicks})
ORDER BY profile.created DESC, profile.id DESC
LIMIT {$limit};
END;
$profile = Memcached_DataObject::cachedQuery('Profile', $qry, 6 * 3600);
return $profile;

View File

@ -411,6 +411,7 @@ class FavoriteModule extends ActivityVerbHandlerModule
if (!empty($uas->after)) {
$fave->whereAdd("modified > '" . common_sql_date($uas->after) . "'");
}
$fave->orderBy('modified DESC, notice_id DESC');
if ($fave->find()) {
while ($fave->fetch()) {

View File

@ -1,43 +1,44 @@
<?php
// This file is part of GNU social - https://www.gnu.org/software/social
//
// GNU social 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.
//
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
/**
* StatusNet, the distributed open-source microblogging tool
*
* Show up to 100 favs of a notice
*
* PHP version 5
*
* LICENCE: 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 <http://www.gnu.org/licenses/>.
*
* @category API
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://www.gnu.org/software/social/
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
if (!defined('GNUSOCIAL')) { exit(1); }
defined('GNUSOCIAL') || die();
/**
* Show up to 100 favs of a notice
*
* @package GNUsocial
* @author Hannes Mannerheim <h@nnesmannerhe.im>
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
*/
class ApiStatusesFavsAction extends ApiAuthAction
{
const MAXCOUNT = 100;
var $original = null; // Notice object for which to retrieve favs
var $cnt = self::MAXCOUNT;
// Notice object for which to retrieve favs
public $original = null;
public $cnt = self::MAXCOUNT;
/**
* Take arguments for running
@ -46,7 +47,7 @@ class ApiStatusesFavsAction extends ApiAuthAction
*
* @return boolean success flag
*/
protected function prepare(array $args=array())
protected function prepare(array $args = [])
{
parent::prepare($args);
@ -86,37 +87,35 @@ class ApiStatusesFavsAction extends ApiAuthAction
protected function handle()
{
parent::handle();
$fave = new Fave();
$fave->selectAdd();
$fave->selectAdd();
$fave->selectAdd('user_id');
$fave->notice_id = $this->original->id;
$fave->orderBy('modified');
$fave->orderBy('modified, user_id');
if (!is_null($this->cnt)) {
$fave->limit(0, $this->cnt);
}
$ids = $fave->fetchAll('user_id');
// get nickname and profile image
$ids_with_profile_data = array();
$i=0;
foreach($ids as $id) {
$profile = Profile::getKV('id', $id);
$ids_with_profile_data[$i]['user_id'] = $id;
$ids_with_profile_data[$i]['nickname'] = $profile->nickname;
$ids_with_profile_data[$i]['fullname'] = $profile->fullname;
$ids_with_profile_data[$i]['profileurl'] = $profile->profileurl;
$profile = new Profile();
$profile->id = $id;
$avatarurl = $profile->avatarUrl(24);
$ids_with_profile_data[$i]['avatarurl'] = $avatarurl;
$i++;
}
$this->initDocument('json');
$this->showJsonObjects($ids_with_profile_data);
$this->endDocument('json');
$ids = $fave->fetchAll('user_id');
// Get nickname and profile image.
$ids_with_profile_data = [];
foreach (array_values($ids) as $i => $id) {
$profile = Profile::getKV('id', $id);
$ids_with_profile_data[$i]['user_id'] = $id;
$ids_with_profile_data[$i]['nickname'] = $profile->nickname;
$ids_with_profile_data[$i]['fullname'] = $profile->fullname;
$ids_with_profile_data[$i]['profileurl'] = $profile->profileurl;
$profile = new Profile();
$profile->id = $id;
$avatarurl = $profile->avatarUrl(24);
$ids_with_profile_data[$i]['avatarurl'] = $avatarurl;
}
$this->initDocument('json');
$this->showJsonObjects($ids_with_profile_data);
$this->endDocument('json');
}
/**
@ -129,7 +128,7 @@ class ApiStatusesFavsAction extends ApiAuthAction
* @return boolean is read only action?
*/
function isReadOnly($args)
public function isReadOnly($args)
{
return true;
}

View File

@ -48,8 +48,8 @@ class Fave extends Managed_DataObject
'fave_user_id_fkey' => array('profile', array('user_id' => 'id')), // note: formerly referenced notice.id, but we can now record remote users' favorites
),
'indexes' => array(
'fave_user_id_modified_idx' => array('user_id', 'modified'),
'fave_modified_idx' => array('modified'),
'fave_user_id_modified_notice_id_idx' => array('user_id', 'modified', 'notice_id'),
'fave_notice_id_modified_user_id_idx' => array('notice_id', 'modified', 'user_id'),
),
);
}
@ -280,7 +280,7 @@ class Fave extends Managed_DataObject
$fav->user_id = $profileId;
$fav->orderBy('modified DESC');
$fav->orderBy('modified DESC, notice_id DESC');
$fav->limit($offset, $limit);

View File

@ -88,46 +88,36 @@ class RawFaveNoticeStream extends NoticeStream
*/
public function getNoticeIds($offset, $limit, $since_id, $max_id)
{
$fav = new Fave();
$qry = null;
$fave = new Fave();
if ($this->own) {
$qry = 'SELECT fave.* FROM fave ';
$qry .= 'WHERE fave.user_id = ' . $this->user_id . ' ';
} else {
$qry = 'SELECT fave.* FROM fave ';
$qry .= 'INNER JOIN notice ON fave.notice_id = notice.id ';
$qry .= 'WHERE fave.user_id = ' . $this->user_id . ' ';
$qry .= 'AND notice.is_local <> ' . Notice::GATEWAY . ' ';
$fave->selectAdd();
$fave->selectAdd('fave.*');
$fave->user_id = $this->user_id;
if (!$this->own) {
$fave->joinAdd(['notice_id', 'notice:id']);
$fave->whereAdd('notice.is_local <> ' . Notice::GATEWAY);
}
if ($since_id != 0) {
$qry .= 'AND notice_id > ' . $since_id . ' ';
}
if ($max_id != 0) {
$qry .= 'AND notice_id <= ' . $max_id . ' ';
}
Notice::addWhereSinceId($fave, $since_id, 'notice_id', 'fave.modified');
Notice::addWhereMaxId($fave, $max_id, 'notice_id', 'fave.modified');
// NOTE: we sort by fave time, not by notice time!
$qry .= 'ORDER BY modified DESC ';
$fave->orderBy('fave.modified DESC, notice_id DESC');
if (!is_null($offset)) {
$qry .= "LIMIT $limit OFFSET $offset";
$fave->limit($offset, $limit);
}
$fav->query($qry);
$ids = array();
while ($fav->fetch()) {
$ids[] = $fav->notice_id;