EntityFieldQuery è davvero così inefficiente?


11

Sono un principiante ammesso all'API Entity, ma sto cercando di risolverlo. Sto lavorando su un sito che utilizza numerosi tipi di contenuto con vari campi ad essi collegati; nulla di bello. Quindi, quando voglio recuperare una serie di voci, nella mia ignoranza ho chiamato direttamente nel database e fatto qualcosa del genere:

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

(sì, probabilmente potrei comprimere un mucchio di queste righe in una singola $the_questions->affermazione; per ora ignoralo.)

Cercando di riscriverlo con EntityFieldQuery, mi viene in mente:

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

che mi dà i risultati desiderati ed è sicuramente molto più bello.

Quindi, ora mi chiedo delle prestazioni. Come inizio, lancio ciascuno di quei frammenti di codice in uno stupido for()loop, catturando time()prima e dopo l'esecuzione. Eseguo ogni versione 100 volte su un database non molto grande e ottengo qualcosa del genere:

  • Versione diretta: 110 msec
  • Versione EFQ: 4943 msec

Ovviamente ottengo risultati diversi quando rieseguo il test, ma i risultati sono coerentemente nello stesso campo di baseball.

Yikes. Sto facendo qualcosa di sbagliato qui, o è solo il costo dell'utilizzo di EFQ? Non ho effettuato alcuna speciale ottimizzazione del database rispetto ai tipi di contenuto; sono solo ciò che deriva dalla definizione dei tipi di contenuto nel solito modo basato sui moduli. qualche idea? Il codice EFQ è decisamente più pulito, ma non credo proprio di potermi permettere un successo di prestazioni 40x.


3
puoi scaricare entrambe le query sql generate?
Andre Baumeier,

1
Vedi questo se non sei sicuro di come estrarre l'SQL da un EFQ
Clive

2
OK, ci sono progressi: quello che sta succedendo qui è che il mio sito ha un sacco di regole di accesso ai nodi che stanno aumentando un po 'le dimensioni della query. Quelli venivano applicati automaticamente alla query EFQ (anche se non c'è ->addTag('node_access')nella query ??). Ripeto la query "diretta" con un tag node_access e i tempi di esecuzione sono molto più vicini: il tempo di EFQ ora è solo circa un fattore 2 maggiore dell'approccio diretto, che sembra ragionevole dato il relativo SQL che entrambi stanno pompando (che Posso pubblicare se alle persone interessa ancora). (proseguendo sul prossimo commento ....)
Jim Miller

Quindi ora la domanda, immagino, è perché sto ricevendo automaticamente le cose node_access nella versione EFQ? Pensavo che dovessi richiederlo esplicitamente tramite la clausola addTag () ??
Jim Miller,

Risposte:


10

La EntityFieldQueryclasse è efficiente quanto i suoi requisiti lo consentono. Deve essere compatibile con qualsiasi classe di archiviazione sul campo, anche con quelle che utilizzano un motore NoSQL per archiviare i dati del campo, come quello che utilizza MongoDB . Per tale motivo, EntityFieldQuerynon è possibile eseguire una query diretta sul database, poiché il back-end di archiviazione del campo corrente potrebbe non utilizzare affatto un database SQL.

Anche nel caso in cui l'archiviazione dei campi utilizzi un motore SQL per archiviare i suoi dati, l'equivalente di $query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);per la EntityFieldQueryclasse richiede:

  • Codice per creare il nome della tabella del database dal nome del campo
  • Codice per creare la condizione da utilizzare per unire la tabella contenente i dati del campo con la tabella contenente i dati dell'entità
  • Codice per creare il nome della riga del database contenente i dati del campo

La differenza è immediatamente visibile: in un caso si utilizzano tre stringhe di lettiera, mentre nell'altro caso è presente un codice che (nel caso più semplice) sta concatenando le stringhe.

Secondo il tuo commento sul codice che controlla se l'utente ha l'autorizzazione ad accedere ai campi, puoi ignorarlo usando la seguente riga al codice usando la EntityFieldQueryclasse.

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

Funziona se stai usando Drupal 7.15 o successivo; per le versioni precedenti, è necessario utilizzare il seguente codice.

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

Come al solito, non è necessario ignorare l'autorizzazione di accesso se il codice potrebbe mostrare all'utente le informazioni a cui l'utente non dovrebbe avere accesso. Questo è simile a quello che viene fatto da Drupal quando un nodo non pubblicato viene mostrato solo agli utenti che hanno il permesso di vedere nodi non pubblicati. Se lo scopo del codice è, ad esempio, selezionare alcune entità che vengono successivamente cancellate (ad esempio durante le attività cron), il by-pass del controllo di accesso non provoca alcun danno ed è l'unico modo per procedere.


dovrei ammettere che probabilmente non ho ragione, dato che anche la prima query usa un cercapersone (all'inizio non l'ho notato ->extend('PagerDefault');)
mojzis

Spiacenti, hai ragione.
kiamlaluno

questo mi ha davvero interessato, quindi sto provando qualcosa sulla falsariga dell'esperimento sopra e non posso confermare l'enorme differenza nei numeri ... qualcuno potrebbe provarlo, per favore?
Mojzis,

Quindi, solo per confermare: EFQ chiama SEMPRE invocare le regole di accesso al nodo del sito a meno che tu non faccia qualcosa per impedire che ciò accada (come descritto sopra). Giusto?
Jim Miller,

@JimMiller È corretto ed è il motivo per cui il tag "DANGEROUS_ACCESS_CHECK_OPT_OUT" è stato aggiunto a Drupal 7.15.
kiamlaluno
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.