ประหยัดค่าฟิลด์เดียวอย่างรวดเร็ว


19

ฉันมีโหนดประเภทที่ระบุในไซต์ของฉันประมาณ 70,000 โหนด ฉันต้องการเรียกใช้การอัปเดต การดำเนินการบางอย่างและการตั้งค่าหนึ่งฟิลด์เป็นค่าที่ต้องการnode_saveช้ามากและทำให้เกิดปัญหา (callstack ยาวเกินไปอาจนานเกินไป) มีวิธีที่เร็วกว่าในการเขียนข้อมูลในฟิลด์นี้โดยเฉพาะหรือไม่?

มีการfield_attach_updateพูดถึงในโพสต์เดียว แต่มันก็ไม่เร็วนัก

แก้ไข: มีมุมมองค่อนข้างซับซ้อนที่สร้างขึ้นบนโหนดชนิดนี้ แต่ไม่ทำงานในฟิลด์นี้ซึ่งฉันต้องการอัปเดต

คำตอบ:


30

field_attach_updateแน่นอนผมต้องการไปสำหรับ

ความคิดนั้นง่าย เพียงโหลดโหนดและบันทึกโดยใช้ 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));

สิ่งนี้จะไม่เปลี่ยนการประทับเวลาหรือเบ็ดอื่น ๆ ที่ node_save มักจะเรียกใช้ การโหลดโหนดจะก่อให้เกิด hooks บางตัวซึ่งอาจไม่ได้ผล

หากคุณมี 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 แคชสำหรับโหนดนั้นจะถูกล้างโดยอัตโนมัติสำหรับวิธีการต่าง ๆ ที่เราจำเป็นต้องล้างมันด้วย 'entity_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 ลงในฟิลด์เนื้อหาสำหรับโหนดของบทความประเภทที่มี 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);

หากไซต์ของคุณเป็นหลายภาษาคุณจะต้องใช้ 'en' หรือภาษาของเนื้อหาแทน 'und'

หากคุณกำลังทำการแก้ไขคุณจะต้องระมัดระวังในการใส่รหัสการแก้ไขที่ถูกต้องมิฉะนั้นคุณสามารถแทรกค่าเดียวกับเอนทิตี้ของ

โปรดสังเกตว่าข้อมูลนี้ถูกแทรกลงในสองตาราง field_data_ * และ field_revision_ * คุณควรแทรกทั้งสองอย่างเพื่อให้แน่ใจว่าไซต์ทำงานตามที่ต้องการ

หลังจากใช้งานแล้วคุณจะต้องล้างแคชเพื่อให้ฟิลด์ปรากฏขึ้นทั้งนี้ขึ้นอยู่กับการตั้งค่าแคชของคุณ


2

สำหรับการอัปเดตอย่างง่ายเช่นนี้ที่จำเป็นต้องอัปเดตโหนดจำนวนมากฉันจะใช้คำสั่ง MySQL update เสมอ ใช่การแคชจะต้องนำมาพิจารณาด้วย แต่คุณสามารถล้างแคชได้หลังจากที่ทำเสร็จแล้วและมันก็ดีทั้งหมด แน่นอนว่าคุณต้องคุ้นเคยกับโครงสร้างข้อมูล แต่ค่อนข้างง่ายใน Drupal 6 (แม้ว่าจะน่ากลัวใน Drupal 7)


โครงการถูกสร้างขึ้นสำหรับ Drupal 7 หลังจากที่บางส่วนของโมดูลของฉันถูกสร้างขึ้นเพื่อจัดการโดยตรงกับโครงสร้าง Drupal DB สำหรับการอ่านค่าและการค้นหาเนื่องจากการสืบค้น sql นั้นเร็วกว่า EntityFieldQueries มาก
Eloar

2

ฉันขอแนะนำfield_attach_updateเช่นกันและไม่ใช่แบบสอบถาม SQL โดยตรงเนื่องจาก sql ไม่ได้อัปเดตวัตถุโหนดแคชและในครั้งต่อไปnode_loadคุณจะไม่โหลดค่าเขตข้อมูลที่อัปเดตคุณจะโหลดค่าเก่า

field_attach_update ดีกว่าแบบสอบถาม SQL โดยตรง


Ayesh K แนะนำให้สร้างโหนดเป็นstdClassวัตถุโดยไม่โหลด คุณรู้หรือไม่ว่าจะเกิดอะไรขึ้นถ้าฉันพยายามอัปเดตโหนดด้วยวิธีนี้โดยไม่ต้องตั้งค่าฟิลด์ทั้งหมด สิ่งเหล่านั้นจะถูกเขียนทับด้วยโมฆะหรือค่าเริ่มต้นหรือไม่ อาจจะถูกเพิกเฉยโดยการอัพเดทกระบวนการ
Eloar

1
เป็นไปได้ที่จะบันทึกโหนดโดยตรงโดยวิธี Ayeshs (stdClass ใหม่และอื่น ๆ ... ) เท่านั้นที่ UI ทำการตรวจสอบความถูกต้องในฟิลด์ที่จำเป็น
pico34

ฉันจะพยายามอัปเดตโหนดด้วยวิธีนี้โดยไม่ต้องตั้งค่าฟิลด์ทั้งหมดและตรวจสอบสิ่งที่จะเกิดขึ้น อาจเป็นวิธีที่เร็วที่สุดสำหรับการอัพเดตโหนดในโพรซีเดอร์การอัพเดตโมดูล (แบตช์)
Eloar

2

ต้องลองวิธีการทั้งหมดที่กล่าวถึงในคำตอบอื่น ๆ ฉันได้รับเวลาอัปเดตช้ามาก (ประมาณ 7 วันสำหรับโหนด 700.000 ชนิดของโหนดที่มี 20+ ฟิลด์) จนกว่าฉันจะพบบทความนี้: http://www.drupalonwindows.com/en/ บล็อก

หลังจากใช้บางอย่างเช่นโค้ดด้านล่างใน hook_update ฉันลดเวลาอัปเดตเป็น 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);
}

ฉันขับรถผ่าน drush และมันก็ค่อนข้างเร็ว


0

คุณได้พิจารณาทำการอัปเดตเหล่านี้ลงในฐานข้อมูลโดยตรงโดยใช้ mySQL หรือไม่? มันอาจเป็นวิธีที่ง่ายและเร็วที่สุดในการบรรลุสิ่งที่คุณต้องการ

นี่คือตัวอย่างง่ายๆ คุณสามารถดำเนินการคำสั่งดังกล่าวจากแท็บ 'SQL' ใน phpMyAdmin ลองนึกภาพคุณมีประเภทเนื้อหาที่เรียกว่าโปรไฟล์สมาชิก ในนั้นคุณมีฟิลด์ชื่อ 'ประเภทของสมาชิก' (เช่น บริษัท บุคคลองค์กร) สมมติว่าคุณต้องการอัปเดตทุกเหตุการณ์สำหรับ 'บริษัท ' เป็น 'บริษัท ' คำสั่งต่อไปนี้จะทำแค่นั้น

อัพเดต content_type_member_profile SET field_type_of_member_value= 'company' WHERE field_type_of_member_value= 'COMPANY';

นอกจากนี้การชำระเงินเริ่มต้นกับ MySQL


มันเป็นหนึ่งในตัวเลือก ฉันปล่อยให้มันเป็นคนสุดท้ายที่จะตรวจสอบและดำเนินการ ฉันไม่ต้องการที่จะทำให้โครงสร้าง drupal และข้อบกพร่องมากเกินไป ดังนั้นก่อนอื่นฉันจะหาวิธีแก้ปัญหาโดยตรงผ่าน Drupal API หากคุณสามารถยกตัวอย่างได้ก็จะได้รับการชื่นชมมาก
Eloar

จุด Goog ฉันเพิ่มตัวอย่างง่ายๆในคำตอบเริ่มต้นของฉัน
Bisonbleu

ใช่ฉันรู้ SQL ดีพอที่จะอัปเดตระเบียนใด ๆ ในตารางใด ๆ มันไม่ใช่ปัญหา ปัญหาคือฉันไม่คุ้นเคยกับวิธีเก็บข้อมูลของ drupal (โดยเฉพาะ meta-data) มีการแคชจำนวนมากอยู่เสมอการเก็บรักษาและอื่น ๆ ในระบบดังกล่าวดังนั้นการอัปเดตในระดับต่ำสุดอาจทำให้เกิดข้อผิดพลาดบางอย่าง ดังนั้นถ้าพูดถึงเรื่องนี้ฉันจะพยายามทำมันในระดับต่ำสุดและฉันจะพร้อมสำหรับความยุ่งเหยิงจริง ๆ
Eloar
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.