Schnell speichernder Einzelfeldwert

19

Auf meiner Website befinden sich ungefähr 70.000 Knoten des angegebenen Typs. Ich muss ein Update für sie ausführen. Einige Operationen und Einstellen eines Felds auf den gewünschten Wert. node_saveist sehr langsam und führt zu Abstürzen (zu langer Callstack kann passieren). Gibt es eine schnellere Möglichkeit, Informationen zu diesem einen bestimmten Feld zu schreiben?

Es wurde field_attach_updatein einem Beitrag erwähnt, aber es ist nicht viel schneller.

BEARBEITEN: Auf diesem Knotentyp ist eine recht komplexe Ansicht aufgebaut, die jedoch in diesem Feld, das ich aktualisieren möchte, nicht funktioniert.

Eloar
quelle

Antworten:

30

Ich würde auf jeden Fall gehen field_attach_update.

Die Idee ist einfach. Laden Sie einfach den Knoten und speichern Sie ihn mit 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));

Dies ändert weder einen Zeitstempel noch einen anderen Hook, den node_save normalerweise aufruft. Das Laden des Knotens ruft auch einige Hooks auf, so dass es wahrscheinlich nicht so effizient ist.

Wenn Sie die NID haben und die Knotenstruktur sehr einfach ist, können Sie dies auch so tun:

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

Wie auch immer, wenn Sie versuchen, etwas anderes als Felder zu aktualisieren, funktioniert dies nicht (Kommentarstatus, Veröffentlichungsstatus usw.). Wenn Sie node_save verwenden, wird der Cache für den jeweiligen Knoten automatisch für verschiedene Methoden gelöscht, die wir zum Löschen mit 'entity_get_controller' benötigen.

Update: Sie sollten anscheinend auch anrufen field_attach_presave(), damit andere Module die Feldeingabe ordnungsgemäß verarbeiten. Das Dateimodul verwendet es beispielsweise, um den Dateistatus mithilfe dieses Hooks auf permanent zu setzen. Ich habe meine 2 obigen Beispiele aktualisiert.

AyeshK
quelle
Ich habe nid, aber die Struktur ist für node nicht so einfach, aber das Feld, das ich aktualisieren möchte, ist denkbar einfach. Was könnte ich erwarten, wenn ich nur ein Feld für einen vorhandenen Knoten setze (identifiziert durch nid, aber nicht geladen) und dann aufrufe field_attach_update?
Eloar
4
Wie sich herausstellte, verlangsamte die Verwendung der Entity-Abfrage die Entwicklung. So von einem Wechsel node_savezu field_attach_updateund von EntityFieldQueryzu db_query_rangewar sehr lohnend. Von 3h Update auf 40 Minuten.
Eloar
2

Wenn Sie keine Felddaten speichern möchten, ohne dass die Standardereignisse und -aktionen ausgeführt werden, können Sie drupal_write_record verwenden .

Hier ist ein Beispiel zum Einfügen von Hello World in das Textfeld für einen Artikelknoten mit der ID 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);

Wenn Ihre Site mehrsprachig ist, möchten Sie 'en' oder die Sprache Ihres Inhalts anstelle von 'und' verwenden.

Wenn Sie eine Revision durchführen, müssen Sie darauf achten, die richtige Revisions-ID einzufügen, andernfalls können Sie einfach denselben Wert wie die entity_id einfügen.

Beachten Sie, wie diese Daten in zwei Tabellen field_data_ * und field_revision_ * eingefügt werden. Sie sollten in beide einfügen, um sicherzustellen, dass die Site wie gewünscht funktioniert.

Nachdem Sie dies ausgeführt haben, müssen Sie die Caches löschen, damit die Felder angezeigt werden, je nachdem, wie Ihr Caching eingerichtet ist.

Thomas4019
quelle
2

Für ein einfaches Update wie dieses, bei dem viele Knoten aktualisiert werden müssen, verwende ich immer eine MySQL-Update-Anweisung. Ja, das Zwischenspeichern muss berücksichtigt werden, aber Sie können den Cache einfach leeren, nachdem Sie fertig sind, und alles ist in Ordnung. Natürlich müssen Sie mit der Datenstruktur vertraut sein, aber sie ist in Drupal 6 relativ einfach (obwohl in Drupal 7 entsetzlich).

Troy Frech
quelle
Das Projekt wurde für Drupal 7 erstellt. Nachdem alle Teile meines Moduls direkt in der Drupal-DB-Struktur bearbeitet wurden, um Werte zu lesen und zu suchen, waren SQL-Abfragen viel schneller als EntityFieldQueries.
Eloar
2

Ich schlage field_attach_updateauch und keine direkte SQL-Abfrage vor, da SQL das Node-Cache-Objekt nicht aktualisiert und Sie in Ihrem nächsten node_loadnicht den aktualisierten Feldwert laden, werden Sie den alten Wert laden

field_attach_update ist viel besser als direkte SQL-Abfrage.

pico34
quelle
Ayesh K schlug vor, einen Knoten als stdClassObjekt zu erstellen, ohne ihn zu laden. Wissen Sie, was passieren kann, wenn ich versuche, den Knoten auf diese Weise zu aktualisieren, ohne alle Felder festzulegen? Werden diese mit Nullen oder Standardwerten überschrieben? Könnten diese durch den Update-Vorgang ignoriert werden?
Eloar
1
Es ist möglich, einen Knoten direkt mit der Ayeshs-Methode (new stdClass etc ...) zu speichern. Nur die Benutzeroberfläche
überprüft
Ich werde versuchen, den Knoten mit dieser Methode zu aktualisieren, ohne alle Felder zu setzen, und prüfen, was passieren wird. Dies ist möglicherweise die schnellste Methode zum Aktualisieren von Knoten in der Modulaktualisierungsprozedur (Batch).
Eloar
2

Nachdem ich alle Ansätze ausprobiert hatte, die in den anderen Antworten erwähnt wurden, kam es zu sehr langsamen Aktualisierungszeiten (ca. 7 Tage für 700.000 Knoten eines Knotentyps mit mehr als 20 Feldern), bis ich diesen Artikel fand: http://www.drupalonwindows.com/en/ blog / only-update-changed-fields-or-properties-entity-drupal .

Nachdem ich so etwas wie den folgenden Code in ein hook_update implementiert hatte, reduzierte ich die Aktualisierungszeit auf 2 Stunden, was ich für überschaubar halte.

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
quelle
1

Ich hatte sogar die gleiche Anforderung, ein Feld für alle Knoten eines bestimmten Inhaltstyps zu aktualisieren. Ich habe node_load_multiple und field_attach_update verwendet .

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

Ich ließ es durch die Puste laufen und es ging ziemlich schnell.

Suresh R
quelle
0

Haben Sie darüber nachgedacht, diese Aktualisierungen mit mySQL direkt in der Datenbank durchzuführen? Es ist wahrscheinlich der einfachste und schnellste Weg, um das zu erreichen, was Sie wollen.

Hier ist ein einfaches Beispiel. Sie können einen solchen Befehl auf der Registerkarte 'SQL' in phpMyAdmin ausführen. Stellen Sie sich vor, Sie haben einen Inhaltstyp namens Mitgliedsprofil. Darin befindet sich ein Feld mit der Bezeichnung "Art des Mitglieds" (z. B. Firma, Person, Organisation). Angenommen, Sie möchten alle Vorkommen für "UNTERNEHMEN" in "Unternehmen" aktualisieren. Der folgende Befehl erledigt genau das.

UPDATE content_type_member_profile SET field_type_of_member_value= 'firma' WHERE field_type_of_member_value= 'FIRMA';

Check-out Erste Schritte mit MySQL

Bisonbleu
quelle
Dies ist eine der Optionen. Ich überlasse es als letztes zu überprüfen und umzusetzen. Ich möchte die Drupal-Struktur und die Fehler nicht zu sehr durcheinander bringen. Also suche ich zuerst nach Lösungen direkt über die Drupal-API. Wenn Sie einige Beispiele nennen könnten, wären Sie sehr dankbar.
Eloar
Goog Point. Ich habe meiner ersten Antwort ein einfaches Beispiel hinzugefügt.
Bisonbleu
Ich kenne SQL gut genug, um Datensätze in Tabellen zu aktualisieren. Das ist nicht das Problem. Das Problem ist, dass ich nicht genug mit der Art und Weise vertraut bin, wie Drupal Daten speichert (insbesondere Metadaten). In solchen Systemen wird immer viel zwischengespeichert, aufbewahrt und so weiter, sodass die Aktualisierung auf der untersten Ebene (nicht immer) einige Fehler vermasseln kann. Wenn es dazu kommt, werde ich versuchen, es auf der untersten Ebene zu tun, und ich werde bereit sein für ein echtes Durcheinander.
Eloar