economia rápida de valor de campo único

19

Eu tenho cerca de 70k nós do tipo especificado no meu site. Eu preciso executar uma atualização neles. Algumas operações e definindo um campo para o valor desejado. node_saveé realmente lento e causa falhas (muito tempo de pilha de chamada). Existe uma maneira mais rápida de escrever informações nesse campo em particular?

field_attach_updateIsso foi mencionado em um post, mas não é muito mais rápido.

EDIT: existe uma visão bastante complexa construída sobre este tipo de nó, mas não está funcionando neste campo que eu quero atualizar.

Eloar
fonte

Respostas:

30

Eu definitivamente iria field_attach_update .

A ideia é simples. Basta carregar o nó e salvá-lo usando field_attach_update.

Ex:

$node = node_load($nid);
$node->field_name[LANGUAGE_NONE][0]['value'] = 'New value';
field_attach_presave('node', $node);
field_attach_update('node', $node);
  // Clear the static loading cache.
entity_get_controller('node')->resetCache(array($node->nid));

Isso não alterará nenhum registro de data e hora ou qualquer outro gancho que o node_save normalmente invoque. Carregar o nó também chamará alguns ganchos, portanto, provavelmente não é tão eficiente.

Se você possui o nid e a estrutura do nó é simples, você também pode fazer o seguinte:

 $node = new stdClass();
 $node->nid = $nid; // Enter the nid taken. Make sure it exists. 
 $node->type = 'article';
 $node->field_name[LANGUAGE_NONE][0]['value'] = 'New value';
 field_attach_presave('node', $node);
 field_attach_update('node', $node);
  // Clear the static loading cache.
 entity_get_controller('node')->resetCache(array($node->nid));

De qualquer forma, se você estiver tentando atualizar algo diferente de campos, isso não funcionará (status do comentário, status publicado, etc.). Além disso, se você estiver usando node_save, o cache para o nó específico será limpo automaticamente para diferentes métodos, precisamos limpá-lo com 'entity_get_controller'.

Atualização: Parece que você também deve ligar field_attach_presave()para permitir que outros módulos processem a entrada de campo corretamente. O módulo de arquivo, por exemplo, usa-o para definir o status do arquivo como permanente usando este gancho. Eu atualizei meus 2 exemplos acima.

AyeshK
fonte
Eu tenho nid, mas a estrutura não é tão simples para o nó, mas o campo que eu quero atualizar é simples. O que eu poderia esperar de definir apenas um campo para o nó existente (identificado por nid, mas não carregado) e depois chamar field_attach_update?
Eloar # 8/13
4
À medida que se desviava usando a consulta de entidade, as coisas ficavam mais lentas. Assim, a mudança de node_saveque field_attach_updatee de EntityFieldQueryque db_query_rangefoi muito gratificante. De 3h de atualização para 40 minutos.
precisa
2

Se você não deseja salvar os dados do campo sem fazer com que os eventos e ações padrão aconteçam, use drupal_write_record .

Aqui está um exemplo para inserir Hello World no campo body para um nó do tipo article com um ID 1.

$values = array(
  'entity_type' => 'node',
  'bundle' => 'article',
  'entity_id' => 1,
  'revision_id' => 1,
  'language' => 'und',
  'delta' => 0,
  'body_value' => 'HELLO WORLD',
  'body_summary' => '',
  'body_format' => 'filtered_html',
);
drupal_write_record('field_data_body', $values);
drupal_write_record('field_revision_body', $values);

Se o seu site for multilíngüe, convém usar 'en' ou o idioma do seu conteúdo em vez de 'und'.

Se você estiver revisando, precisará ter o cuidado de inserir o ID de revisão correto, caso contrário, pode simplesmente inserir o mesmo valor que o entity_id.

Observe como esses dados são inseridos em duas tabelas field_data_ * e field_revision_ *. Você deve inserir os dois para garantir que o site funcione conforme desejado.

Depois de executar isso, você precisará limpar os caches para os campos aparecerem, dependendo de como o cache estiver configurado.

Thomas4019
fonte
2

Para uma atualização simples como essa, na qual muitos nós precisam ser atualizados, eu sempre uso uma instrução de atualização do MySQL. Sim, o armazenamento em cache precisa ser levado em consideração, mas você pode apenas liberá-lo após concluir e tudo fica bem. Você, é claro, precisa estar familiarizado com a estrutura de dados, mas é relativamente simples no Drupal 6. (embora horrendo no Drupal 7)

Troy Frech
fonte
O projeto foi criado para o Drupal 7. Depois que todas as partes do meu módulo foram feitas para manipular diretamente a estrutura do Drupal DB para ler valores e pesquisar como consultas sql, foram muito mais rápidas que EntityFieldQueries.
Eloar
2

Sugiro field_attach_updatetambém, e não uma consulta SQL direta, porque o sql não atualiza o objeto de cache do nó e em sua próximanode_load você não carregará o valor do campo atualizado, você carregará o valor antigo

field_attach_update é muito melhor do que a consulta SQL direta.

pico34
fonte
Ayesh K sugeriu a criação de nó como stdClassobjeto sem carregar. Você sabe o que pode acontecer se eu tentar atualizar o nó dessa maneira sem definir todos os campos? Eles serão substituídos por valores nulos ou padrões? Talvez eles sejam ignorados pelo processo de atualização?
Eloar
1
É possível salvar um nó diretamente pelo método Ayeshs (novo stdClass etc ...) só a interface do usuário faz valida sobre campos obrigatórios
pico34
Vou tentar atualizar o nó com esse método sem definir todos os campos e verificar o que acontecerá. Pode ser o método mais rápido para atualizar nós no procedimento de atualização do módulo (lote).
Eloar
2

Tendo tentado todas as abordagens mencionadas nas outras respostas, obtive tempos de atualização muito lentos (cerca de 7 dias para 700.000 nós de um tipo de nó com mais de 20 campos) até encontrar este artigo: http://www.drupalonwindows.com/en/ blog / somente atualização-alterada-campos-ou-propriedades-entidade-drupal .

Depois de implementar algo como o código abaixo em um hook_update, reduzi o tempo de atualização para 2 horas, o que eu acho que é gerenciável.

if (!isset($sandbox['storage']['nids'])) {
    $sandbox['storage']['nids'] = [];
    $query = 'SELECT {nid} FROM node WHERE type = \'article\';';
    $result = db_query($query)->fetchCol();
    if ($result) {
      $sandbox['storage']['nids'] = $result;
      $sandbox['storage']['total'] = count($sandbox['storage']['nids']);
      $sandbox['storage']['last_run_time'] = time();
      $sandbox['progress'] = 0;
    }
  }

  $amount = 300;
  $nids = array_slice($sandbox['storage']['nids'], 0, $amount);

  if (!empty($nids)) {
    $nodes = node_load_multiple($nids, [], TRUE);
    foreach ($nodes as $node) {
      // Lets manipualte the entity.
      $article_wrapper = UtilsEntity::entity_metadata_wrapper('node', $node);
      // Eventual logic here.

        // Field to update
        $article_wrapper->my_field = 'my_value';
        $article_wrapper->save();

    $sandbox['progress']++;
    }
    $sandbox['message'] = 'Runs left: ' . (($sandbox['storage']['total'] - $sandbox['progress'])/$amount) . ' Progress: ' . (($sandbox['progress'] * $amount)/$sandbox['storage']['total']) . '%';
    $sandbox['storage']['last_run_time'] = time();
    unset($nids);
  }
  $sandbox['storage']['nids'] = array_slice($sandbox['storage']['nids'], 100, count($sandbox['storage']['nids']));
  if (!empty($sandbox['storage']['total'])) {
    $sandbox['#finished'] = ($sandbox['storage']['total'] - count($sandbox['storage']['nids'])) / $sandbox['storage']['total'];
  }
  return $sandbox['message'];
dasj19
fonte
1

Eu ainda tinha o mesmo requisito de atualizar um campo para todos os nós de um tipo de conteúdo específico. Eu usei node_load_multiple e field_attach_update .

$nodes = node_load_multiple(array(), array('type' => 'content_type_name'));
foreach ($nodes as $node) {
  $node->field_name['und'][0]['value'] = 'field value';
  field_attach_update('node', $node);
}

Eu passei pelo drush e foi bem rápido.

Suresh R
fonte
0

Você já pensou em fazer essas atualizações diretamente no banco de dados usando o mySQL? É provavelmente a maneira mais simples e rápida de alcançar o que deseja.

Aqui está um exemplo simples. Você pode executar esse comando na guia 'SQL' no phpMyAdmin. Imagine que você tem um tipo de conteúdo chamado Perfil do Membro. Nele você tem um campo chamado 'Tipo de membro' (por exemplo, empresa, indivíduo, organização). Digamos que você queira atualizar todas as ocorrências de 'COMPANY' para 'company'. O comando a seguir fará exatamente isso.

UPDATE content_type_member_profile SET field_type_of_member_value= 'empresa' WHERE field_type_of_member_value= 'EMPRESA';

Além disso, check-out Introdução ao MySQL

Bisonbleu
fonte
É uma das opções. Deixo como o último a verificar e implementar. Não quero estragar muito a estrutura drupal e as falhas. Primeiro, procuro soluções diretamente através da API Drupal. Se você pudesse fornecer alguns exemplos, seria muito apreciado.
Eloar # 8/13
Ponto Goog. Adicionei um exemplo simples à minha resposta inicial.
Bisonbleu 8/03/2013
eh, eu conheço SQL o suficiente para atualizar quaisquer registros em qualquer tabela. Não é esse o problema. O problema é que eu não estou familiarizado o suficiente com a maneira como o drupal armazena dados (especialmente metadados). Sempre há muito armazenamento em cache, preservação e assim por diante nesses sistemas, portanto, atualizá-lo no nível mais baixo pode (nem sempre) atrapalhar algumas falhas. Portanto, se isso acontecer, tentarei fazê-lo no nível mais baixo e estarei pronto para uma verdadeira bagunça.
Eloar