szybka oszczędność wartości pojedynczego pola

19

Mam na swojej stronie około 70 000 węzłów określonego typu. Muszę uruchomić na nich aktualizację. Niektóre operacje i ustawienie jednego pola na żądaną wartość. node_savejest naprawdę wolny i powoduje awarie (zbyt długi czas oczekiwania na callstack). Czy istnieje szybszy sposób na zapisanie informacji na temat tego konkretnego pola?

Zostało field_attach_updatewspomniane w jednym poście, ale nie jest to znacznie szybsze.

EDYCJA: Ten typ węzła ma dość złożony widok, ale nie działa na tym polu, które chcę zaktualizować.

Eloar
źródło

Odpowiedzi:

30

Zdecydowanie wybrałbym field_attach_update.

Pomysł jest prosty. Po prostu załaduj węzeł i zapisz go, używając field_attach_update.

Dawny:

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

Nie zmieni to żadnego znacznika czasu ani żadnego innego haka, który zwykle wywołuje save_save. Ładowanie węzła spowoduje również wywołanie niektórych haków, więc prawdopodobnie nie jest to tak wydajne.

Jeśli masz identyfikator nid i struktura węzła jest bardzo prosta, możesz to zrobić w następujący sposób:

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

W każdym razie, jeśli próbujesz zaktualizować cokolwiek innego niż pola, to nie zadziała (status komentarza, status publikacji itp.). Również jeśli używasz node_save, pamięć podręczna dla konkretnego węzła zostanie automatycznie wyczyszczona dla różnych metod, które musimy wyczyścić za pomocą „entity_get_controller”.

Aktualizacja: Wydaje się, że powinieneś także zadzwonić, field_attach_presave()aby umożliwić innym modułom prawidłowe przetwarzanie danych wejściowych w polu. Na przykład moduł pliku używa go do ustawiania statusu pliku na stały za pomocą tego zaczepu. Zaktualizowałem moje 2 przykłady powyżej.

AyeshK
źródło
Mam nid, ale struktura węzła nie jest taka prosta, ale pole, które chcę zaktualizować, jest bardzo proste. Czego mógłbym oczekiwać od ustawienia tylko jednego pola dla istniejącego węzła (identyfikowanego przez nid, ale nie załadowanego), a następnie wywołania field_attach_update?
Eloar,
4
W miarę, jak się kończy, użycie zapytania o jednostkę spowalniało proces. Więc przejście z node_savedo field_attach_updateiz EntityFieldQuerydo db_query_rangebyło dość satysfakcjonujące. Od 3h aktualizacji do 40 minut.
Eloar,
2

Jeśli nie chcesz zapisywać danych pól bez spowodowania wystąpienia standardowych zdarzeń i akcji, możesz użyć drupal_write_record .

Oto przykład wstawienia Hello World w polu body dla węzła typu article o 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);

Jeśli Twoja witryna jest wielojęzyczna, zamiast „und” będziesz chciał użyć „en” lub języka treści.

Jeśli przeprowadzasz weryfikację, musisz uważać, aby wstawić odpowiedni identyfikator wersji, w przeciwnym razie możesz po prostu wstawić tę samą wartość, co identyfikator_obiektu.

Zwróć uwagę, jak te dane są wstawiane do dwóch tabel field_data_ * i field_revision_ *. Powinieneś wstawić do obu, aby upewnić się, że strona działa zgodnie z oczekiwaniami.

Po uruchomieniu musisz wyczyścić pamięć podręczną pól, aby pojawiały się w zależności od konfiguracji buforowania.

Thomas4019
źródło
2

W przypadku prostej aktualizacji, takiej jak ta, w której należy zaktualizować wiele węzłów, zawsze używam instrukcji aktualizacji MySQL. Tak, należy wziąć pod uwagę buforowanie, ale możesz po prostu opróżnić pamięć podręczną po zakończeniu i wszystko jest w porządku. Musisz oczywiście zapoznać się ze strukturą danych, ale w Drupal 6 jest ona stosunkowo prosta (choć przerażająca w Drupal 7)

Troy Frech
źródło
Projekt został stworzony dla Drupal 7. Po tym, jak wszystkie części mojego modułu zostały zmanipulowane bezpośrednio na strukturze DB Drupal w celu odczytu wartości i wyszukiwania, ponieważ zapytania SQL były znacznie szybsze niż EntityFieldQueries.
Eloar,
2

Sugeruję field_attach_updateteż, a nie bezpośrednie zapytanie SQL, ponieważ sql nie aktualizuje obiektu pamięci podręcznej węzła, a przy następnym node_loadnie załadujesz zaktualizowanej wartości pola, załadujesz starą wartość

field_attach_update jest znacznie lepszy niż bezpośrednie zapytanie SQL.

pico34
źródło
Ayesh K zaproponował utworzenie węzła jako stdClassobiektu bez ładowania. Czy wiesz, co może się stać, jeśli spróbuję zaktualizować węzeł w ten sposób bez ustawiania wszystkich pól? Czy zostaną one zastąpione wartościami zerowymi lub domyślnymi? Czy mogą być one zignorowane przez proces aktualizacji?
Eloar,
1
Możliwe jest zapisanie węzła bezpośrednio metodą Ayeshs (nowa stdClass itp.). Tylko interfejs użytkownika sprawdza poprawność wymaganych pól
pico34
Spróbuję zaktualizować węzeł za pomocą tej metody bez ustawiania wszystkich pól i sprawdzę, co się stanie. Może to być najszybsza metoda aktualizacji węzłów w procedurze aktualizacji modułu (partia).
Eloar,
2

Po wypróbowaniu wszystkich podejść wymienionych w innych odpowiedziach mam bardzo wolne czasy aktualizacji (około 7 dni dla 700 000 węzłów typu węzła z ponad 20 polami), dopóki nie znalazłem tego artykułu: http://www.drupalonwindows.com/en/ blog / only-update-modified-fields-or-properties-bytu-drupal .

Po zaimplementowaniu czegoś takiego jak poniższy kod w hook_update skróciłem czas aktualizacji do 2 godzin, co moim zdaniem jest możliwe do zarządzania.

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
źródło
1

Miałem nawet ten sam wymóg aktualizacji pola dla wszystkich węzłów określonego typu treści. Użyłem wielokrotnego_węzła_węzła i aktualizacji_pola_pola .

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

Przebiegłem przez drush i było dość szybko.

Suresh R.
źródło
0

Czy zastanawiałeś się nad zrobieniem tych aktualizacji bezpośrednio w bazie danych przy użyciu mySQL? Jest to prawdopodobnie najprostszy i najszybszy sposób na osiągnięcie tego, co chcesz.

Oto prosty przykład. Możesz wykonać takie polecenie z zakładki „SQL” w phpMyAdmin. Wyobraź sobie, że masz typ zawartości o nazwie Profil członka. W nim masz pole o nazwie „Typ członka” (np. Firma, osoba, organizacja). Załóżmy, że chcesz zaktualizować wszystkie wystąpienia hasła „FIRMA” do „firma”. Następujące polecenie to zrobi.

AKTUALIZACJA content_type_member_profile SET field_type_of_member_value= „firma” WHERE field_type_of_member_value= „FIRMA”;

Kasa Rozpocznij z MySQL

Bisonbleu
źródło
Jest to jedna z opcji. Zostawiam to jako ostatnie do sprawdzenia i wdrożenia. Nie chcę zepsuć struktury drupala i wad. Najpierw szukam jakichkolwiek rozwiązań bezpośrednio za pośrednictwem Drupal API. Byłbyś bardzo wdzięczny, gdybyś mógł podać kilka przykładów.
Eloar,
Punkt Goog. Do mojej początkowej odpowiedzi dodałem prosty przykład.
Bisonbleu,
eh, znam SQL wystarczająco dobrze, aby zaktualizować dowolne rekordy w dowolnych tabelach. To nie jest problem. Problem polega na tym, że nie znam wystarczająco sposobu, w jaki Drupal przechowuje dane (szczególnie metadane). W takich systemach zawsze jest dużo buforowania, konserwacji itp., Więc aktualizowanie go na najniższym poziomie może (nie zawsze) zepsuć pewne wady. Więc jeśli do tego dojdzie, spróbuję to zrobić na najniższym poziomie i będę gotowy na prawdziwy bałagan.
Eloar,