0

I want to update some term id on some node (let suppose 1000 node),My changes is convert all news node with category(term_id) = 3040 to category(term_id)=5).

I know I can do it with several methods( use views bulk operation, write batch running etc, .... ) but my question is about performance of below approaches

  1. Approach based on old method ( node_save)

    $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', 'node')
            ->entityCondition('bundle', 'news')
            ->propertyCondition('status', 1)
            ->fieldCondition('field_category', 'tid', array('3040'));
    $result = $query->execute();
    $nids= array_keys($result['node']);
    //ym($nids);
    
    $nodes= node_load_multiple($nids);
    foreach ($nodes as $node){
        foreach($node->field_category['und'] as $i=>$item){
           if($item['tid']==3040){
                $node->field_category['und'][$i]['tid']=5;
                node_save($node);
            }
        }
    
    }
    
  2. Approach based on entity wrapper method

        $query = new EntityFieldQuery();
        $query->entityCondition('entity_type', 'node')
                ->entityCondition('bundle', 'news')
                ->propertyCondition('status', 1)
                ->fieldCondition('field_category', 'tid', array('3040'));
        $result = $query->execute();
    foreach($result['node'] as $nid => $stub_node) {
      // Load the full node and wrap it with entity_metadata_wrapper().
      $node = node_load($nid);
      $wrapped_node = entity_metadata_wrapper("node", $node);
      if ($wrapped_node->field_category->value()->tid == 3040) {
        $wrapped_node->field_category->tid->set(5);
        $wrapped_node->save();
      }
    

in first approach we have one node_load and one node_save but in second approach we have one node_load one creating entity metawrapper creation and one entity wrapper save. I want to know which method has better performance?
let suppose we have the same situation for both 2 method, I just want somebody tell me ( take a benchmark on one node and in bulk ( multi node) mode and tell me the performance of both that which one of this method is faster in the same situation.
please provide your answer with facts or benchmark testing result

  • Rolled back to original question - please don't substantially change questions once they've been answered. Thanks – Clive Jan 16 '16 at 23:20
  • @Clive I have a mistake in question, load node multiple and one by one and it make my question to suitable,So I change my question to manner that the situation on loading be same and with this manner others can has better answers,What is your idea Master Clive, Do u think which method is faster? – Yusef Mohamadi Jan 17 '16 at 6:06
  • 1
    You need to run the benchmarks yourself, it's very unlikely you'll convince someone to do that work for you. It would be pointless anyway, an answer could only make sense if a benchmark was run on the exact environment the site in question is running on. Then it comes down to what modules are installed, how everything is configured, and what resources are available. On shared hosting it would be even more impossible to predict, and probably even more pointless to try – Clive Jan 17 '16 at 10:51
6

This is essentially impossible to answer. You have one part of your question, wrong

  • Your first method uses a single node_load_multiple() with multiple node_save
  • Your second answer uses multiple node_load() with multiple Entity API calls

My gut tells me the first will be quicker, as a single node_load_multiple() is typically more performant than multiple node_load() calls, given the same set of nodes. You also aren't creating the the metadata wrapper objects.

However, you need to do the benchmarking on your system. There may be a difference, but it may be small enough to not make a real imapact. Some things that make cross-system benchmarks unreliable

  • entitycache can have a tremendous impact on a system, especially if you use a non-SQL backend for it
  • the number of fields on your node can effect how long the wrapper creation process takes, and also how long the node_save() takes
  • APC and/or OpCache usage and configuration
  • PHP version
  • MySQL configuration (or whatever database backend you are using), including some of the Drupal-specific tricks that can improve performance
  • The number of hook_node_update() instances that fire for each save
  • ok, I change my question that both use one method and strategy. – Yusef Mohamadi Jan 16 '16 at 19:49
  • I update my question and make it easier to answer about it. – Yusef Mohamadi Jan 16 '16 at 19:54
  • 1
    Normally, a substantial edit to a question like this after answers isn't really allowed. I will let another moderator make the call here since one of the answers is mine. However, my answer stands. There are too many other factors at play. You need to benchmark this on your system to see how much of a difference this makes. – mpdonadio Jan 16 '16 at 21:24
  • I am sorry about make some change in my question, it because I found there is not the same situation, tnx for your attention, but I ask, if we suppose we are in a common shared host with default and equal situation which method is fast. – Yusef Mohamadi Jan 17 '16 at 6:08
  • As far as I understand, EntityMetadataWrapper::save() somewhere eventually just calls node_save() anyways, but the additional memory and CPU overhead of using the entity wrapper is probably not significant for most use cases. If you are hitting timeout or memory limits, it would be best to use Batch API. – gapple Jan 25 '16 at 20:19
3

Given that performance is an issue you may alter the code of your first approach (1) like this :

$field_name = 'field_category';
$tid = '3040';
$nids = db_query("SELECT nid FROM {node} n, field_data_{$field_name} c WHERE n.nid = c.entity_id AND c.bundle = 'blog_post' AND c.{$field_name}_tid = :tid", array(':tid' => $tid))->fetchCol();
$nodes = node_load_multiple($nids);
foreach ($nodes as $node) {
  if ($node->{$field_name}[LANGUAGE_NONE][0]['tid'] == 3040) {
    $node->{$field_name}[LANGUAGE_NONE][0]['tid'] = 5;
    field_attach_update('node', $node); // Instead of node_load
  }
}

What I did was to (1) change the use of EntityFieldQuery() with db_query() by the time it is much faster. Moreover (2) I changed the node_save with the field_attach_update since there is no need to save the whole node (ref).

From drupal.org: Static queries vs Dynamic queries

I hope that helps!

  • 1
    tnx for your attention, but I just need compare between 2 method( I know I can do them with better solution) – Yusef Mohamadi Jan 16 '16 at 21:02
  • 3
    Two notes. In general queries against the node table should be an EFQ or db_select('node')->addTag('node_access') to prevent any access bypass problems. However, there are cases where db_query is OK (eg, admin only touchup scripts). And, the node hooks don't fire and the node access tables don't get updated with field_attach_update. Again, this may or may not be a problem, depending on the situation. If you have grants that use the term data, then it is a problem and would require a manual permission rebuild. – mpdonadio Jan 16 '16 at 21:30
  • 1
    Thus would be the fastest, and may only take a few seconds at worst only. However, as MPD mentioned node access and grants, you will need to rebuild permissions. Furthermore, taxonomy module implements node_update hook to update its index, which field_update_* isn't doing. You'll have to manually rebuild taxonomy index at least. – AyeshK Jan 17 '16 at 8:54
  • I thought that the question was about performance on how to update a node's field, possibly through a script. I didn't knew that node access, grants, rebuild permissions and indexes also had to be updated,I guess it depends on the use case like MPD said. Frankly, I didn't even knew that field_attach_update don't updates those things.Still I learned something new, thanks! – mchar Jan 17 '16 at 9:49
  • @zhilevan This code (if you don't know already) is a reliable execution time measurement: $mtime = microtime(); $mtime = explode(" ", $mtime); $mtime = $mtime[1] + $mtime[0]; $starttime = $mtime; // Code goes here $mtime = microtime(); $mtime = explode(" ", $mtime); $mtime = $mtime[1] + $mtime[0]; $endtime = $mtime; $totaltime = ($endtime - $starttime); Perhaps will help! – mchar Jan 17 '16 at 10:06
-1

After some research and some benchmark I get below results

In equal and regular situation using Entity Meta Wrapper for change the node fileds has better performance.

Loading the entity object just for passing into EMW. Unless you already have the pre-populated entity, it's more efficient to just pass the entity id to entity_metadata_wrapper(); EMW will lazy load the field and property data that it needs, hoorah! So much magic.

(if we have $node it is better use it but if we don't have it Don't use node_load to load it before pass it to entity_metadata_wrapper and just pass nid to it has better performance rather first load node then pass it to EMW

// Unnecessary node load... $node = node_load($nid); 
$wrapper = entity_metadata_wrapper('node', $node); // More efficient       
(if the object isn't handed to you)... 
$wrapper = entity_metadata_wrapper('node', $nid)

Saving the entity with node_save(), taxonomy_term_save(), etc. instead of the EMW save() method. Fun fact: both EMW and standard entity controllers include save() methods, so you can call e.g. $node->save() on properly instantiated entity objects and wrappers.

// If you don't already have the node object, don't bother loading it just to save... 
$wrapper->field_foobar = 'new value'; 
$node = $wrapper->value(); 
node_save($node); // Easier and prettier... 
$wrapper->field_foobar = 'new value';
$wrapper->save();

Reference: Death to Field Arrays

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.