web-dev-qa-db-ja.com

EntityFieldQueryは本当にこれは非効率ですか?

私は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ミリ秒

テストを再実行すると、明らかに異なる結果が得られますが、結果は一貫して同じ球場にあります。

うわぁ。私はここで何か悪いことをしていますか、それともこれはEFQを使用するコストだけですか?コンテンツタイプに関して特別なデータベースチューニングは行っていません。これらは、通常のフォームベースの方法でコンテンツタイプを定義することから得られるものです。何かご意見は? EFQコードは間違いなくよりクリーンですが、40倍のパフォーマンスヒットができるとは思えません。

11
Jim Miller

EntityFieldQueryクラスは、その要件で可能な限り効率的です。 MongoDBを使用するもの のように、NoSQLエンジンを使用してフィールドデータを格納するクラスであっても、フィールドストレージクラスと互換性がある必要があります。そのため、現在のフィールドストレージバックエンドはSQLデータベースをまったく使用しない可能性があるため、EntityFieldQueryはデータベースに直接クエリを実行できません。

フィールドストレージがSQLエンジンを使用してデータを格納する場合でも、EntityFieldQueryクラスの$query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);と同等のものが必要です。

  • フィールド名からデータベーステーブル名を作成するコード
  • フィールドデータを含むテーブルとエンティティデータを含むテーブルを結合するために使用する条件を構築するコード
  • フィールドデータを含むデータベース行の名前を作成するコード

違いはすぐにわかります。1つのケースでは、3つの文字列を使用していますが、もう1つのケースでは、(最も単純なケースでは)文字列を連結するコードがあります。

ユーザーがフィールドにアクセスする権限を持っているかどうかを確認するコードに関するコメントに従って、次の行を使用してEntityFieldQueryクラスを使用するコードにバイパスできます。

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

これは、Drupal 7.15以降を使用している場合に機能します。以前のバージョンでは、次のコードを使用する必要があります。

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

いつものように、ユーザーがアクセスできないはずの情報をコードがユーザーに表示できる場合は、アクセス許可をバイパスしないでください。これは、Drupalから行われるものと同様です。非公開ノードが非公開ノードを表示する権限を持つユーザーにのみ表示される場合。コードの目的が、たとえば、連続して削除されます(例:cronタスクの実行中)。その後、アクセス制御をバイパスしても害はありません。これが続行する唯一の方法です。

10
kiamlaluno