Index: votingapi.admin.inc
===================================================================
--- votingapi.admin.inc	(revision 46)
+++ votingapi.admin.inc	(working copy)
@@ -34,6 +34,42 @@
   return system_settings_form($form);
 }

+/**
+ * Rebuild the voting results and clear the voting cache.
+ */
+function votingapi_rebuild_form($form, $form_state) {
+  $query = db_select('votingapi_vote')
+    ->fields('votingapi_vote', array('tag'))
+    ->distinct(TRUE);
+
+  $results = $query
+    ->execute()
+    ->fetchAll(PDO::FETCH_ASSOC);
+
+  $tags = array('All Tags');
+  foreach ($results as $tag_type) {
+    $tags[] = $tag_type['tag'];
+  }
+
+  $form['results'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Rebuild voting results'),
+  );
+  $form['results']['votingapi_rebuild_content_selected_tag'] = array(
+    '#type' => 'select',
+    '#title' => t('Content tags'),
+    '#options' => $tags,
+  );
+   $form['results']['votingapi_rebuild_content_tags'] = array(
+    '#type' => 'hidden',
+    '#value' => $tags,
+  );
+  $form['results']['votingapi_recalculate_all'] = array(
+    '#type' => 'submit',
+    '#value' => t('Rebuild all Voting Results'),
+    '#description' => t('This action rebuilds all voting results, and may be a lengthy process.'),
+  );
+  $form['results']['help'] = array(
+    '#prefix' => '<div class="description">',
+    '#markup' => t('This action rebuilds all voting results, and may be a lengthy process.'),
+    '#suffix' => '</div>',
+  );
+
+  return $form;
+}
+
 /**
  * Developer tool to generate dummy votes.
  */
@@ -78,3 +114,100 @@
   require_once(drupal_get_path('module', 'votingapi') . '/votingapi.drush.inc');
   votingapi_generate_votes('node', $form_state['values']['vote_type'], $options);
 }
+
+/**
+ * Implements hook_form_submit().
+ *
+ * Recalculates voting results for all content, using the batch API.
+ */
+function votingapi_rebuild_form_submit($form, &$form_state) {
+  $tags = $form_state['values']['votingapi_rebuild_content_tags'];
+  $selected_tag_index = $form_state['values']['votingapi_rebuild_content_selected_tag'];
+
+  // Change the list of tags to remove accordingly.
+  if ($selected_tag_index > 0) {
+    $tags = array($tags[$selected_tag_index]);
+  }
+  else {
+    unset($tags[0]);
+  }
+
+  $operations = array();
+  foreach ($tags as $tag) {
+    $operations[] = array('_votingapi_results_rebuild', array($tag));
+  }
+  $batch = array(
+    'title' => t('Recalculating votes'),
+    'operations' => $operations,
+    'init_message' => 'Preparing to recalculate',
+    'finished' => '_votingapi_results_rebuild_finished',
+    'file' => drupal_get_path('module', 'votingapi') . '/votingapi.admin.inc',
+  );
+  // Set the recalculation batch so it can be processed.
+  batch_set($batch);
+
+  // Redirect the user to the votingapi rebuild page.
+  $form_state['redirect'] = 'admin/config/search/votingapi/rebuild';
+}
+
+/**
+ * Batch API callback for the vote rebuilding operation.
+ */
+function _votingapi_results_rebuild($tag, &$context) {
+  if (empty($context['sandbox']['max'])) {
+
+    // Set up the sandbox to work within.
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['current_entity_id'] = 0;
+
+    // Initialize the result count to 0 if it has not already been set.
+    if (!isset($context['results']['count'])) {
+      $context['results']['count'] = 0;
+    }
+    // Get the highest entity_id for this context.
+    $context['sandbox']['max'] = db_query('SELECT MAX(entity_id) FROM {votingapi_vote} WHERE tag= :tag', array(':tag' => $tag))->fetchField();
+  }
+
+  $limit = 100;
+
+  // Grab all entity_ids from the votingapi_vote table.
+  $result = db_query_range('SELECT entity_type, entity_id FROM {votingapi_vote} WHERE entity_id > 0 AND tag = :tag AND entity_id > :cid ORDER BY entity_id  ASC', 0, $limit, array(':tag' => $tag, ':cid' => $context['sandbox']['current_entity_id']));
+
+  foreach($result as $row) {
+    watchdog('votingapi', 'trying type @type @entity', array('@type' => $type, '@entity' => $row->entity_id), WATCHDOG_ERROR);
+
+    $context['sandbox']['progress']++;
+    $context['sandbox']['current_entity_id'] = $row->entity_id;
+    // Force vote recalculation for this entity.
+    votingapi_recalculate_results($row->entity_type, $row->entity_id, TRUE);
+    $context['results']['count']++;
+  }
+
+  // Check to see if the process has not finished yet.
+  if ($context['sandbox']['current_entity_id'] != $context['sandbox']['max']) {
+    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+  else {
+    // If we are finished with this run, delete the sandbox.
+    unset($context['sandbox']);
+  }
+}
+
+/**
+ * Batch API callback for finished recalculation batch operations.
+ */
+function _votingapi_results_rebuild_finished($success, $results, $operations) {
+  if ($success) {
+    $message = format_plural($results['count'], 'One content item was recalculated.', '@count content items were recalculated');
+  }
+  else {
+    $message = t('The voting results rebuild process encountered an error.');
+  }
+  drupal_set_message($message);
+}
+
Index: votingapi.module
===================================================================
--- votingapi.module	(revision 46)
+++ votingapi.module	(working copy)
@@ -37,6 +37,22 @@
     );
   }
 
+  $items['admin/config/search/votingapi/general'] = array(
+    'title' => 'General Settings',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/config/search/votingapi/rebuild'] = array(
+    'title' => t('Rebuild voting results'),
+    'description' => t('Allows rebuilding of voting results cache'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('votingapi_rebuild_form'),
+    'access callback' => 'user_access',
+    'access arguments' => array('administer voting api'),
+    'file' => 'votingapi.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+
   return $items;
 }
 
