Alter field values via module How can I update field values of all nodes via module?

I have a hook_cron function inside MYMODULE.module that updates field_popularity_score field of all nodes, of a particular type, so that I can use Views to sort the nodes by field_popularity_score.
In Drupal 7, you should use a queue for such type of operations. In short, what happens is the following:


An item is added to the queue
An item in the queue is processed, and removed


In the case of cron tasks, there is a hook (hook_cron_queue_info()) that tells Drupal which queues need to be handled when drupal_cron_run() is executed, and which function handles the items contained in the queue.

function mymodule_cron_queue_info() {
  $queues['mymodule_update_node_popularity'] = array(
    'worker callback' =&gt; 'mymodule_update_node_popularity', 
    'time' =&gt; 20,
  );

  return $queues;
}


For how to populate that queue, I see two possible cases:


The field is updated for every node that is updated
The field is updated for every node, once
The field is updated for every node that is created, or updated


In all the cases, the following function should be used, together the hook I previously shown.

function mymodule_update_node_popularity($nid) {
  $node = node_load($nid);

  // Calculate the new score for the node.

  $node-&gt;field_popularity_score[LANGUAGE_NONE][0]['value'] = $new_value;
  // Replace the previous line with the following one, if the value should be different for each language.
  // $node-&gt;field_popularity_score[field_language('node', $node, 'field_popularity_score')][0]['value'] = $new_value;

  node_save($node);
}


The parameter received from the worker callback (in this case, mymodule_update_node_popularity()) is the one that is passed to $queue->createItem().  

In the first case, I would implement the following code.

function mymodule_node_update($node) {
  if ($node-&gt;type == 'content type to handle') {
    $queue = DrupalQueue::get('mymodule_update_node_popularity');
    $queue-&gt;createItem($node-&gt;nid);
  }
}


In the second case, I would implement the following code.

function mymodule_cron() {
  $args = array(
    ':type' =&gt; 'content type to check',
    ':created' =&gt; variable_get('mymodule_last_checked', 0), 
  );
  $queue = DrupalQueue::get('mymodule_update_node_popularity');

  $nodes_result = db_query('SELECT nid, created FROM {node} WHERE type = :type AND created &gt;= :created ORDER BY created ASC', $args);
  foreach ($nodes_result as $node) {
    $queue-&gt;createItem($node-&gt;nid);
    variable_set('mymodule_last_checked', $node-&gt;created);
  }
}


The reason I am not using "SELECT nid FROM {node} WHERE type = :type" as query is that such query would cause mymodule_cron() to always add the same nodes to the queue (except for those nodes that have been deleted). mymodule_cron() needs a way to filter out the nodes that have been already handled, and the query I wrote is a way to achieve that.
The value passed to $queue-&gt;createItem() can also be an array, or an object, and that value would be passed to the worker callback.

In the third case, I would use the following code.

function mymodule_cron() {
  $args = array(
    ':type' =&gt; 'content type to check',
    ':changed' =&gt; variable_get('mymodule_last_checked', 0), 
  );
  $queue = DrupalQueue::get('mymodule_update_node_popularity');

  $nodes_result = db_query('SELECT nid, changed FROM {node} WHERE type = :type AND changed &gt;= :changed ORDER BY changed ASC', $args);
  foreach ($nodes_result as $node) {
    $queue-&gt;createItem($node-&gt;nid);
    variable_set('mymodule_last_checked', $node-&gt;changed);
  }
}


This code works because, when a node is first created, both $node-&gt;created, and $node-&gt;changed are set to the same value. 

In the first case, when you need to update the field value when the node is updated, you could implement hook_field_presave(), instead of using a Drupal queue.