Jestem wpuszczonym nowicjuszem w interfejsie API Entity, ale próbuję to wyleczyć. Pracuję nad witryną, która wykorzystuje wiele typów treści z dołączonymi do nich różnymi polami; nic fajnego. Tak więc, gdy chcę pobrać zestaw wpisów, w swojej ignorancji dzwoniłem bezpośrednio do bazy danych i robiłem coś takiego:
$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();
(tak, prawdopodobnie mógłbym zwinąć kilka z tych wierszy w jedno $the_questions->
zdanie; pls zignoruj to na razie.)
Próbując przepisać to za pomocą EntityFieldQuery, wymyślam:
$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();
}
co daje mi pożądane wyniki i jest z pewnością ładniejsze.
Więc teraz zastanawiam się nad wydajnością. Na początek wrzucam każdy z tych fragmentów kodu do głupiej for()
pętli, przechwytując time()
przed i po wykonaniu. Każdą wersję uruchamiam 100 razy na niezbyt dużej bazie danych i otrzymuję coś takiego:
- Wersja bezpośrednia: 110 ms
- Wersja EFQ: 4943 ms
Oczywiście po ponownym uruchomieniu testu otrzymuję różne wyniki, ale wyniki są konsekwentnie w tym samym parku.
Yikes. Czy robię tu coś złego, czy to tylko koszt korzystania z EFQ? Nie przeprowadziłem żadnego specjalnego strojenia bazy danych w odniesieniu do typów treści; wynikają one ze zdefiniowania typów treści w zwykły sposób oparty na formularzach. jakieś pomysły? Kod EFQ jest zdecydowanie czystszy, ale naprawdę nie sądzę, że mogę sobie pozwolić na 40-krotny wzrost wydajności.
->addTag('node_access')
w zapytaniu ??). Zmieniłem zapytanie „bezpośrednie” za pomocą znacznika node_access, a czasy wykonania są znacznie bliższe: czas EFQ jest teraz tylko o współczynnik 2 większy niż bezpośrednie podejście, co wygląda rozsądnie, biorąc pod uwagę względne SQL, które wypompowują oba ( Mogę pisać, jeśli ludzie nadal się przejmują). (ciąg dalszy od następnego komentarza ....)Odpowiedzi:
EntityFieldQuery
Klasa jest tak skuteczny jak jego wymagania pozwalają mu być. Musi być kompatybilny z dowolnymi klasami pamięci masowej, nawet z tymi, które używają silnika NoSQL do przechowywania danych pól, takich jak ta, która używa MongoDB . Z tego powoduEntityFieldQuery
nie można bezpośrednio wysłać zapytania do bazy danych, ponieważ bieżący backend pamięci pola może w ogóle nie używać bazy danych SQL.Nawet w przypadku, gdy pamięć masowa używa silnika SQL do przechowywania danych, odpowiednik
$query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);
dlaEntityFieldQuery
klasy wymaga:Różnica jest natychmiast widoczna: w jednym przypadku używasz trzech łańcuchów literalnych, podczas gdy w drugim przypadku jest kod, który (w najprostszym przypadku) łączy łańcuchy.
Zgodnie z komentarzem na temat kodu, który sprawdza, czy użytkownik ma uprawnienia dostępu do pól, można go obejść, używając następującego wiersza do kodu przy użyciu
EntityFieldQuery
klasy.Działa to, jeśli używasz Drupala 7.15 lub nowszego; we wcześniejszych wersjach powinieneś użyć następującego kodu.
Jak zwykle nie należy pomijać uprawnień dostępu, jeśli kod może pokazywać użytkownikowi informacje, do których użytkownik nie powinien mieć dostępu. Jest to podobne do tego, co robi Drupal, gdy niepublikowany węzeł jest pokazywany tylko użytkownikom, którzy mają uprawnienia do wyświetlania niepublikowanych węzłów. Jeśli celem kodu jest na przykład wybranie niektórych jednostek, które są sukcesywnie usuwane (np. Podczas zadań CRON), to ominięcie kontroli dostępu nie wyrządzi żadnej szkody i jest to jedyny sposób, aby kontynuować.
źródło
->extend('PagerDefault');
na początku)