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


19

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

そこたfield_attach_updateつのポストで述べたが、それははるかに高速ではありません。

編集:このノードタイプに構築された非常に複雑なビューがありますが、更新したいこのフィールドでは動作していません。

回答:


30

絶対に行きたいです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));

これにより、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つの例を更新しました。


私はnidを持っていますが、ノードの構造はそれほど単純ではありませんが、更新したいフィールドは非常に単純です。既存のノード(nidで識別されるがロードされない)に1つのフィールドのみを設定してから呼び出すことで期待できることは何field_attach_updateですか?
エロール

4
エンティティクエリを使用すると結果が遅くなりました。そのnode_saveためfield_attach_update、「から」EntityFieldQueryへの切り替えdb_query_rangeは非常にやりがいがありました。3時間の更新から40分まで。
エロア

2

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

IDが1のarticleタイプのノードの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

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


プロジェクトはDrupal 7用に作成されました。SQLクエリはEntityFieldQueriesよりもはるかに高速であったため、値の読み取りと検索のために、Drupal DB構造を直接操作するモジュールのすべての部分が作成されました。
エロア14

2

field_attach_updatesqlはノードキャッシュオブジェクトを更新せず、次にnode_load更新されたフィールド値をロードしないので、古い値をロードするため、SQLクエリではなく、私もお勧めします。

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


Ayesh Kは、stdClassロードせずにオブジェクトとしてノードを作成することを提案しました。すべてのフィールドを設定せずにこの方法でノードを更新しようとするとどうなるかご存知ですか?これらはヌルまたはデフォルトで上書きされますか?プロセスを更新することで、これらが無視される可能性がありますか?
エアロ

1
Ayeshsメソッド(新しいstdClassなど)によってノードを直接保存することは可能です。UIのみが必須フィールドで検証を行います
-pico34

すべてのフィールドを設定せずにこのメソッドでノードを更新し、何が起こるかを確認します。モジュール更新手順(バッチ)でノードを更新するための最速の方法かもしれません。
エアロ

2

他の回答に記載されているすべてのアプローチを試した後、この記事を見つけるまで、更新時間が非常に遅くなりました(20+フィールドを持つノードタイプの700.000ノードで約7日)。ブログ/のみ更新-変更-フィールド・オア・プロパティ・エンティティ- 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'];

1

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

私はそれを駆け抜けて走りましたが、それはかなり迅速でした。


0

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

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

UPDATE content_type_member_profile SET field_type_of_member_value= 'company' WHERE field_type_of_member_value= 'COMPANY';

また、MySQLの使用を開始する


これはオプションの1つです。確認して実装する最後のものとして残します。drupal構造と欠陥をめちゃくちゃにしたくありません。そこでまず、Drupal APIから直接ソリューションを探します。いくつかの例を提供できれば、とてもありがたいです。
エロア

ググポイント。最初の答えに簡単な例を追加しました。
ビソンブルー

ええ、私はSQLをよく知っていて、どのテーブルのどのレコードも更新できます。問題ではありません。問題は、drupalがデータ(特にメタデータ)を保存する方法について十分な知識がないことです。そのようなシステムには常に多くのキャッシングや保存などがあるため、最低レベルで更新すると、(常にではないが)いくつかの欠陥が台無しになる可能性があります。ですから、これに至ったら、最低レベルでやろうとします。そして、本当の混乱に備えます。
エロール
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.