Ist EntityFieldQuery wirklich so ineffizient?

11

Ich bin ein zugelassener Neuling in der Entity-API, aber ich versuche, das zu heilen. Ich arbeite an einer Site, die eine Reihe von Inhaltstypen mit verschiedenen Feldern verwendet. Nichts Besonderes. Wenn ich also eine Reihe von Einträgen abrufen möchte, habe ich in meiner Unwissenheit direkt in die Datenbank aufgerufen und so etwas getan:

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

(Ja, ich könnte wahrscheinlich ein paar dieser Zeilen zu einer einzigen $the_questions->Anweisung zusammenfassen. Bitte ignorieren Sie dies vorerst.)

Beim Versuch, dies mit EntityFieldQuery neu zu schreiben, habe ich Folgendes gefunden:

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

Das gibt mir die gewünschten Ergebnisse und ist sicherlich viel schöner.

Jetzt wundere ich mich über die Leistung. Zunächst werfe ich jedes dieser Codebits in eine blöde for()Schleife und nehme sie time()vor und nach der Ausführung auf. Ich führe jede Version 100 Mal über eine nicht sehr große Datenbank aus und erhalte so etwas:

  • Direkte Version: 110 ms
  • EFQ-Version: 4943 ms

Natürlich erhalte ich unterschiedliche Ergebnisse, wenn ich den Test erneut durchführe, aber die Ergebnisse befinden sich durchweg im selben Bereich.

Huch. Mache ich hier etwas falsch oder sind dies nur die Kosten für die Verwendung von EFQ? Ich habe keine spezielle Datenbankoptimierung in Bezug auf die Inhaltstypen durchgeführt. Sie sind genau das, was sich aus der Definition der Inhaltstypen auf die übliche, formularbasierte Weise ergibt. Irgendwelche Gedanken? Der EFQ-Code ist definitiv sauberer, aber ich glaube wirklich nicht, dass ich mir einen 40-fachen Leistungseinbruch leisten kann.

Jim Miller
quelle
3
Können Sie beide generierten SQL-Abfragen sichern?
Andre Baumeier
1
Sehen Sie sich diese an, wenn Sie nicht sicher sind, wie Sie SQL aus einem EFQ
Clive
2
OK, es gibt Fortschritte: Was hier vor sich geht, ist, dass meine Site eine Reihe von Knotenzugriffsregeln hat, die die Größe der Abfrage erheblich erhöhen. Diese wurden automatisch auf die EFQ-Abfrage angewendet (obwohl ->addTag('node_access')die Abfrage keine enthält ??). Ich habe die "direkte" Abfrage mit einem node_access-Tag erneut durchgeführt, und die Ausführungszeiten sind viel näher: Die Zeit von EFQ ist jetzt nur um den Faktor 2 größer als der direkte Ansatz, der angesichts der relativen SQL, die beide auspumpen (was vernünftig erscheint), vernünftig erscheint Ich kann posten, wenn sich die Leute noch darum kümmern. (Fortsetzung beim nächsten Kommentar ....)
Jim Miller
Nun stellt sich wohl die Frage, warum ich das node_access-Zeug in der EFQ-Version automatisch erhalte. Ich dachte, Sie müssten explizit über die addTag () -Klausel danach fragen?
Jim Miller

Antworten:

10

Die EntityFieldQueryKlasse ist so effizient, wie es ihre Anforderungen zulassen. Es muss mit allen Feldspeicherklassen kompatibel sein, auch mit solchen, die eine NoSQL-Engine zum Speichern der Felddaten verwenden, z. B. die, die MongoDB verwendet . Aus diesem Grund EntityFieldQuerykann die Datenbank nicht direkt abgefragt werden, da das aktuelle Feldspeicher-Backend möglicherweise überhaupt keine SQL-Datenbank verwendet.

Selbst wenn der Feldspeicher eine SQL-Engine zum Speichern seiner Daten verwendet, erfordert das Äquivalent $query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);für die EntityFieldQueryKlasse:

  • Code zum Erstellen des Datenbanktabellennamens aus dem Feldnamen
  • Code zum Erstellen der Bedingung zum Verknüpfen der Tabelle mit den Felddaten mit der Tabelle mit den Entitätsdaten
  • Code zum Erstellen des Namens der Datenbankzeile, die die Felddaten enthält

Der Unterschied ist sofort sichtbar: In einem Fall verwenden Sie drei Wurfzeichenfolgen, während es in dem anderen Fall Code gibt, der (im einfachsten Fall) Zeichenfolgen verkettet.

Gemäß Ihrem Kommentar zu Code, der prüft, ob der Benutzer die Berechtigung zum Zugriff auf die Felder hat, können Sie diesen mithilfe der folgenden Zeile mit der EntityFieldQueryKlasse an den Code umgehen .

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

Dies funktioniert, wenn Sie Drupal 7.15 oder höher verwenden. Für frühere Versionen sollten Sie den folgenden Code verwenden.

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

Wie üblich sollten Sie die Zugriffsberechtigung nicht umgehen, wenn der Code den Benutzerinformationen anzeigen könnte, auf die der Benutzer keinen Zugriff haben sollte. Dies ähnelt der Vorgehensweise von Drupal, wenn ein unveröffentlichter Knoten nur den Benutzern angezeigt wird, die die Berechtigung haben, unveröffentlichte Knoten anzuzeigen. Wenn der Zweck des Codes beispielsweise darin besteht, einige Entitäten auszuwählen, die nacheinander gelöscht werden (z. B. während Cron-Aufgaben), schadet die Umgehung der Zugriffssteuerung nicht und ist die einzige Möglichkeit, fortzufahren.

kiamlaluno
quelle
Ich sollte zugeben, dass ich wahrscheinlich nicht richtig bin, da die erste Abfrage auch einen Pager verwendet (ich habe das ->extend('PagerDefault');zuerst nicht bemerkt )
Mojzis
Hoppla, du hast recht.
Kiamlaluno
Das hat mich wirklich interessiert, also versuche ich etwas in Anlehnung an das obige Experiment und kann den großen Unterschied in den Zahlen nicht bestätigen ... könnte es bitte auch jemand versuchen?
Mojzis
Nur zur Bestätigung: EFQ-Aufrufe rufen IMMER die Knotenzugriffsregeln der Site auf, es sei denn, Sie tun etwas, um dies zu verhindern (wie oben beschrieben). Richtig?
Jim Miller
@JimMiller Das ist richtig und es ist der Grund, warum das Tag "DANGEROUS_ACCESS_CHECK_OPT_OUT" zu Drupal 7.15 hinzugefügt wurde.
Kiamlaluno