mcpField
E
enonic-content-migration

enonic-content-migration

github.com/webmaxruデータ・データベース0143 回インストール22 週間前に更新
リポジトリ

enonic-content-migration について

Enonic XP のコンテンツ一括操作と移行を扱うスキルです。API とブランチコンテキストの選択、バッチ処理、長時間タスクの進捗や結果の確認を案内します。

主な機能

  1. 01Enonic XP プロジェクトとバージョンの検出
  2. 02API とコンテキスト選択の案内
  3. 03NoQL クエリと集計の作成
  4. 04コンテンツのバッチ処理
  5. 05長時間タスクの進捗報告
  6. 06移行と公開結果の検証

活用シーン

  1. 01外部データからコンテンツを一括作成
  2. 02条件に合うコンテンツの更新や削除
  3. 03パスや環境間でコンテンツを移行・変換
  4. 04コンテンツを検索し集計で要約

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/webmaxru/enonic-agent-skills --skill enonic-content-migration

インストール後、Claude Code で/enonic-content-migrationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
bulk-update.template.ts
task-migration.template.ts
references/
compatibility.md
examples.md
migration-reference.md
troubleshooting.md
scripts/
find-enonic-targets.mjs

詳細

Enonic コンテンツ移行

手順

ステップ 1:Enonic XP プロジェクトと操作範囲を特定する

  1. ワークスペース内で Enonic XP プロジェクトを示す要素を確認します。com.enonic.xp 依存関係を含む build.gradle、src/main/resources/ ディレクトリ構造、または xpVersion を含む gradle.properties が該当します。
  2. Node ランタイムを利用できる場合は、node scripts/find-enonic-targets.mjs . を実行して、Enonic XP プロジェクトを示す要素と既存のコンテンツ操作ファイルを検索します。
  3. Enonic XP プロジェクトが検出されない場合は、ここで終了し、このスキルは Enonic XP のコンテンツ操作のみを対象としていることを説明します。
  4. gradle.properties または build.gradle から対象の XP バージョンを特定し、適切な API サーフェスを選択します。
  5. 要求された操作を分類します。
    • 一括作成:外部ソース(JSON、CSV、API)からコンテンツをインポートします。
    • 一括更新:クエリ条件に一致する既存のコンテンツを変更します。
    • 一括削除:クエリ条件に一致するコンテンツを削除します。
    • 移行:パス、サイト、または環境間でコンテンツを移動または変換します。
    • クエリ/集計:NoQL と集計を使用してコンテンツを取得、分析します。
    • 長時間実行タスク:進捗報告が必要な、約 100 件を超えるアイテムを処理する操作です。

ステップ 2:API レイヤーとコンテキストを選択する

  1. クエリ DSL のパターン、バッチ処理の戦略、ブランチの処理ルールについては、references/migration-reference.md を読みます。
  2. CMS コンテンツを扱い、公開/非公開、コンテンツタイプの検証、コンテンツドメインの抽象化が必要な操作では、lib-content を選択します。
  3. 低レベルのノード操作、カスタムリポジトリ、または生データ移行でコンテンツタイプの検証を回避する必要がある操作では、lib-node を選択します。
  4. 必要なコンテキストを特定します。
    • 変更操作では draft ブランチ、公開済みコンテンツの読み取りでは master ブランチで操作を実行するために、lib-context を使用します。
    • 高い権限が必要な操作では、role:system.admin プリンシパルを指定して lib-context を使用します。
  5. 約 100 件を超えるアイテムを処理する操作は、lib-task を使用したタスクコントローラーでラップします。パターンについては、references/migration-reference.md のタスクコントローラーのセクションを読みます。

ステップ 3:クエリを構築する

  1. 対象コンテンツに一致する NoQL クエリ文字列または DSL 式を構築します。
  2. コンテンツタイプでフィルタリングするには、contentLib.query() の contentTypes パラメーター、またはノードクエリの type プロパティ比較を使用します。
  3. 日付範囲クエリには、instant() または dateTime() 関数と、range() クエリ関数を使用します。
  4. 全文検索には、適切なフィールドパスと演算子(AND/OR)を指定して fulltext() を使用します。
  5. パスを限定した操作では、子孫に一致するよう _path LIKE '/content/site-path/*' を使用します。注:NoQL クエリの _path プロパティには内部の /content/ プレフィックスが含まれますが、結果の hit._path にはコンテンツドメインのパスが返され、プレフィックスは含まれません。結果のパスからクエリを構築する際は、必ず /content/ を先頭に付けてください。
  6. exists、notExists、hasValue、または boolean の組み合わせを使用して、クエリ後の絞り込みを効率化する filters を追加します。
  7. グループ化された統計情報(用語の件数、日付ヒストグラム、数値範囲、統計情報)が必要な操作では、aggregations を追加します。
  8. クエリと集計の完全なパターンについては、references/examples.md を読みます。 ステップ 4:バッチ処理を実装する
  9. start パラメーターと count パラメーターを使ってページネーションで取得し、すべての結果をメモリに読み込まないようにします。
  10. アイテムごとの処理の複雑さに応じて、count を 50~200 のバッチサイズに設定します。
  11. result.hits.length === 0 または start >= result.total になるまでループし、反復ごとに start をバッチサイズ分増やします。
  12. contentLib.create() を一括で実行する際は、アイテムごとのインデックス更新を避けるために refresh: false を設定し、バッチ完了後に手動で更新します。
  13. contentLib.modify() では、各コンテンツアイテムを安全に変換するためにエディターコールバックパターンを使用します。
  14. contentLib.publish() では、大規模な公開セットでタイムアウトしないよう、キーを 50~100 件ずつのグループに分けて処理します。
  15. レポート用に成功数と失敗数を記録します。
  16. 再利用可能なバッチ更新コントローラーテンプレートについては、assets/bulk-update.template.ts を確認します。注:テンプレートでは TypeScript/ESM 構文(import、const、アロー関数)が使われています。.js ファイルとして XP ランタイムにデプロイする際は、CommonJS JavaScript(require()、var、function())に適合させてください。

ステップ 5:ブランチ操作と公開を処理する

  1. コンテンツの変更は draft ブランチのコンテキストで実行します。
  2. 変更が完了したら、contentLib.publish() を使って変更したアイテムを master に公開します。XP < 7.12 の場合は、sourceBranch: 'draft' と targetBranch: 'master' を渡します。XP 7.12 以降では、これらのパラメーターは無視されます(常に draft から master に公開されます)。
  3. 一括更新したアイテムを公開する際は、依存関係が意図せず公開されるのを避けるため、includeDependencies: false を設定します。
  4. draft と master の状態を比較する操作には、lib-node の repo.diff() を使用し、target: 'master' と includeChildren: true を指定します。

ステップ 6:時間のかかる操作をタスクコントローラーでラップする

  1. インラインのタスク関数には taskLib.executeFunction() を、名前付きタスク記述子には taskLib.submitTask() を使用します。
  2. バッチ処理中は、一定の間隔で taskLib.progress({ info, current, total }) を使って進捗を報告します。
  3. 進捗報告に対応した再利用可能なタスクコントローラーテンプレートについては、assets/task-migration.template.ts を確認します。
  4. 重複した操作を開始する前に、taskLib.isRunning() で既存の実行中インスタンスを確認します。
  5. 操作による負荷が過剰な場合は、バッチ間のスロットリングに taskLib.sleep() を使用します。
  6. XP 7.13 以降の名前付きタスクでは、run 関数の第 2 引数として taskId が渡されます:exports.run = function(params, taskId) { ... }。

ステップ 7:結果を検証して報告する

  1. 操作の完了後、処理したアイテム数、作成・更新・削除したアイテム数、失敗したアイテム数、合計所要時間の概要をログに記録します。
  2. 移行操作では、移行先のパスをクエリして、移行先にコンテンツが存在することを確認します。
  3. 公開操作では、master ブランチをクエリして、公開状態を確認します。
  4. エラーが発生した場合は、エラーの詳細(コンテンツパス、エラーメッセージ)を構造化レポートにまとめます。

エラー処理

  • scripts/find-enonic-targets.mjs で Enonic XP プロジェクトが見つからない場合は、ワークスペースに Enonic XP アプリケーションが含まれていないことを説明します。
  • コンテンツ操作で contentAlreadyExists エラーが発生した場合は、対象が存在するか確認し、更新するかスキップするかを判断します。重複の処理パターンについては、references/troubleshooting.md を確認します。
  • クエリ結果が 0 件の場合は、クエリ構文、ブランチのコンテキスト、プロパティのパスを確認します。NoQL の文字列形式の不一致(たとえば、日付比較で instant() ラッパーがないこと)は、よくある原因です。
  • タスクが失敗またはタイムアウトした場合は、taskLib.get(taskId) で状態と進捗の詳細を確認します。タイムアウトや権限の問題については、references/troubleshooting.md を確認します。
  • 公開または変更時に AccessDeniedException が発生した場合は、lib-context を介してコンテキストに role:system.admin プリンシパルが含まれていることを確認します。

コメント