EntityFieldQuery é realmente tão ineficiente?

11

Sou um novato admitido na API de entidades, mas estou tentando curar isso. Estou trabalhando em um site que usa vários tipos de conteúdo com vários campos anexados a eles; nada chique. Portanto, quando quero recuperar um conjunto de entradas, na minha ignorância, liguei diretamente para o banco de dados e fiz algo assim:

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

(sim, eu provavelmente poderia recolher várias dessas linhas em uma única $the_questions->declaração; por favor, ignore isso por enquanto.)

Tentando reescrever isso com EntityFieldQuery, eu venho com:

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

o que me dá os resultados desejados e é certamente muito mais bonito.

Então, agora estou me perguntando sobre desempenho. Para começar, jogo cada um desses bits de código em um for()loop estúpido , capturando time()antes e depois da execução. Eu executo cada versão 100 vezes em um banco de dados não muito grande e obtenho algo assim:

  • Versão direta: 110 ms
  • Versão EFQ: 4943 ms

Obviamente, obtenho resultados diferentes quando refiz o teste, mas os resultados estão consistentemente no mesmo estádio.

Caramba. Estou fazendo algo errado aqui, ou isso é apenas o custo do EFQ? Eu não fiz nenhum ajuste especial no banco de dados com relação aos tipos de conteúdo; são exatamente o que vem da definição dos tipos de conteúdo da maneira usual e baseada em formulários. Alguma ideia? O código EFQ é definitivamente mais limpo, mas eu realmente não acho que posso pagar um desempenho de 40x.

Jim Miller
fonte
3
você pode despejar as duas consultas sql geradas?
Andre Baumeier 12/03
1
Veja este caso não tenha certeza de como obter o SQL de um EFQ
Clive
2
OK, há progresso: o que está acontecendo aqui é que meu site possui várias regras de acesso ao nó que aumentam bastante o tamanho da consulta. Aqueles estavam sendo aplicados automaticamente à consulta EFQ (mesmo que não exista ->addTag('node_access')na consulta ??). Eu redireciono a consulta "direta" com uma tag node_access, e os tempos de execução são muito mais próximos: o tempo do EFQ agora é apenas um fator 2 maior que a abordagem direta, o que parece razoável, considerando o SQL relativo que ambos estão produzindo (o que Posso postar se as pessoas ainda se importam). (continua na próxima comentário ....)
Jim Miller
Então agora a pergunta, eu acho, é por que estou recebendo automaticamente o material node_access na versão EFQ? Eu pensei que você tinha que pedir explicitamente através da cláusula addTag ()?
Jim Miller

Respostas:

10

A EntityFieldQueryclasse é tão eficiente quanto seus requisitos o permitem. Ele precisa ser compatível com qualquer classe de armazenamento de campo, mesmo com aquelas que usam um mecanismo NoSQL para armazenar os dados de campo, como o que usa o MongoDB . Por esse motivo, EntityFieldQuerynão é possível consultar diretamente o banco de dados, porque o backend de armazenamento de campo atual pode não usar um banco de dados SQL.

Mesmo no caso de o armazenamento em campo usar um mecanismo SQL para armazenar seus dados, o equivalente $query->leftJoin('field_data_field_user_role', 'role', 'n.nid = role.entity_id'); $query->condition('role.field_user_role_value', $some_value);a da EntityFieldQueryclasse exige:

  • Código para criar o nome da tabela do banco de dados a partir do nome do campo
  • Código para criar a condição a ser usada para associar a tabela que contém os dados do campo à tabela que contém os dados da entidade
  • Código para criar o nome da linha do banco de dados que contém os dados do campo

A diferença é imediatamente visível: em um caso, você está usando três cadeias de caracteres literais, enquanto no outro caso há um código que (no mais simples dos casos) está concatenando cadeias de caracteres.

Conforme seu comentário sobre o código que verifica se o usuário tem permissão para acessar os campos, você pode ignorá-lo usando a linha a seguir no código usando a EntityFieldQueryclasse

$query->addTag('DANGEROUS_ACCESS_CHECK_OPT_OUT');

Isso funciona se você estiver usando o Drupal 7.15 ou superior; para versões anteriores, você deve usar o seguinte código.

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

Como de costume, você não deve ignorar a permissão de acesso se o código puder mostrar às informações do usuário às quais o usuário não deve ter acesso. Isso é semelhante ao que é feito no Drupal quando um nó não publicado é mostrado apenas aos usuários que têm permissão para ver nós não publicados. Se o objetivo do código é, por exemplo, selecionar algumas entidades que são excluídas sucessivamente (por exemplo, durante tarefas cron), ignorar o controle de acesso não causa nenhum dano e é a única maneira de prosseguir.

kiamlaluno
fonte
devo admitir que eu sou, provavelmente, não é certo, desde a primeira consulta usa um pager, também (eu não notar o ->extend('PagerDefault');em primeiro lugar)
mojzis
Opa, você está certo.
kiamlaluno
isso me deixou realmente interessado, então estou tentando algo ao longo das linhas do experimento acima e não posso confirmar a enorme diferença nos números ... alguém poderia tentar também, por favor?
Mjzis 12/03
Portanto, apenas para confirmar: as chamadas EFQ invocam SEMPRE as regras de acesso ao nó do site, a menos que você faça algo para impedir que isso aconteça (como descrito acima). Direita?
Jim Miller
@JimMiller Isso está correto e é a razão pela qual a tag "DANGEROUS_ACCESS_CHECK_OPT_OUT" foi adicionada ao Drupal 7.15.
kiamlaluno