web-dev-qa-db-ja.com

単一フィールド値の高速保存

私のサイトには、指定されたタイプの約70kのノードがあります。更新を実行する必要があります。一部の操作と1つのフィールドを目的の値に設定。 node_saveは本当に遅く、クラッシュを引き起こします(コールスタックが長すぎる可能性があります)。この1つの特定のフィールドに関する情報を書き込むより速い方法はありますか?

ありました field_attach_updateが1つの投稿で言及されていますが、それほど速くはありません。

編集:このノードタイプには非常に複雑なビューが組み込まれていますが、更新したいこのフィールドでは機能しません。

19
Eloar

私は間違いなく _field_attach_update_ に行きます。

アイデアは簡単です。ノードをロードし、field_attach_updateを使用して保存するだけです。

例:

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

これにより、timestampやnode_saveが通常呼び出すその他のフックは変更されません。ノードをロードすると、いくつかのフックも呼び出されるため、おそらくそれほど効率的ではありません。

Nidがあり、ノード構造が非常に単純な場合は、次のように行うこともできます。

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

とにかく、フィールド以外のものを更新しようとすると、これは機能しません(コメントステータス、公開ステータスなど)。また、node_saveを使用している場合、特定のノードのキャッシュは、「entity_get_controller」でクリアする必要があるさまざまなメソッドに対して自動的にクリアされます。

更新:他のモジュールにフィールド入力を適切に処理させるには、field_attach_presave()も呼び出す必要があるようです。たとえば、ファイルモジュールはこれを使用して、このフックを使用してファイルステータスを永続的に設定します。上記の2つの例を更新しました。

30
AyeshK

他の回答で述べられているすべてのアプローチを試してみたところ、この記事が見つかるまで、更新時間が非常に遅くなりました(ノードタイプが20以上のノードの700.000ノードで約7日間)。 http://www.drupalonwindows。 com/en/blog/only-update-changed-fields-or-properties-entity-drupal

以下のコードのようなものをhook_updateに実装した後、更新時間を2時間に短縮しました。

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'];
2
dasj19

私は提案します field_attach_updateも直接SQLクエリではありません。sqlはノードキャッシュオブジェクトを更新しないため、次のnode_load更新されたフィールド値をロードせず、古い値をロードします

field_attach_updateは、直接SQLクエリよりもはるかに優れています。

2
pico34

標準のイベントとアクションを発生させずにフィールドデータを保存したくない場合は、 drupal_write_record を使用できます。

IDが1のアーティクルタイプのノードのbodyフィールドにHello Worldを挿入する例を次に示します。

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

あなたのサイトが複数である場合は、「und」の代わりに「en」またはコンテンツの言語を使用する必要があります。

リビジョンを行う場合は、正しいリビジョンIDを挿入するように注意する必要があります。そうでない場合は、entity_idと同じ値を挿入できます。

このデータが2つのテーブルfield_data_ *とfield_revision_ *に挿入されていることに注意してください。両方に挿入して、サイトが希望どおりに機能することを確認する必要があります。

これを実行した後、キャッシュの設定方法に応じて、フィールドが表示されるようにキャッシュをクリアする必要があります。

2
Thomas4019

多くのノードを更新する必要があるこのような単純な更新の場合、私は常にMySQL更新ステートメントを使用します。はい、キャッシュを考慮する必要がありますが、完了後にキャッシュをフラッシュするだけで十分です。もちろん、データ構造に精通している必要がありますが、Drupal 6.は比較的簡単です(ただし、Drupal 7)は恐ろしいです)

2
Troy Frech

特定のコンテンツタイプのすべてのノードのフィールドを更新するという同じ要件もありました。 node_load_multiplefield_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);
}

私はそれをドラッシュにかけました、そしてそれはかなり迅速でした。

1
Suresh R

MySQLを使用してこれらの更新をデータベースに直接行うことを検討しましたか?それはおそらく、あなたが望むものを達成するための最も簡単で最も速い方法でしょう。

これは簡単な例です。このようなコマンドは、phpMyAdminの[SQL]タブから実行できます。 Member Profileというコンテンツタイプがあるとします。その中に「メンバーのタイプ」という名前のフィールドがあります(会社、個人、組織など)。 「COMPANY」のすべてのオカレンスを「company」に更新するとします。次のコマンドはそれを行います。

UPDATE content_type_member_profile SET field_type_of_member_value = '会社' WHERE field_type_of_member_value = '会社';

また、チェックアウト Getting Started with MySQL

0
Bisonbleu