швидке збереження значення одного поля


19

На моєму сайті близько 70 тис. Вузлів вказаного типу. Мені потрібно запустити оновлення на них. Деякі операції та встановлення одного поля на потрібне значення. node_saveнасправді повільно, і це призводить до збоїв (занадто довгий callstack mayby). Чи є швидший спосіб ввести інформацію в цьому конкретному полі?

Про це field_attach_updateзгадувалося в одному дописі, але це не набагато швидше.

EDIT: На цьому типі вузла побудовано досить складний вигляд, але він не працює над цим полем, яке я хочу оновити.

Відповіді:


30

Я б точно пішов field_attach_update.

Ідея проста. Просто завантажте вузол і збережіть його за допомогою field_attach_update.

Наприклад:

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

Це не змінить жодної часової позначки чи будь-якого іншого гака, до якого зазвичай запускається node_save. Завантаження вузла також викликає деякі гачки, так що, ймовірно, це не так ефективно.

Якщо у вас nid, і якщо структура вузла мертва проста, ви можете це зробити так само:

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

У будь-якому разі, якщо ви намагаєтесь оновити що-небудь, крім поля, це не допоможе (статус коментаря, опублікований стан тощо). Крім того, якщо ви використовуєте node_save, кеш для конкретного вузла буде автоматично очищений для різних методів, нам потрібно очистити його за допомогою "entit_get_controller".

Оновлення: Очевидно, що вам слід також зателефонувати, field_attach_presave()щоб інші модулі могли належним чином обробити введення поля. Наприклад, файловий модуль використовує його для встановлення постійного стану файлу за допомогою цього гака. Я оновив свої 2 приклади вище.


У мене є nid, але структура не така проста для вузла, але поле, яке я хочу оновити, є мертвим простим. Що я можу очікувати від встановлення лише одного поля для існуючого вузла (ідентифікованого nid, але не завантаженого), а потім дзвінка field_attach_update?
Eloar

4
Коли це з'ясувалося за допомогою запиту сутності, сповільнювалося. Так перехід від node_saveдо field_attach_updateі від EntityFieldQueryдо db_query_rangeбуло дуже корисним. Від 3х до оновлення до 40 хвилин.
Eloar

2

Якщо ви не хочете зберігати дані поля, не викликаючи стандартних подій та дій, тоді ви можете використовувати drupal_write_record .

Ось приклад для вставки Hello World у поле тіла для вузла статті типу з ідентифікатором 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);

Якщо ваш сайт багатомовний, ви хочете використовувати "en" або мову вашого вмісту замість "und".

Якщо ви робите версію, вам доведеться бути обережними, щоб вставити правильний ідентифікатор редагування, інакше ви можете просто вставити те саме значення, що й entityid.

Зверніть увагу, як ці дані вставляються у дві таблиці field_data_ * та field_revision_ *. Ви повинні вставити обидва, щоб переконатися, що сайт працює за бажанням.

Після цього вам потрібно буде очистити кеші для полів, які відображатимуться залежно від налаштування кешування.


2

Для простого оновлення на зразок цього, де потрібно оновити багато вузлів, я завжди використовую оператор оновлення MySQL. Так, кешування потрібно враховувати, але ви можете просто очистити кеш після закінчення, і це все добре. Вам, звичайно, потрібно ознайомитись зі структурою даних, але в Drupal 6. це досить просто (хоча в Drupal 7 жахливо)


Проект був створений для Drupal 7. Після того, як деякі частини мого модуля були зроблені для маніпулювання безпосередньо на структурі БД Drupal для зчитування значень та пошуку, оскільки запити sql були набагато швидшими, ніж EntityFieldQueries.
Eloar

2

Я також пропоную field_attach_update, а не прямий SQL-запит, оскільки sql не оновлює об’єкт кешу вузла, а в наступному node_loadвам не буде завантажено оновлене значення поля, ви завантажите старе значення

field_attach_update набагато краще, ніж прямий запит SQL.


Айеш К запропонував створити вузол як stdClassоб'єкт без завантаження. Чи знаєте ви, що може статися, якщо я спробую оновити вузол таким чином, не встановлюючи всіх полів? Чи будуть вони перезаписані нулями або типовими значеннями? Можливо, вони будуть ігноровані шляхом оновлення?
Eloar

1
Зберегти вузол можна безпосередньо методом Ayeshs (новий stdClass і т. Д.), Лише інтерфейс користувача підтверджує необхідні поля
pico34

Я спробую оновити вузол цим методом без встановлення всіх полів і перевірити, що буде. Це може бути найшвидший метод оновлення вузлів у процедурі оновлення модуля (пакетне).
Eloar

2

Спробувавши всі підходи, згадані в інших відповідях, я отримав дуже повільний час оновлення (приблизно 7 днів для 700 000 вузлів типу вузла з 20+ полями), поки не знайшов цю статтю: http://www.drupalonwindows.com/uk/ блог / тільки-оновлення-змінені поля-або-властивості-сутність-drupal .

Реалізувавши щось на кшталт наведеного нижче коду в куповому_поновленні, я скоротив час оновлення до 2 годин, що, на мою думку, є керованим.

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

У мене навіть була однакова вимога оновлення поля для всіх вузлів певного типу вмісту. Я використовував node_load_multiple та 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);
}

Я пробіг це через барабан і це було досить швидко.


0

Чи думали ви робити ці оновлення безпосередньо в базі даних за допомогою mySQL? Це, мабуть, найпростіший і найшвидший спосіб досягти того, що ви хочете.

Ось простий приклад. Ви можете виконати таку команду на вкладці 'SQL' в phpMyAdmin. Уявіть, що у вас є тип вмісту, який називається Профіль учасника. У ньому ви маєте поле з назвою "Тип члена" (наприклад, компанія, особа, організація). Скажімо, ви хочете оновити всі події для "COMPANY" на "company". Наступна команда зробить саме це.

ОНОВЛЕННЯ content_type_member_profile SET field_type_of_member_value= 'компанія' WHERE field_type_of_member_value= 'КОМПАНІЯ';

Також оформляйте замовлення Початок роботи з MySQL


Це один із варіантів. Я залишаю це як останнє для перевірки та реалізації. Я не хочу багато псувати брутальну структуру та вади. Тому спочатку я шукаю будь-які рішення безпосередньо через Drupal API. Якби ви могли навести кілька прикладів, було б дуже вдячно.
Eloar

Гугл-пункт. Я додав простий приклад до своєї початкової відповіді.
Bisonbleu

Так, я знаю SQL досить добре, щоб оновити будь-які записи в будь-яких таблицях. Це не проблема. Проблема полягає в тому, що я недостатньо знайомий із тим, як drupal зберігає дані (особливо метадані). У таких системах завжди багато кешування, збереження тощо, тому оновлення на найнижчому рівні може (не завжди) зіпсувати деякі недоліки. Тож якщо це дійшло до цього, я спробую це зробити на найнижчому рівні, і буду готовий до справжнього безладу.
Eloar
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.