EntityFieldQuery นี้ไม่มีประสิทธิภาพจริงๆหรือ


11

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

$query = db_select('node', 'n')->extend('PagerDefault');
$query->fields('n', array('nid'));
$query->condition('n.type', 'my_content_type');

$query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id');
$query->condition('role.field_user_role_value', $some_value);

$query->leftJoin('field_data_field_withdrawn_time', 'wt', 'n.nid = wt.entity_id');
$query->condition('wt.field_withdrawn_time_value', 0);

$query->orderBy('n.created', 'desc');

$query->limit(10);

$result = $the_questions->execute()->fetchCol();

(ใช่ฉันอาจจะยุบเส้นเหล่านี้เป็น$the_questions->ประโยคเดียวได้โปรดเพิกเฉยต่อเรื่องนั้นตอนนี้)

พยายามเขียนสิ่งนี้ด้วย EntityFieldQuery ฉันคิดว่า:

$query = new EntityFieldQuery();
$query
  ->entityCondition('entity_type', 'node')
  ->entityCondition('bundle', 'my_content_type')
  ->fieldCondition('field_user_role', 'value', $some_value)
  ->fieldCondition('field_withdrawn_time', 'value', 0)
  ->propertyOrderBy('created', 'desc')
  ->pager(10);

$result = $query->execute();

if (isset($result['node'])) {
    $result_nids = array_keys($result['node']);
}
else {
    $result_nids = array();
}

ซึ่งให้ผลลัพธ์ที่ต้องการและดีกว่า

ดังนั้นตอนนี้ฉันสงสัยเกี่ยวกับประสิทธิภาพ เมื่อเริ่มต้นฉันจะโยนแต่ละบิตของรหัสเหล่านั้นลงในfor()ลูปโง่จับภาพtime()ก่อนและหลังการดำเนินการ ฉันรันแต่ละเวอร์ชัน 100 ครั้งบนฐานข้อมูลที่ไม่ใหญ่มากและได้รับสิ่งนี้:

  • รุ่นตรง: 110 msec
  • รุ่น EFQ: 4943 msec

เห็นได้ชัดว่าฉันได้รับผลลัพธ์ที่แตกต่างกันเมื่อฉันทำการทดสอบอีกครั้ง แต่ผลลัพธ์นั้นอยู่ใน ballpark เดียวกัน

Yikes ฉันกำลังทำสิ่งผิดพลาดที่นี่หรือนี่เป็นเพียงค่าใช้จ่ายในการใช้ EFQ หรือไม่ ฉันยังไม่ได้ทำการปรับแต่งฐานข้อมูลพิเศษใด ๆ ที่เกี่ยวข้องกับประเภทเนื้อหา; สิ่งเหล่านี้มาจากการกำหนดประเภทเนื้อหาตามวิธีปกติตามรูปแบบ ความคิดใด ๆ รหัส EFQ นั้นสะอาดกว่าจริง ๆ แต่ฉันไม่คิดว่าฉันจะซื้อประสิทธิภาพได้ถึงตีเลย


3
คุณสามารถถ่ายโอนแบบสอบถาม SQL สร้างทั้งสอง?
Andre Baumeier

1
ดูสิ่งนี้หากคุณไม่แน่ใจว่าจะดึง SQL ออกจาก EFQ
Clive

2
ตกลงมีความคืบหน้า: สิ่งที่เกิดขึ้นที่นี่คือเว็บไซต์ของฉันมีกฎการเข้าถึงโหนดที่เพิ่มขนาดของแบบสอบถามค่อนข้างมาก สิ่งเหล่านั้นจะถูกนำไปใช้กับแบบสอบถาม EFQ โดยอัตโนมัติ (แม้ว่าจะไม่มี->addTag('node_access')ในแบบสอบถามหรือไม่ก็ตาม) ฉันเรียกใช้ข้อความค้นหา "โดยตรง" อีกครั้งด้วยแท็ก node_access และเวลาในการดำเนินการใกล้เคียงกันมาก: เวลาของ EFQ ตอนนี้เป็นเพียงแค่ปัจจัยของ 2 มากกว่าวิธีการโดยตรงซึ่งดูสมเหตุสมผลเนื่องจาก SQL แบบสัมพัทธ์ซึ่งทั้งคู่สูบออกมา ฉันสามารถโพสต์ได้หากคนยังคงสนใจ) (ต่อความคิดเห็นต่อไป .... )
Jim Miller

ตอนนี้คำถามฉันเดาว่าทำไมฉันถึงได้รับ node_access ในรุ่น EFQ โดยอัตโนมัติ ฉันคิดว่าคุณต้องขออย่างชัดเจนผ่านทาง addTag () ข้อ?
Jim Miller

คำตอบ:


10

EntityFieldQueryชั้นเป็นที่มีประสิทธิภาพตามความต้องการของตนอนุญาตให้เป็น มันจะต้องเข้ากันได้กับการเรียนการจัดเก็บข้อมูลใด ๆ แม้จะมีผู้ที่ใช้เครื่องยนต์ NoSQL ในการจัดเก็บข้อมูลภาคสนามเช่นเดียวกับที่ใช้ MongoDB ด้วยเหตุผลดังกล่าวEntityFieldQueryไม่สามารถสอบถามฐานข้อมูลได้โดยตรงเนื่องจากแบ็กเอนด์หน่วยเก็บข้อมูลปัจจุบันอาจไม่ใช้ฐานข้อมูล SQL เลย

แม้ในกรณีที่ที่เก็บข้อมูลภาคสนามใช้เอนจิน SQL เพื่อเก็บข้อมูลความเทียบเท่า$query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);สำหรับEntityFieldQueryคลาสนั้นต้องการ:

  • รหัสเพื่อสร้างชื่อตารางฐานข้อมูลจากชื่อฟิลด์
  • รหัสที่จะสร้างเงื่อนไขที่จะใช้เพื่อเข้าร่วมตารางที่มีข้อมูลของเขตข้อมูลที่มีตารางที่มีข้อมูลนิติบุคคล
  • รหัสเพื่อสร้างชื่อของแถวฐานข้อมูลที่มีข้อมูลในฟิลด์

ความแตกต่างสามารถมองเห็นได้ทันที: ในกรณีหนึ่งคุณใช้สตริง litteral สามสายในขณะที่อีกกรณีหนึ่งจะมีรหัสที่ (ในกรณีที่ง่ายที่สุด) คือการต่อสตริง

ตามความคิดเห็นของคุณเกี่ยวกับรหัสที่ตรวจสอบว่าผู้ใช้มีสิทธิ์ในการเข้าถึงฟิลด์หรือไม่คุณสามารถข้ามโดยใช้บรรทัดต่อไปนี้เพื่อรหัสโดยใช้EntityFieldQueryคลาส

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

วิธีนี้ใช้ได้ผลถ้าคุณใช้ Drupal 7.15 ขึ้นไป สำหรับเวอร์ชั่นก่อนหน้านี้คุณควรใช้รหัสต่อไปนี้

$account = user_load(1);
$query->addMetaData('account', $account);

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


ฉันควรจะยอมรับฉันอาจจะไม่ถูกต้องตั้งแต่แรกใช้แบบสอบถามเพจเจอร์เกินไป (ฉันไม่ได้แจ้งให้ทราบ->extend('PagerDefault');ในตอนแรก)
Mojžiš

อ๊ะคุณพูดถูก
kiamlaluno

สิ่งนี้ทำให้ฉันสนใจจริง ๆ ดังนั้นฉันจึงพยายามทำบางอย่างตามแนวการทดสอบด้านบนและไม่สามารถยืนยันความแตกต่างจำนวนมากได้ ... มีใครลองดูบ้างก็ได้โปรดได้ไหม
mojzis

ดังนั้นเพียงเพื่อยืนยันว่า: EFQ โทรศัพท์เสมอเรียกกฎการเข้าถึงโหนดของเว็บไซต์เว้นแต่ว่าคุณจะทำสิ่งที่เกิดขึ้น (ดังอธิบายข้างต้น) ขวา?
Jim Miller

@JimMiller ถูกต้องและเป็นเหตุผลที่เพิ่มแท็ก "DANGEROUS_ACCESS_CHECK_OPT_OUT" ลงใน Drupal 7.15
kiamlaluno
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.