Index: feeds.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/feeds.install,v
retrieving revision 1.4
diff -u -p -r1.4 feeds.install
--- feeds.install	20 Oct 2009 20:59:04 -0000	1.4
+++ feeds.install	18 Nov 2009 23:37:04 -0000
@@ -164,6 +164,13 @@ function feeds_schema() {
         'not null' => TRUE,
         'description' => t('Unique identifier for the feed item.'),
       ),
+      'hash' => array(
+        'type' => 'varchar',
+        'length' => 32, //The length of the output of MD5
+        'not null' => TRUE,
+        'default' => '',
+        'description' => t('The hash of the item.'),
+      ),
     ),
     'primary key' => array('nid'),
     'indexes' => array(
@@ -291,4 +298,22 @@ function feeds_update_6006() {
   db_add_index($ret, 'feeds_schedule', 'feed_nid', array('feed_nid'));
 
   return $ret;
+}
+
+/**
+ * Add hash column to feeds_node_item.
+ */
+function feeds_update_6007() {
+  $ret = array();
+
+  $spec = array(
+    'type' => 'varchar',
+    'length' => 32, //The length of the output of md5()
+    'not null' => TRUE,
+    'default' => '',
+    'description' => t('The hash of the item.'),
+  );
+  db_add_field($ret, 'feeds_node_item', 'hash', $spec);
+  
+  return $ret;
 }
\ No newline at end of file
Index: plugins/FeedsNodeProcessor.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/feeds/plugins/FeedsNodeProcessor.inc,v
retrieving revision 1.15
diff -u -p -r1.15 FeedsNodeProcessor.inc
--- plugins/FeedsNodeProcessor.inc	18 Nov 2009 16:53:48 -0000	1.15
+++ plugins/FeedsNodeProcessor.inc	18 Nov 2009 23:37:04 -0000
@@ -17,36 +17,56 @@ class FeedsNodeProcessor extends FeedsPr
   public function process(FeedsParserResult $parserResult, FeedsSource $source) {
 
     // Count number of created and updated nodes.
-    $created  = $updated = 0;
+    $created = $updated = 0;
 
     foreach ($parserResult->value['items'] as $item) {
       // If the target item does not exist OR if update_existing is enabled,
       // map and save.
       if (!($nid = $this->existingItemId($item, $source)) || $this->config['update_existing']) {
-
-        // Map item to a node.
-        $node = $this->map($item);
-
-        // Add some default information.
-        $node->feeds_node_item->id = $this->id;
-        $node->feeds_node_item->imported = FEEDS_REQUEST_TIME;
-        $node->feeds_node_item->feed_nid = $source->feed_nid;
-
+        $hash = $this->hash($item);
+        $node = new stdClass();
+        
         // If updating populate nid and vid avoiding an expensive node_load().
         if (!empty($nid)) {
+          if ($hash == $this->getHash($nid)) {
+            // No change, nothing to do.
+            continue;
+          }
           $node->nid = $nid;
           $node->vid = db_result(db_query('SELECT vid FROM {node} WHERE nid = %d', $nid));
-        }
-
-        // Save the node.
-        node_save($node);
-
-        if ($nid) {
           $updated++;
         }
         else {
           $created++;
         }
+        
+        $node->type = $this->config['content_type'];
+        $node->feeds_node_item = new stdClass();
+        $node->feeds_node_item->hash = $hash;
+         
+        // Prepare node object.
+        static $included;
+        if (!$included) {
+          module_load_include('inc', 'node', 'node.pages');
+          $included = TRUE;
+        }
+        node_object_prepare($node);
+        $node->log = 'Created/updated by FeedsNodeProcessor';
+        
+        // Execute mappings from $item to $node.
+        $this->map($item, $node);
+        
+        // Add some default information.
+        $node->feeds_node_item->id = $this->id;
+        $node->feeds_node_item->imported = FEEDS_REQUEST_TIME;
+        $node->feeds_node_item->feed_nid = $source->feed_nid;
+        
+        // Assign an aggregated node always to anonymous.
+        // @todo: change to feed node uid to keep in line with feedapi?
+        $node->uid = 0;
+
+        // Save the node.
+        node_save($node);
       }
     }
 
@@ -108,22 +128,7 @@ class FeedsNodeProcessor extends FeedsPr
   /**
    * Execute mapping on an item.
    */
-  protected function map($source_item) {
-    // Prepare node object.
-    static $included;
-    if (!$included) {
-      module_load_include('inc', 'node', 'node.pages');
-      $included = TRUE;
-    }
-    $target_node = new stdClass();
-    $target_node->type = $this->config['content_type'];
-    $target_node->feeds_node_item = new stdClass();
-    node_object_prepare($target_node);
-    // Assign an aggregated node always to anonymous.
-    // @todo: change to feed node uid to keep in line with feedapi.
-    $target_node->uid = 0;
-    $target_node->log = 'Created/updated by FeedsNodeProcessor';
-
+  protected function map($source_item, $target_node) {
     // Load all mappers. parent::map() might invoke their callbacks.
     self::loadMappers();
 
@@ -316,6 +321,29 @@ class FeedsNodeProcessor extends FeedsPr
     }
     $loaded = TRUE;
   }
+  
+  /**
+   * Create MD5 hash of $item array.
+   * @return Always returns a hash, even with empty, NULL, FALSE:
+   *  Empty arrays return 40cd750bba9870f18aada2478b24840a
+   *  Empty/NULL/FALSE strings return d41d8cd98f00b204e9800998ecf8427e
+   */
+  protected function hash($item) {
+    return hash('md5', serialize($item));
+  }
+  
+  /**
+   * Retrieve MD5 hash of $nid from DB.
+   * @return Empty string if no item is found, hash otherwise.
+   */
+  protected function getHash($nid) {
+    $hash = db_result(db_query('SELECT hash FROM {feeds_node_item} WHERE nid = %d', $nid));
+    if ($hash) {
+      // Return with the hash.
+      return $hash;
+    }
+    return '';
+  }
 }
 
 /**
@@ -343,4 +371,4 @@ function _feeds_node_delete($nid) {
   }
   watchdog('content', '@type: deleted %title.', array('@type' => $node->type, '%title' => $node->title));
   drupal_set_message(t('@type %title has been deleted.', array('@type' => node_get_types('name', $node), '%title' => $node->title)));
-}
\ No newline at end of file
+}
