Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reworking updateStats() #984

Merged
merged 6 commits into from
Nov 6, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
209 changes: 19 additions & 190 deletions sources/Subs.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,199 +47,28 @@
*/
function updateStats($type, $parameter1 = null, $parameter2 = null)
{
global $modSettings;

$db = database();

switch ($type)
{
case 'member':
$changes = array(
'memberlist_updated' => time(),
);

// #1 latest member ID, #2 the real name for a new registration.
if (is_numeric($parameter1))
{
$changes['latestMember'] = $parameter1;
$changes['latestRealName'] = $parameter2;

updateSettings(array('totalMembers' => true), true);
}

// We need to calculate the totals.
else
{
// Update the latest activated member (highest id_member) and count.
$result = $db->query('', '
SELECT COUNT(*), MAX(id_member)
FROM {db_prefix}members
WHERE is_activated = {int:is_activated}',
array(
'is_activated' => 1,
)
);
list ($changes['totalMembers'], $changes['latestMember']) = $db->fetch_row($result);
$db->free_result($result);

case 'member':
require_once(SUBSDIR . '/Members.subs.php');
// Get the latest activated member's display name.
$result = getBasicMemberData((int) $changes['latestMember']);
$changes['latestRealName'] = $result['real_name'];

// Are we using registration approval?
if ((!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2) || !empty($modSettings['approveAccountDeletion']))
{
// Update the amount of members awaiting approval - ignoring COPPA accounts, as you can't approve them until you get permission.
$result = $db->query('', '
SELECT COUNT(*)
FROM {db_prefix}members
WHERE is_activated IN ({array_int:activation_status})',
array(
'activation_status' => array(3, 4),
)
);
list ($changes['unapprovedMembers']) = $db->fetch_row($result);
$db->free_result($result);
}
}

updateSettings($changes);
break;

case 'message':
if ($parameter1 === true && $parameter2 !== null)
updateSettings(array('totalMessages' => true, 'maxMsgID' => $parameter2), true);
else
{
// SUM and MAX on a smaller table is better for InnoDB tables.
$result = $db->query('', '
SELECT SUM(num_posts + unapproved_posts) AS total_messages, MAX(id_last_msg) AS max_msg_id
FROM {db_prefix}boards
WHERE redirect = {string:blank_redirect}' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
AND id_board != {int:recycle_board}' : ''),
array(
'recycle_board' => isset($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0,
'blank_redirect' => '',
)
);
$row = $db->fetch_assoc($result);
$db->free_result($result);

updateSettings(array(
'totalMessages' => $row['total_messages'] === null ? 0 : $row['total_messages'],
'maxMsgID' => $row['max_msg_id'] === null ? 0 : $row['max_msg_id']
));
}
break;

case 'subject':
// Remove the previous subject (if any).
$db->query('', '
DELETE FROM {db_prefix}log_search_subjects
WHERE id_topic = {int:id_topic}',
array(
'id_topic' => (int) $parameter1,
)
);

// Insert the new subject.
if ($parameter2 !== null)
{
$parameter1 = (int) $parameter1;
$parameter2 = text2words($parameter2);

$inserts = array();
foreach ($parameter2 as $word)
$inserts[] = array($word, $parameter1);

if (!empty($inserts))
$db->insert('ignore',
'{db_prefix}log_search_subjects',
array('word' => 'string', 'id_topic' => 'int'),
$inserts,
array('word', 'id_topic')
);
}
break;

case 'topic':
if ($parameter1 === true)
updateSettings(array('totalTopics' => true), true);
else
{
// Get the number of topics - a SUM is better for InnoDB tables.
// We also ignore the recycle bin here because there will probably be a bunch of one-post topics there.
$result = $db->query('', '
SELECT SUM(num_topics + unapproved_topics) AS total_topics
FROM {db_prefix}boards' . (!empty($modSettings['recycle_enable']) && $modSettings['recycle_board'] > 0 ? '
WHERE id_board != {int:recycle_board}' : ''),
array(
'recycle_board' => !empty($modSettings['recycle_board']) ? $modSettings['recycle_board'] : 0,
)
);
$row = $db->fetch_assoc($result);
$db->free_result($result);

updateSettings(array('totalTopics' => $row['total_topics'] === null ? 0 : $row['total_topics']));
}
break;

case 'postgroups':
// Parameter two is the updated columns: we should check to see if we base groups off any of these.
if ($parameter2 !== null && !in_array('posts', $parameter2))
return;

$postgroups = cache_get_data('updateStats:postgroups', 360);
if ($postgroups === null || $parameter1 === null)
{
// Fetch the postgroups!
$request = $db->query('', '
SELECT id_group, min_posts
FROM {db_prefix}membergroups
WHERE min_posts != {int:min_posts}',
array(
'min_posts' => -1,
)
);
$postgroups = array();
while ($row = $db->fetch_assoc($request))
$postgroups[$row['id_group']] = $row['min_posts'];
$db->free_result($request);

// Sort them this way because if it's done with MySQL it causes a filesort :(.
arsort($postgroups);

cache_put_data('updateStats:postgroups', $postgroups, 360);
}

// Oh great, they've screwed their post groups.
if (empty($postgroups))
return;

// Set all membergroups from most posts to least posts.
$conditions = '';
$lastMin = 0;
foreach ($postgroups as $id => $min_posts)
{
$conditions .= '
WHEN posts >= ' . $min_posts . (!empty($lastMin) ? ' AND posts <= ' . $lastMin : '') . ' THEN ' . $id;
$lastMin = $min_posts;
}

// A big fat CASE WHEN... END is faster than a zillion UPDATE's ;).
$db->query('', '
UPDATE {db_prefix}members
SET id_post_group = CASE ' . $conditions . '
ELSE 0
END' . ($parameter1 != null ? '
WHERE ' . (is_array($parameter1) ? 'id_member IN ({array_int:members})' : 'id_member = {int:members}') : ''),
array(
'members' => $parameter1,
)
);
break;

updateMemberStats($parameter1, $parameter2);
break;
case 'message':
require_once(SUBSDIR . '/Messages.subs.php');
updateMessageStats($parameter1, $parameter2);
break;
case 'subject':
require_once(SUBSDIR . '/Messages.subs.php');
updateSubjectStats($parameter1, $parameter2);
break;
case 'topic':
require_once(SUBSDIR . '/Topic.subs.php');
updateTopicStats($parameter1, $parameter2);
break;
case 'postgroups':
require_once(SUBSDIR . '/Membergroups.subs.php');
updatePostGroupStats($parameter1, $parameter2);
break;
default:
trigger_error('updateStats(): Invalid statistic type \'' . $type . '\'', E_USER_NOTICE);
}
Expand Down
77 changes: 73 additions & 4 deletions sources/subs/Membergroups.subs.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,11 @@ function deleteMembergroups($groups)
);

// Recalculate the post groups, as they likely changed.
updateStats('postgroups');
updatePostgroupStats();

// Make a note of the fact that the cache may be wrong.
$settings_update = array('settings_updated' => time());

// Have we deleted the spider group?
if (isset($modSettings['spider_group']) && in_array($modSettings['spider_group'], $groups))
$settings_update['spider_group'] = 0;
Expand Down Expand Up @@ -276,7 +277,7 @@ function removeMembersFromGroups($members, $groups = null, $permissionCheckDone
)
);

updateStats('postgroups', $members);
updatePostgroupStats($members);

// Log what just happened.
foreach ($members as $member)
Expand Down Expand Up @@ -393,7 +394,7 @@ function removeMembersFromGroups($members, $groups = null, $permissionCheckDone
);

// Their post groups may have changed now...
updateStats('postgroups', $members);
updatePostgroupStats($members);

// Do the log.
if (!empty($log_inserts) && !empty($modSettings['modlog_enabled']))
Expand Down Expand Up @@ -523,7 +524,7 @@ function addMembersToGroup($members, $group, $type = 'auto', $permissionCheckDon
call_integration_hook('integrate_add_members_to_group', array($members, $group_details, &$group_names));

// Update their postgroup statistics.
updateStats('postgroups', $members);
updatePostgroupStats($members);

require_once(SOURCEDIR . '/Logging.php');
foreach ($members as $member)
Expand Down Expand Up @@ -1880,4 +1881,72 @@ function deleteGroupRequests($groups)
'request_list' => $groups,
)
);
}

/**
* This function updates those members who match post-based
* membergroups in the database (restricted by parameter $members).
* Used by updateStats('postgroups').
*
* @param array $members = null The members to update, null if all
* @param array $parameter2 = null
*/
function updatePostGroupStats($members = null, $parameter2)
{
$db = database();

// Parameter two is the updated columns: we should check to see if we base groups off any of these.
if ($parameter2 !== null && !in_array('posts', $parameter2))
return;

$postgroups = cache_get_data('updateStats:postgroups', 360);
if ($postgroups === null || $parameter1 === null)
{
// Fetch the postgroups!
$request = $db->query('', '
SELECT id_group, min_posts
FROM {db_prefix}membergroups
WHERE min_posts != {int:min_posts}',
array(
'min_posts' => -1,
)
);
$postgroups = array();
while ($row = $db->fetch_assoc($request))
$postgroups[$row['id_group']] = $row['min_posts'];
$db->free_result($request);

// Sort them this way because if it's done with MySQL it causes a filesort :(.
arsort($postgroups);

cache_put_data('updateStats:postgroups', $postgroups, 360);
}

// Oh great, they've screwed their post groups.
if (empty($postgroups))
return;

// Set all membergroups from most posts to least posts.
$conditions = '';
$lastMin = 0;
foreach ($postgroups as $id => $min_posts)
{
$conditions .= '
WHEN posts >= ' . $min_posts . (!empty($lastMin) ? ' AND posts <= ' . $lastMin : '') . ' THEN ' . $id;
$lastMin = $min_posts;
}

$members = is_array($members) ? $members : array($members);

// A big fat CASE WHEN... END is faster than a zillion UPDATE's ;).
$db->query('', '
UPDATE {db_prefix}members
SET id_post_group = CASE ' . $conditions . '
ELSE 0
END' . ($members !== null ? '
WHERE id_member IN ({array_int:members})' : ''),
array(
'members' => $members,
)
);
}