Skip to content

Commit

Permalink
Merge pull request #984 from Spuds/stats
Browse files Browse the repository at this point in the history
reworking updateStats()
  • Loading branch information
eurich committed Nov 6, 2013
2 parents 3228ed9 + a27b10f commit 66fb617
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 202 deletions.
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,
)
);
}

0 comments on commit 66fb617

Please sign in to comment.