valor de campo único de ahorro rápido

19

Tengo alrededor de 70k nodos del tipo especificado en mi sitio. Necesito ejecutar una actualización sobre ellos. Algunas operaciones y establecer un campo al valor deseado. node_savees realmente lento y causa fallas (demasiado largo callstack mayby). ¿Hay alguna forma más rápida de escribir información sobre este campo en particular?

Se field_attach_updatemencionó en una publicación, pero no es mucho más rápido.

EDITAR: Hay una vista bastante compleja construida en este tipo de nodo, pero no funciona en este campo que quiero actualizar.

Eloar
fuente

Respuestas:

30

Definitivamente lo haría field_attach_update.

La idea es simple. Simplemente cargue el nodo y guárdelo 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));

Esto no cambiará ninguna marca de tiempo ni ningún otro gancho que invoque node_save. Cargar el nodo también invocará algunos ganchos, por lo que probablemente no sea tan eficiente.

Si tiene el nid y si la estructura del nodo es muy simple, también puede hacerlo así:

 $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 todos modos, si está intentando actualizar algo que no sean campos, esto no funcionará (estado de comentarios, estado publicado, etc.). Además, si está utilizando node_save, la memoria caché para el nodo en particular se borrará automáticamente para los diferentes métodos que necesitamos borrar con 'entity_get_controller'.

Actualización: Parece que también debe llamar field_attach_presave()para permitir que otros módulos procesen la entrada de campo correctamente. El módulo de archivo, por ejemplo, lo usa para establecer el estado del archivo en permanente usando este enlace. He actualizado mis 2 ejemplos anteriores.

AyeshK
fuente
Tengo nid pero la estructura no es tan simple para el nodo, pero el campo que quiero actualizar es muy simple. ¿Qué podría esperar de configurar solo un campo para el nodo existente (identificado por nid pero no cargado) y luego llamar field_attach_update?
Eloar
44
A medida que resultó, el uso de la consulta de entidad ralentizó las cosas. Así se cambia de node_savea field_attach_updatey desde EntityFieldQueryque db_query_rangefue muy gratificante. De 3h actualización a 40 minutos.
Eloar
2

Si no desea guardar datos de campo sin causar que ocurran los eventos y acciones estándar, puede usar drupal_write_record .

Aquí hay un ejemplo para insertar Hello World en el campo del cuerpo para un nodo de tipo artículo con una identificación de 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);

Si su sitio es multlingual, entonces querrá usar 'en' o el idioma de su contenido en lugar de 'und'.

Si está realizando una revisión, deberá tener cuidado de insertar la identificación de revisión correcta; de lo contrario, simplemente puede insertar el mismo valor que la entidad_id.

Observe cómo se insertan estos datos en dos tablas field_data_ * y field_revision_ *. Debe insertar en ambos para asegurarse de que el sitio funciona como lo desea.

Después de ejecutar esto, deberá borrar los cachés para que se muestren los campos, según cómo esté configurado el almacenamiento en caché.

Thomas4019
fuente
2

Para una actualización simple como esta donde se necesitan actualizar muchos nodos, siempre uso una declaración de actualización de MySQL. Sí, es necesario tener en cuenta el almacenamiento en caché, pero puede limpiar el caché una vez que haya terminado y todo está bien. Usted, por supuesto, debe estar familiarizado con la estructura de datos, pero es relativamente simple en Drupal 6. (aunque horrible en Drupal 7)

Troy Frech
fuente
El proyecto se creó para Drupal 7. Después de que todas las partes de mi módulo se manipularan directamente en la estructura de Drupal DB para leer valores y buscar como consultas SQL, eran mucho más rápidas que EntityFieldQueries.
Eloar
2

También sugiero field_attach_update, y no una consulta directa de SQL, porque sql no actualiza el objeto de caché del nodo, y en su próximo node_loadno cargará el valor del campo actualizado, cargará el valor anterior

field_attach_update es mucho mejor que la consulta directa de SQL.

pico34
fuente
Ayesh K sugirió crear un nodo como stdClassobjeto sin cargar. ¿Sabes qué podría suceder si intento actualizar el nodo de esta manera sin configurar todos los campos? ¿Se sobrescribirán con nulos o valores predeterminados? ¿Puede ser ignorado por el proceso de actualización?
Eloar
1
Es posible guardar un nodo directamente mediante el método Ayeshs (nueva clase std, etc.) solo la interfaz de usuario realiza validaciones en los campos obligatorios
pico34
Intentaré actualizar el nodo con este método sin configurar todos los campos y verificar lo que sucederá. Podría ser el método más rápido para actualizar nodos en el procedimiento de actualización del módulo (lote).
Eloar
2

Después de probar todos los enfoques mencionados en las otras respuestas, obtuve tiempos de actualización muy lentos (aproximadamente 7 días para 700,000 nodos de un tipo de nodo con más de 20 campos) hasta que encontré este artículo: http://www.drupalonwindows.com/en/ blog / only-update-changed-fields-or-properties-entity-drupal .

Después de implementar algo como el código a continuación en un hook_update, reduje el tiempo de actualización a 2 horas, lo que creo que es manejable.

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
fuente
1

Incluso tenía el mismo requisito de actualizar un campo para todos los nodos de un tipo de contenido en particular. Solía node_load_multiple y 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);
}

Lo puse en seco y fue bastante rápido.

Suresh R
fuente
0

¿Ha considerado hacer estas actualizaciones directamente en la base de datos usando mySQL? Probablemente sea la forma más sencilla y rápida de lograr lo que desea.

Aquí hay un ejemplo simple. Puede ejecutar dicho comando desde la pestaña 'SQL' en phpMyAdmin. Imagine que tiene un tipo de contenido llamado Perfil de miembro. En él tiene un campo llamado 'Tipo de miembro' (por ejemplo, empresa, individuo, organización). Supongamos que desea actualizar todas las ocurrencias de 'EMPRESA' a 'empresa'. El siguiente comando hará exactamente eso.

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

Además, vaya a Comenzar con MySQL

Bisonbleu
fuente
Es una de las opciones. Lo dejo como el último para verificar e implementar. No quiero estropear demasiado la estructura y fallas de drupal. Entonces, primero busco cualquier solución directamente a través de la API de Drupal. Si pudiera proporcionar algunos ejemplos, sería muy apreciado.
Eloar
Buen punto. Agregué un ejemplo simple a mi respuesta inicial.
Bisonbleu
eh, conozco SQL lo suficientemente bien como para actualizar cualquier registro en cualquier tabla. No es el problema El problema es que no estoy lo suficientemente familiarizado con la forma en que drupal almacena los datos (especialmente los metadatos). Siempre hay mucho almacenamiento en caché, preservación, etc. en tales sistemas, por lo que actualizarlo en el nivel más bajo podría (no siempre) estropear algunos defectos. Entonces, si se trata de esto, intentaré hacerlo en el nivel más bajo, y estaré listo para un verdadero desastre.
Eloar