valore di campo singolo risparmio veloce


19

Ho circa 70k nodi del tipo specificato sul mio sito. Devo eseguire un aggiornamento su di essi. Alcune operazioni e impostazione di un campo sul valore desiderato. node_saveè veramente lento e causa arresti anomali (troppo lungo callstack mayby). C'è un modo più veloce per scrivere informazioni su questo particolare campo?

È stato field_attach_updatemenzionato in un post, ma non è molto più veloce.

EDIT: c'è una vista abbastanza complessa costruita su questo tipo di nodo, ma non funziona su questo campo che voglio aggiornare.

Risposte:


30

Ritornerei sicuramente field_attach_update.

L'idea è semplice Basta caricare il nodo e salvarlo utilizzando 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));

Questo non cambierà alcun timestamp o qualsiasi altro hook che node_save solitamente invoca. Il caricamento del nodo invocherà anche alcuni hook, quindi probabilmente non è così efficiente.

Se hai il nid e se la struttura del nodo è semplice, puoi farlo anche in questo modo:

 $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));

Ad ogni modo, se stai provando ad aggiornare qualcosa di diverso dai campi, questo non funzionerà (stato dei commenti, stato pubblicato, ecc.). Inoltre, se si utilizza node_save, la cache per il nodo particolare verrà cancellata automaticamente per i diversi metodi che è necessario cancellare con 'entity_get_controller'.

Aggiornamento: sembra che dovresti anche chiamare field_attach_presave()per consentire ad altri moduli di elaborare correttamente l'input di campo. Il modulo file, ad esempio, lo utilizza per impostare lo stato del file su permanente utilizzando questo hook. Ho aggiornato i miei 2 esempi sopra.


Ho nid ma la struttura non è così semplice per il nodo, ma il campo che voglio aggiornare è semplicissimo. Cosa potrei aspettarmi dall'impostazione di un solo campo per il nodo esistente (identificato da nid ma non caricato) e quindi dalla chiamata field_attach_update?
Eloar

4
Come si è scoperto, l'uso della query delle entità ha rallentato le cose. Quindi, il passaggio da node_savea field_attach_updatee da EntityFieldQuerya db_query_rangeè stato molto gratificante. Dall'aggiornamento 3h a 40 minuti.
Eloar

2

Se non si desidera salvare i dati dei campi senza causare eventi e azioni standard, è possibile utilizzare drupal_write_record .

Ecco un esempio per inserire Hello World nel campo body per un nodo di tipo articolo con un ID di 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);

Se il tuo sito è multilingue, ti consigliamo di utilizzare "en" o la lingua dei tuoi contenuti anziché "und".

Se stai eseguendo la revisione, dovrai fare attenzione a inserire l'id di revisione corretto, altrimenti puoi semplicemente inserire lo stesso valore di entity_id.

Nota come questi dati vengono inseriti in due tabelle field_data_ * e field_revision_ *. È necessario inserire in entrambi per assicurarsi che il sito funzioni come desiderato.

Dopo averlo eseguito, dovrai quindi svuotare le cache affinché i campi vengano visualizzati in base alla configurazione della cache.


2

Per un semplice aggiornamento come questo in cui è necessario aggiornare molti nodi, utilizzo sempre un'istruzione di aggiornamento MySQL. Sì, la cache deve essere presa in considerazione, ma puoi semplicemente svuotare la cache dopo aver finito ed è tutto a posto. Naturalmente, devi avere familiarità con la struttura dei dati, ma è relativamente semplice in Drupal 6. (anche se orrendo in Drupal 7)


Il progetto è stato creato per Drupal 7. Dopo che tutte le parti del mio modulo sono state create per manipolare direttamente la struttura del DB Drupal per leggere i valori e cercare poiché le query sql erano molto più veloci di EntityFieldQueries.
Eloar,

2

Suggerisco field_attach_updateanche, e non una query SQL diretta, perché sql non aggiorna l'oggetto cache del nodo e nel prossimo node_loadnon caricherete il valore del campo aggiornato, caricherete il vecchio valore

field_attach_update è molto meglio della query SQL diretta.


Ayesh K ha suggerito di creare un nodo come stdClassoggetto senza caricare. Sai cosa potrebbe accadere se provassi ad aggiornare il nodo in questo modo senza impostare tutti i campi? Saranno sovrascritti con valori nulli o predefiniti? Forse quelli verrebbero ignorati dal processo di aggiornamento?
Eloar

1
È possibile salvare un nodo direttamente con il metodo Ayeshs (nuova stdClass ecc ...) solo l'interfaccia utente effettua convalide sui campi obbligatori
pico34

Proverò ad aggiornare il nodo con questo metodo senza impostare tutti i campi e controllare cosa accadrà. Potrebbe essere il metodo più rapido per l'aggiornamento dei nodi nella procedura di aggiornamento del modulo (batch).
Eloar

2

Dopo aver provato tutti gli approcci citati nelle altre risposte, ho avuto tempi di aggiornamento molto lenti (circa 7 giorni per 700.000 nodi di un tipo di nodo con oltre 20 campi) fino a quando non ho trovato questo articolo: http://www.drupalonwindows.com/en/ blog / only-update-cambiato-campi-o-proprietà-entità-drupal .

Dopo aver implementato qualcosa come il seguente codice in un hook_update ho ridotto il tempo di aggiornamento a 2 ore, che penso sia gestibile.

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'];

1

Avevo persino lo stesso requisito di aggiornare un campo per tutti i nodi di un determinato tipo di contenuto. Ho usato node_load_multiple e 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);
}

L'ho passato attraverso la droga ed è stato abbastanza veloce.


0

Hai pensato di fare questi aggiornamenti direttamente nel database usando mySQL? È probabilmente il modo più semplice e veloce per ottenere ciò che desideri.

Qui c'è un semplice esempio. È possibile eseguire tale comando dalla scheda "SQL" in phpMyAdmin. Immagina di avere un tipo di contenuto chiamato Profilo membro. In esso è presente un campo denominato "Tipo di membro" (ad es. Società, individuo, organizzazione). Supponiamo che tu voglia aggiornare tutte le occorrenze di "AZIENDA" a "Azienda". Il seguente comando farà proprio questo.

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

Inoltre, controlla Inizia con MySQL


È una delle opzioni. Lascio che sia l'ultimo a controllare e implementare. Non voglio rovinare troppo la struttura drupal e i difetti. Quindi prima cerco qualsiasi soluzione direttamente tramite l'API Drupal. Se potessi fornire alcuni esempi, sarebbe molto apprezzato.
Eloar

Punto goog. Ho aggiunto un semplice esempio alla mia risposta iniziale.
Bisonbleu,

eh, conosco SQL abbastanza bene per aggiornare qualsiasi record in qualsiasi tabella. Non è il problema. Il problema è che non ho abbastanza familiarità con il modo in cui drupal memorizza i dati (specialmente i metadati). C'è sempre molta cache, conservazione e così via in tali sistemi, quindi aggiornarlo al livello più basso potrebbe (non sempre) rovinare alcuni difetti. Quindi, se arrivasse a questo, proverò a farlo al livello più basso e sarò pronto per un vero casino.
Eloar
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.