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->语句;请暂时忽略它。)

试图用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毫秒

显然,当我重新运行测试时,我得到了不同的结果,但是结果始终如一。

kes 我在这里做错什么了吗,还是仅仅是使用EFQ的代价?对于内容类型,我还没有进行任何特殊的数据库调整。它们就是以通常的基于表单的方式定义内容类型的结果。有什么想法吗?EFQ代码绝对更干净,但是我真的认为我无法承受40倍的性能冲击。


3
您可以转储两个生成的SQL查询吗?
Andre Baumeier

1
这一个,如果你不知道如何让SQL出EFQ的
克莱夫

2
OK,有进展:这里发生的是我的站点上有一堆节点访问规则,这些规则正在极大地增加查询的大小。这些已自动应用于EFQ查询(即使查询中没有->addTag('node_access'))。我使用node_access标记重新运行了“直接”查询,执行时间更加接近:EFQ​​的时间现在仅比直接方法大2倍,考虑到两者都被抽出的相对SQL,这看起来是合理的(如果有人还在乎,我可以发帖)。(续接下一条评论...。)
Jim Miller

因此,我想现在的问题是为什么我要自动获得EFQ版本的node_access东西?我以为您必须通过addTag()子句明确要求它?
Jim Miller

Answers:


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 2013年

哎呀,你是对的。
kiamlaluno

这让我非常感兴趣,所以我正在尝试按照上述实验的方法进行操作,但无法确认数字上的巨大差异……也有人可以尝试吗?
mojzis 2013年

因此,仅需确认一下:EFQ调用始终会调用站点的节点访问规则,除非您采取措施阻止该事件发生(如上所述)。对?
Jim Miller

@JimMiller没错,这就是为什么将“ DANGEROUS_ACCESS_CHECK_OPT_OUT”标签添加到Drupal 7.15的原因。
kiamlaluno
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.