EntityFieldQuery gerçekten bu verimsiz mi?


11

Entity API için kabul edilen bir acemi, ama bunu tedavi etmeye çalışıyorum. Onlara bağlı çeşitli alanları ile çeşitli içerik türleri kullanan bir site üzerinde çalışıyorum; hiçbir şey fantezi. Yani, bir dizi girdi almak istediğimde, cehaletimde, doğrudan veritabanına çağırıyorum ve böyle bir şey yaptım:

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

(evet, muhtemelen bu satırların bir demetini tek bir $the_questions->ifadeye daraltabilirim ; lütfen şimdilik bunu görmezden gel.)

EntityFieldQuery ile yeniden yazmaya çalışırken, ben geldim:

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

bu da bana istenen sonuçları veriyor ve kesinlikle çok daha güzel.

Şimdi performansı merak ediyorum. Başlangıç ​​olarak, bu kod parçalarının her birini for(), time()yürütmeden önce ve sonra yakalayan aptal bir döngüye atarım . Ben çok büyük olmayan bir veritabanı üzerinde her sürümü 100 kez çalıştırın ve böyle bir şey olsun:

  • Doğrudan sürüm: 110 ms
  • EFQ sürümü: 4943 ms

Açıkçası testi tekrar yaptığımda farklı sonuçlar elde ediyorum, ancak sonuçlar sürekli olarak aynı balo sahasında.

Amanın. Burada yanlış bir şey mi yapıyorum, yoksa bu sadece EFQ kullanmanın maliyeti midir? İçerik türlerine göre özel bir veritabanı ayarlaması yapmadım; içerik türlerini olağan, form tabanlı bir şekilde tanımlamaktan ibarettir. Düşüncesi olan var mı? EFQ kodu kesinlikle daha temiz, ancak 40x performans artışı sağlayabildiğimi sanmıyorum.


3
her iki oluşturulan sql sorgu dökümü olabilir?
Andre Baumeier

1
Bkz bu bir emin bir EFQ ait SQL çıkmak nasıl değilseniz
Clive

2
Tamam, ilerleme var: Burada olan şey, sitemin sorgunun boyutunu biraz artıran bir dizi düğüm erişim kuralına sahip olmasıdır. Bunlar otomatik olarak EFQ sorgusuna uygulanıyordu (sorguda hiç yok olmasına rağmen ->addTag('node_access')??). Ben bir node_access etiketi ile "doğrudan" sorgusu yeniden ve yürütme süreleri çok daha yakın: EFQ zaman şimdi sadece her ikisi de dışarı pompalama göreceli SQL göz önüne alındığında makul görünüyor doğrudan yaklaşım, sadece 2 kat daha büyük bir faktör hakkında İnsanlar hala umursuyorsa gönderebilirim). (devamı için bir sonraki yorumda ....)
Jim Miller

Şimdi soru, sanırım, neden otomatik olarak EFQ sürümünde node_access öğelerini alıyorum? Ben açıkça addTag () yan tümcesi ile sormak zorunda düşündüm?
Jim Miller

Yanıtlar:


10

EntityFieldQueryOnun gereksinimleri gerçekleşmesine izin olarak sınıf olarak etkilidir. MongoDB kullanan gibi saha verilerini depolamak için NoSQL motoru kullananlarla bile tüm alan depolama sınıflarıyla uyumlu olması gerekir . Bu nedenle, EntityFieldQuerygeçerli alan depolama arka ucu hiç bir SQL veritabanı kullanmayabileceğinden, doğrudan veritabanını sorgulayamaz.

Saha depolama, verilerini depolamak için bir SQL motoru kullanıyor olsa bile $query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);, EntityFieldQuerysınıfın eşdeğeri şunları gerektirir:

  • Alan adından veritabanı tablosu adını oluşturmak için kod
  • Alan verilerini içeren tabloyu varlık verilerini içeren tabloyla birleştirmek için kullanılacak koşulu oluşturmak için kod
  • Alan verilerini içeren veritabanı satırının adını oluşturmak için kod

Fark hemen görülebilir: Bir durumda üç litre dizgi kullanırken, diğer durumda (en basit durumda) dizeleri birleştiren kod vardır.

Kullanıcının alanlara erişim iznine sahip olup olmadığını kontrol eden kod hakkındaki yorumunuza göre, aşağıdaki satırı kullanarak EntityFieldQuerysınıfı kullanarak kodu atlayabilirsiniz .

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

Bu, Drupal 7.15 veya üzerini kullanıyorsanız çalışır; önceki sürümler için aşağıdaki kodu kullanmalısınız.

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

Kod, kullanıcının erişiminin olmaması gereken kullanıcı bilgilerine gösterilebiliyorsa, her zamanki gibi erişim iznini atlamamalısınız. Bu, yayınlanmamış bir düğüm yalnızca yayınlanmamış düğümleri görme iznine sahip olan kullanıcılara gösterildiğinde Drupal'dan yapılanlara benzer. Kodun amacı, örneğin, art arda silinen bazı varlıkları seçmekse (örn. Cron görevleri sırasında), erişim kontrolünü atlamak herhangi bir zarar vermez ve devam etmenin tek yolu budur.


ilk sorgu çok bir çağrı cihazı kullandığı için ben muhtemelen doğru değilim itiraf etmeliyim ( ->extend('PagerDefault');ilk fark
etmedi

Hata! Haklısın.
kiamlaluno

Bu beni gerçekten ilgilendirdi, bu yüzden yukarıdaki deney çizgileri boyunca bir şey deniyorum ve sayılardaki büyük farkı onaylayamıyorum ... birisi de deneyebilir misiniz, lütfen?
mojzis

Bu nedenle, sadece onaylamak için: EFQ, her zaman gerçekleşmesini önlemek için bir şey yapmadığınız sürece (yukarıda açıklandığı gibi) sitenin düğüm erişim kurallarını çağırır. Sağ?
Jim Miller

@JimMiller Bu doğrudur ve "DANGEROUS_ACCESS_CHECK_OPT_OUT" etiketinin Drupal 7.15'e eklenmesinin nedeni budur.
kiamlaluno
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.