hemat nilai bidang tunggal

19

Saya punya sekitar 70 ribu node dari tipe yang ditentukan di situs saya. Saya perlu menjalankan pembaruan pada mereka. Beberapa operasi dan pengaturan satu bidang ke nilai yang diinginkan. node_savesangat lambat dan itu menyebabkan crash (terlalu lama callstack mayby). Apakah ada cara yang lebih cepat untuk menulis info pada bidang khusus ini?

Ada yang field_attach_updatedisebutkan dalam satu posting, tetapi tidak jauh lebih cepat.

EDIT: Ada tampilan yang cukup kompleks yang dibangun pada tipe simpul ini, tetapi tidak berfungsi pada bidang ini yang ingin saya perbarui.

Eloar
sumber

Jawaban:

30

Saya pasti akan melakukannya field_attach_update.

Idenya sederhana. Cukup muat simpul dan simpan menggunakan 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));

Ini tidak akan mengubah stempel waktu atau kait apa pun yang biasanya dipanggil node_save. Memuat node juga akan meminta beberapa kait jadi mungkin itu tidak efisien.

Jika Anda memiliki nid dan jika struktur simpul mati sederhana, Anda dapat melakukannya seperti ini juga:

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

Bagaimanapun, jika Anda mencoba memperbarui apa pun selain bidang, ini tidak akan berfungsi (status komentar, status yang dipublikasikan, dll). Jika Anda menggunakan node_save, cache untuk node tertentu akan dihapus secara otomatis untuk metode yang berbeda, kami perlu menghapusnya dengan 'entitas_get_controller'.

Pembaruan: Tampaknya Anda juga harus menelepon field_attach_presave()untuk membiarkan modul lain memproses input bidang dengan benar. Modul file, misalnya, menggunakannya untuk mengatur status file ke permanen menggunakan kait ini. Saya telah memperbarui 2 contoh saya di atas.

AyeshK
sumber
Saya memiliki nid tetapi struktur tidak begitu sederhana untuk node, tetapi bidang yang ingin saya perbarui adalah mati sederhana. Apa yang bisa saya harapkan dari pengaturan hanya satu bidang untuk simpul yang ada (diidentifikasi oleh nid tetapi tidak dimuat) dan kemudian memanggil field_attach_update?
Eloar
4
Karena ternyata menggunakan kueri entitas memperlambat segalanya. Jadi beralih dari node_saveke field_attach_updatedan dari EntityFieldQueryke db_query_rangecukup menguntungkan. Dari pembaruan 3j hingga 40 menit.
Eloar
2

Jika Anda tidak ingin menyimpan data bidang tanpa menyebabkan peristiwa dan tindakan standar terjadi, Anda dapat menggunakan drupal_write_record .

Berikut ini adalah contoh untuk menyisipkan Hello World ke bidang tubuh untuk simpul artikel jenis dengan 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);

Jika situs Anda multlingual, maka Anda ingin menggunakan 'en' atau bahasa konten Anda alih-alih 'und'.

Jika Anda melakukan revisi, Anda harus berhati-hati untuk memasukkan id revisi yang tepat, jika tidak, Anda dapat memasukkan nilai yang sama dengan entitas_id.

Perhatikan bagaimana data ini dimasukkan ke dalam dua tabel field_data_ * dan field_revision_ *. Anda harus memasukkan keduanya untuk memastikan situs berfungsi seperti yang diinginkan.

Setelah menjalankan ini, Anda perlu menghapus cache untuk menampilkan bidang-bidang tergantung pada bagaimana pengaturan cache Anda.

Thomas4019
sumber
2

Untuk pembaruan sederhana seperti ini di mana banyak node perlu diperbarui, saya selalu menggunakan pernyataan pembaruan MySQL. Ya, caching perlu dipertimbangkan tetapi Anda bisa membersihkan cache setelah selesai dan semuanya baik-baik saja. Anda, tentu saja, harus terbiasa dengan struktur data tetapi relatif sederhana di Drupal 6. (walaupun menghebohkan di Drupal 7)

Troy Frech
sumber
Proyek dibuat untuk Drupal 7. Setelah semua bagian modul saya dibuat untuk memanipulasi secara langsung pada struktur DB Drupal untuk membaca nilai dan mencari karena sql queries jauh lebih cepat daripada EntityFieldQueries.
Eloar
2

Saya sarankan field_attach_updatejuga, dan bukan query SQL langsung, karena sql tidak memperbarui objek cache node, dan di berikutnya node_loadAnda tidak akan memuat nilai bidang yang diperbarui, Anda akan memuat nilai lama

field_attach_update jauh lebih baik daripada query SQL langsung.

pico34
sumber
Ayesh K menyarankan membuat simpul sebagai stdClassobjek tanpa memuat. Apakah Anda tahu apa yang mungkin terjadi jika saya mencoba memperbarui simpul dengan cara ini tanpa mengatur semua bidang? Apakah itu akan ditimpa dengan nol atau default? Apakah itu akan diabaikan dengan memperbarui proses?
Eloar
1
Dimungkinkan untuk menyimpan simpul secara langsung dengan metode Ayeshs (stdClass baru dll ...) hanya UI yang membuat validasi pada bidang yang diperlukan
pico34
Saya akan mencoba memperbarui node dengan metode ini tanpa mengatur semua bidang dan memeriksa apa yang akan terjadi. Ini mungkin metode tercepat untuk memperbarui node dalam prosedur pembaruan modul (batch).
Eloar
2

Setelah mencoba semua pendekatan yang disebutkan dalam jawaban lain saya mendapat waktu pembaruan yang sangat lambat (sekitar 7 hari untuk 700.000 node dari jenis node dengan 20 bidang) sampai saya menemukan artikel ini: http://www.drupalonwindows.com/en/ blog / hanya-perbarui-berubah-bidang-atau-properti-entitas-drupal .

Setelah menerapkan sesuatu seperti kode di bawah ini di hook_update saya mengurangi waktu pembaruan menjadi 2 jam, yang saya pikir dapat dikelola.

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

Saya bahkan memiliki persyaratan yang sama untuk memperbarui bidang untuk semua node dari jenis konten tertentu. Saya menggunakan node_load_multiple dan 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);
}

Saya menjalankannya melalui drush dan itu cukup cepat.

Suresh R
sumber
0

Sudahkah Anda mempertimbangkan untuk melakukan pembaruan ini langsung ke dalam basis data menggunakan mySQL? Mungkin cara termudah & tercepat untuk mencapai apa yang Anda inginkan.

Ini adalah contoh sederhana. Anda dapat menjalankan perintah seperti itu dari tab 'SQL' di phpMyAdmin. Bayangkan Anda memiliki tipe konten yang disebut Profil Anggota. Di dalamnya Anda memiliki bidang bernama 'Jenis anggota' (misalnya perusahaan, individu, organisasi). Katakanlah Anda ingin memperbarui semua kejadian untuk 'PERUSAHAAN' menjadi 'perusahaan'. Perintah berikut akan melakukan hal itu.

UPDATE content_type_member_profile SET field_type_of_member_value= 'perusahaan' WHERE field_type_of_member_value= 'PERUSAHAAN';

Juga, checkout Memulai dengan MySQL

Bisonbleu
sumber
Ini adalah salah satu opsi. Saya membiarkannya sebagai yang terakhir untuk memeriksa dan mengimplementasikan. Saya tidak ingin mengacaukan struktur drupal dan banyak kekurangan. Jadi pertama-tama saya mencari solusi apa pun secara langsung melalui Drupal API. Jika Anda bisa memberikan beberapa contoh, itu akan sangat dihargai.
Eloar
Titik goog. Saya menambahkan contoh sederhana ke jawaban awal saya.
Bisonbleu
eh, saya tahu SQL cukup baik untuk memperbarui catatan dalam tabel apa pun. Bukan itu masalahnya. Masalahnya adalah, saya tidak cukup akrab dengan cara drupal menyimpan data (terutama meta-data). Selalu ada banyak caching, pengawetan dan sebagainya dalam sistem seperti itu, jadi memperbaruinya pada level terendah mungkin (tidak selalu) mengacaukan beberapa kekurangan. Jadi jika sampai seperti ini, saya akan mencoba melakukannya pada level terendah, dan saya akan siap untuk beberapa kekacauan nyata.
Eloar