sauvegarde rapide d'une valeur de champ unique

19

J'ai environ 70 000 nœuds de type spécifié sur mon site. Je dois exécuter une mise à jour sur eux. Certaines opérations et la définition d'un champ à la valeur souhaitée. node_saveest vraiment lent et il provoque des plantages (pile d'appel trop longue peut-être). Existe-t-il un moyen plus rapide d'écrire des informations sur ce domaine particulier?

Il a été field_attach_updatementionné dans un article, mais ce n'est pas beaucoup plus rapide.

EDIT: Il existe une vue assez complexe construite sur ce type de nœud, mais ne fonctionne pas sur ce champ que je souhaite mettre à jour.

Eloar
la source

Réponses:

30

J'irais certainement pour field_attach_update.

L'idée est simple. Chargez simplement le nœud et enregistrez-le à l'aide de field_attach_update.

Ex:

$node = node_load($nid);
$node->field_name[LANGUAGE_NONE][0]['value'] = 'New value';
field_attach_presave('node', $node);
field_attach_update('node', $node);
  // Clear the static loading cache.
entity_get_controller('node')->resetCache(array($node->nid));

Cela ne changera aucun horodatage ni aucun autre hook que node_save invoque habituellement. Le chargement du nœud invoquera également certains crochets, donc ce n'est probablement pas si efficace.

Si vous avez le nid et si la structure du nœud est simple, vous pouvez également le faire comme ceci:

 $node = new stdClass();
 $node->nid = $nid; // Enter the nid taken. Make sure it exists. 
 $node->type = 'article';
 $node->field_name[LANGUAGE_NONE][0]['value'] = 'New value';
 field_attach_presave('node', $node);
 field_attach_update('node', $node);
  // Clear the static loading cache.
 entity_get_controller('node')->resetCache(array($node->nid));

Quoi qu'il en soit, si vous essayez de mettre à jour autre chose que des champs, cela ne fonctionnera pas (statut des commentaires, statut publié, etc.). De plus, si vous utilisez node_save, le cache pour le noeud particulier sera effacé automatiquement pour différentes méthodes, nous devons l'effacer avec 'entity_get_controller'.

Mise à jour: Il semble que vous devriez également appeler field_attach_presave()pour permettre aux autres modules de traiter correctement l'entrée de champ. Le module de fichiers, par exemple, l'utilise pour définir l'état du fichier sur permanent à l'aide de ce hook. J'ai mis à jour mes 2 exemples ci-dessus.

AyeshK
la source
J'ai nid mais la structure n'est pas si simple pour le nœud, mais le champ que je veux mettre à jour est très simple. À quoi puis-je m'attendre en définissant un seul champ pour le nœud existant (identifié par nid mais non chargé), puis en appelant field_attach_update?
Eloar
4
Comme il s'est avéré que l'utilisation de la requête d'entité ralentissait les choses. Ainsi , le passage de node_savela field_attach_updateet de EntityFieldQueryla db_query_rangea été très enrichissante. De 3h de mise à jour à 40 minutes.
Eloar
2

Si vous ne souhaitez pas enregistrer les données de champ sans provoquer les événements et actions standard, vous pouvez utiliser drupal_write_record .

Voici un exemple pour insérer Hello World dans le champ de corps d'un noeud de type article avec un id de 1.

$values = array(
  'entity_type' => 'node',
  'bundle' => 'article',
  'entity_id' => 1,
  'revision_id' => 1,
  'language' => 'und',
  'delta' => 0,
  'body_value' => 'HELLO WORLD',
  'body_summary' => '',
  'body_format' => 'filtered_html',
);
drupal_write_record('field_data_body', $values);
drupal_write_record('field_revision_body', $values);

Si votre site est multilingue, vous voudrez utiliser 'en' ou la langue de votre contenu au lieu de 'und'.

Si vous effectuez une révision, vous devrez faire attention à insérer le bon ID de révision, sinon vous pouvez simplement insérer la même valeur que entity_id.

Notez comment ces données sont insérées dans deux tables field_data_ * et field_revision_ *. Vous devez insérer dans les deux pour vous assurer que le site fonctionne comme vous le souhaitez.

Après avoir exécuté cela, vous devrez ensuite vider les caches pour que les champs s'affichent en fonction de la configuration de votre mise en cache.

Thomas4019
la source
2

Pour une simple mise à jour comme celle-ci où de nombreux nœuds doivent être mis à jour, j'utilise toujours une instruction de mise à jour MySQL. Oui, la mise en cache doit être prise en considération, mais vous pouvez simplement vider le cache après avoir terminé et tout va bien. Bien sûr, vous devez être familier avec la structure des données, mais elle est relativement simple dans Drupal 6. (bien qu'horrible dans Drupal 7)

Troy Frech
la source
Le projet a été créé pour Drupal 7. Après que toutes les parties de mon module ont été faites pour manipuler directement sur la structure de la base de données Drupal pour la lecture des valeurs et la recherche car les requêtes SQL étaient beaucoup plus rapides que EntityFieldQueries.
Eloar
2

Je suggère field_attach_updateaussi, et pas une requête SQL directe, car sql ne met pas à jour l'objet de cache de noeud, et dans votre prochain node_loadvous ne chargerez pas la valeur de champ mise à jour, vous chargerez l'ancienne valeur

field_attach_update est bien meilleur qu'une requête SQL directe.

pico34
la source
Ayesh K a suggéré de créer un nœud en tant stdClassqu'objet sans chargement. Savez-vous ce qui pourrait arriver si j'essaie de mettre à jour le nœud de cette manière sans définir tous les champs? Seront-ils remplacés par des valeurs nulles ou par défaut? Peut-on ignorer ceux-ci en mettant à jour le processus?
Eloar
1
Il est possible d'enregistrer un nœud directement par la méthode Ayeshs (nouvelle stdClass etc ...) seule l'interface utilisateur fait des validations sur les champs obligatoires
pico34
Je vais essayer de mettre à jour le nœud avec cette méthode sans définir tous les champs et vérifier ce qui se passera. Ce pourrait être la méthode la plus rapide pour mettre à jour les nœuds dans la procédure de mise à jour du module (batch).
Eloar
2

Après avoir essayé toutes les approches mentionnées dans les autres réponses, j'ai eu des temps de mise à jour très lents (environ 7 jours pour 700 000 nœuds d'un type de nœud avec plus de 20 champs) jusqu'à ce que je trouve cet article: http://www.drupalonwindows.com/en/ blog / uniquement-mise à jour-champs-modifiés-ou-propriétés-entité-drupal .

Après avoir implémenté quelque chose comme le code ci-dessous dans un hook_update, j'ai réduit le temps de mise à jour à 2 heures, ce qui, je pense, est gérable.

if (!isset($sandbox['storage']['nids'])) {
    $sandbox['storage']['nids'] = [];
    $query = 'SELECT {nid} FROM node WHERE type = \'article\';';
    $result = db_query($query)->fetchCol();
    if ($result) {
      $sandbox['storage']['nids'] = $result;
      $sandbox['storage']['total'] = count($sandbox['storage']['nids']);
      $sandbox['storage']['last_run_time'] = time();
      $sandbox['progress'] = 0;
    }
  }

  $amount = 300;
  $nids = array_slice($sandbox['storage']['nids'], 0, $amount);

  if (!empty($nids)) {
    $nodes = node_load_multiple($nids, [], TRUE);
    foreach ($nodes as $node) {
      // Lets manipualte the entity.
      $article_wrapper = UtilsEntity::entity_metadata_wrapper('node', $node);
      // Eventual logic here.

        // Field to update
        $article_wrapper->my_field = 'my_value';
        $article_wrapper->save();

    $sandbox['progress']++;
    }
    $sandbox['message'] = 'Runs left: ' . (($sandbox['storage']['total'] - $sandbox['progress'])/$amount) . ' Progress: ' . (($sandbox['progress'] * $amount)/$sandbox['storage']['total']) . '%';
    $sandbox['storage']['last_run_time'] = time();
    unset($nids);
  }
  $sandbox['storage']['nids'] = array_slice($sandbox['storage']['nids'], 100, count($sandbox['storage']['nids']));
  if (!empty($sandbox['storage']['total'])) {
    $sandbox['#finished'] = ($sandbox['storage']['total'] - count($sandbox['storage']['nids'])) / $sandbox['storage']['total'];
  }
  return $sandbox['message'];
dasj19
la source
1

J'avais même la même exigence de mettre à jour un champ pour tous les nœuds d'un type de contenu particulier. J'ai utilisé node_load_multiple et field_attach_update .

$nodes = node_load_multiple(array(), array('type' => 'content_type_name'));
foreach ($nodes as $node) {
  $node->field_name['und'][0]['value'] = 'field value';
  field_attach_update('node', $node);
}

Je l'ai couru par drush et c'était assez rapide.

Suresh R
la source
0

Avez-vous envisagé de faire ces mises à jour directement dans la base de données à l'aide de mySQL? C'est probablement le moyen le plus simple et le plus rapide pour réaliser ce que vous voulez.

Voici un exemple simple. Vous pouvez exécuter une telle commande à partir de l'onglet 'SQL' de phpMyAdmin. Imaginez que vous ayez un type de contenu appelé Profil de membre. Vous y trouverez un champ nommé «Type de membre» (par exemple, entreprise, particulier, organisation). Supposons que vous souhaitiez mettre à jour toutes les occurrences de «COMPANY» vers «company». La commande suivante fera exactement cela.

UPDATE content_type_member_profile SET field_type_of_member_value= 'company' WHERE field_type_of_member_value= 'COMPANY';

En outre, passez à la section Prise en main de MySQL

Bisonbleu
la source
C'est une des options. Je le laisse comme dernier pour vérifier et mettre en œuvre. Je ne veux pas trop gâcher la structure et les défauts de Drupal. Donc, je cherche d'abord des solutions directement via l'API Drupal. Si vous pouviez fournir quelques exemples, ce serait très apprécié.
Eloar
Point de goog. J'ai ajouté un exemple simple à ma réponse initiale.
Bisonbleu
eh, je connais assez bien SQL pour mettre à jour tous les enregistrements dans toutes les tables. Ce n'est pas le problème. Le problème est que je ne connais pas assez la façon dont drupal stocke les données (en particulier les métadonnées). Il y a toujours beaucoup de mise en cache, de préservation, etc. dans de tels systèmes, donc la mettre à jour au niveau le plus bas peut (pas toujours) gâcher quelques défauts. Donc, si cela arrive, j'essaierai de le faire au niveau le plus bas, et je serai prêt pour un vrai gâchis.
Eloar