When the cached votes are recalculated, the module first deletes the existing cached entry, then inserts a new line into the db with a new vote_cache_id.
The result of this is that the vote_cache_id is constantly growing on each tally.

This can be avoided by simply updating the existing cached entry. To do this, I modified votingapi_recalculate_results() and added a new function _votingapi_update_cache_result() which is quite similar to _votingapi_insert_cache_result().
Actually, with _votingapi_update_cache_result() in place, I'm pretty sure that _votingapi_insert_cache_result() is redundant.
Here's the code:


function votingapi_recalculate_results($content_type, $content_id, $force_calculation = FALSE) {
  // if we're operating in cron mode, and the 'force recalculation' flag is NOT set, 
  // bail out. The cron run will pick up the results.
  if (variable_get('votingapi_calculation_schedule', 'immediate') != 'cron' && $force_calculation != TRUE) {
    // blow away the existing cache records.
    /**
     * vote_cache_id patch. Entry not deleted, just getting updated.
     */
    //db_query("DELETE FROM {votingapi_cache} WHERE content_type = '%s' AND content_id = %d", $content_type, $content_id);
    
    $cache = array();
    $votes = _votingapi_get_raw_votes($content_type, $content_id);
    
    // Loop through, calculate per-type and per-tag totals, etc.
    foreach($votes as $vote) {
      switch ($vote->value_type) {
        case 'percent':
          $cache[$vote->tag][$vote->value_type]['count'] += 1;
          $cache[$vote->tag][$vote->value_type]['sum'] += $vote->value;
          break;
          
        case 'points':
          $cache[$vote->tag][$vote->value_type]['count'] += 1;
          $cache[$vote->tag][$vote->value_type]['sum'] += $vote->value;
          break;
          
        case 'option':
          $cache[$vote->tag][$vote->value_type][$vote->value] += 1;
          break;
      }
    }
   
    // Do a quick loop through to calculate averages.
    // This is also a good example of how external modules can do their own processing.
    foreach ($cache as $tag=>$types) {
      foreach ($types as $type=>$functions) {
        if ($type == 'percent' || $type == 'points') {
          $cache[$tag][$type]['average'] = $functions['sum'] / $functions['count'];
        }
        if ($type == 'percent') {
          // we don't actually need the sum for this. discard it to avoid cluttering the db.
          unset($cache[$tag][$type]['sum']);
        }
      }
    }
  
    // Give other modules a chance to alter the collection of votes.
    votingapi_invoke_votingapi($cache, 'calculate', $votes, $content_type, $content_id);
    
    // Now, do the caching. Woo.
    foreach ($cache as $tag=>$types) {
      foreach ($types as $type=>$functions) {
        foreach ($functions as $function=>$value) {
          /**
           * vote_cache_id patch. Entry not deleted, just getting updated.
           */
           //$cached[] = _votingapi_insert_cache_result($content_type, $content_id, $value, $type, $tag, $function);
          $cached[] = _votingapi_update_cache_result($content_type, $content_id, $value, $type, $tag, $function);
        }
      }
    }
  
    // Give other modules a chance to act on the results of the vote totaling.
    votingapi_invoke_votingapi($cached, 'post calculate', $votes, $content_type, $content_id);
  
    // If the actions module is enabled, hand off the voting results
    // for possible node promotion, user banning, or what not.
    if (module_exist('actions') && variable_get('votingapi_actions_mode', 'all') != 'none') {
      _votingapi_process_actions($content_id, $content_type, $votes, $cached);
    }
  
    return $cached;
  }
}

/**
 * Update a cached aggregate vote for a given piece of content.
 *
 * vote_cache_id patch. Used instead of _votingapi_insert_cache_result(), to prevent
 * vote_cache_id from updating each recalculation.
 * 
 * @param $content_type
 *   A string identifying the type of content being rated. Node, comment, aggregator item, etc.
 * @param $content_id
 *   The key ID of the content being rated.
 * @param $value
 *   An int representing the aggregate value of the votes cast.
 * @param $value_type
 *   An int representing the value_type shared by the aggregated votes.
 * @param $tag
 *   A string to separate multiple voting criteria. For example, a voting system that rates software for 'stability'
 *   and 'features' would cast two votes, each with a different tag. If none is specified, the default 'vote' tag is used.
 * @param $function
 *   The summary function being used to aggregate the votes. 'sum', 'count', and 'average' are used by votingapi.
 *   If the value_type is 'option', this field contains the key. Slightly ugly, but oh well.
 * @return
 *   The resulting cached object.
 */
function _votingapi_update_cache_result($content_type, $content_id, $value, $value_type, $tag, $function) {
  $vobj->content_type = $content_type;
  $vobj->content_id = $content_id;
  $vobj->value = $value;
  $vobj->value_type = $value_type;
  $vobj->tag = $tag;
  $vobj->function = $function;
  
  $vote_cache_id = db_result(db_query("SELECT vote_cache_id FROM {votingapi_cache} WHERE content_type = '%s' AND content_id = %d AND value_type = '%s' AND tag = '%s' AND function = '%s'", $vobj->content_type, $vobj->content_id, $vobj->value_type, $vobj->tag, $vobj->function));

  if ($vote_cache_id) {
    $vobj->vote_cache_id = $vote_cache_id;
    db_query("UPDATE {votingapi_cache} SET value = %f WHERE vote_cache_id = %d", $vobj->value, $vobj->vote_cache_id);
  } else {
    $vobj->vote_cache_id = db_next_id('{votingapi_cache}');
    db_query("INSERT INTO {votingapi_cache} (vote_cache_id, content_type, content_id, value, value_type, tag, function) VALUES (%d, '%s', %d, %f, '%s', '%s', '%s')", 
    $vobj->vote_cache_id, $vobj->content_type, $vobj->content_id, $vobj->value, $vobj->value_type, $vobj->tag, $vobj->function);
  }
  return $vobj;
}

Comments

ardee-1’s picture

Is this still outstanding, 9 months later?

eaton’s picture

Status: Needs review » Closed (works as designed)

Individual vote IDs are not preserved; this is by design. Imposing a full 'update' cycle would grow the amount of database chatter needed to cast votes dramatically.