Чи дійсно 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->вислів; pls поки що це ігнорую.

Намагаючись переписати це за допомогою 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 мсек
  • Версія EFQ: 4943 мсек

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

Yikes. Я щось тут неправильно чи це лише вартість використання EFQ? Я не робив жодної спеціальної настройки бази даних щодо типів вмісту; саме те, що випливає з визначення типів вмісту звичайним, на основі форми. Будь-які думки? Код EFQ, безумовно, чистіший, але я дійсно не думаю, що я можу дозволити собі хіт в 40 разів.


3
Ви можете скинути обидва генеровані запити sql?
Андре Баум'є

1
Ознайомтеся з цим, якщо ви не впевнені, як вивести SQL з EFQ
Clive

2
Гаразд, є прогрес: тут відбувається те, що на моєму веб-сайті є купа правил доступу до вузлів, які значно збільшують розмір запиту. Вони автоматично застосовувались до запиту EFQ (навіть якщо ->addTag('node_access')у запиті немає). Я перекинув "прямий" запит тегом node_access, і час виконання набагато ближче: час EFQ тепер лише приблизно в 2 рази більший, ніж у прямого підходу, що виглядає розумним, враховуючи відносний SQL, який обидва викачують (який Я можу дописувати, якщо людей все одно хвилює). (продовження наступного коментаря ....)
Джим Міллер

Тож тепер, напевно, питання, чому я автоматично отримую речі node_access у версії EFQ? Я думав, що вам доведеться явно запитати про це через пункт addTag () ??
Джим Міллер

Відповіді:


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класу вимагає:

  • Код для побудови імені таблиці бази даних з імені поля
  • Код для побудови умови, яка використовується для приєднання таблиці, що містить дані поля, до таблиці, що містить дані сутності
  • Код для побудови імені рядка бази даних, що містить дані поля

Різниця видно відразу: в одному випадку ви використовуєте три літеральні рядки, а в іншому - код, який (у найпростіших випадках) є об'єднуючими рядками.

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

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

Це працює, якщо ви використовуєте Drupal 7.15 або новішої версії; для більш ранніх версій слід використовувати наступний код.

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

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


я повинен визнати, що я, мабуть, не прав, оскільки перший запит також використовує пейджер (я не помітив ->extend('PagerDefault');спочатку)
mojzis

Упс, ви праві.
kiamlaluno

це мене дуже зацікавило, тож я намагаюсь щось зробити за результатами експерименту, наведеного вище, і не можу підтвердити величезну різницю в числах ... може хтось теж спробує, будь ласка?
mojzis

Отже, лише для підтвердження: виклики EFQ ВЖЕ завжди посилаються на правила доступу до вузла сайту, якщо ви не зробите щось, щоб уникнути цього (як описано вище). Правильно?
Джим Міллер

@JimMiller Це правильно, і саме тому тег "DANGEROUS_ACCESS_CHECK_OPT_OUT" додано до Drupal 7.15.
kiamlaluno
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.