Salesforce Bulk API 2.0 & 外部IDを使ったデータ移行をjsforceでおこなう
副業で既存システムからSalesforceへのデータ移行をおこなっており、 Bulk API 2.0 をNode.jsから利用したのでその備忘メモになります✍
Salesforce 側の設定
Salesforceには外部 IDという概念があり、データの移行元で発番したIDをベースにレコードの一意性の識別をおこなえます
外部IDを利用するには、あらかじめSalesforceのオブジェクトマネージャにて、データ移行をおこなう対象のオブジェクトに対して設定が必要です
例としてリード Lead
にレコード追加をおこなっていくものとします
URLとしては https://${アカウント名}.lightning.force.com/lightning/setup/ObjectManager/Lead/FieldsAndRelationships/view
にて、カスタム項目を新規作成します
項目作成時に、 外部システムの一意のレコード識別子として設定する
と 値の重複を許可しない
にチェックをしておきます
今回は、項目名が K_RECORD_NUMBER__c
という値であったと仮定します
__c
はカスタム項目作成時に自動的に追加されるサフィックスだそうです
加えて、リードに対してイベントおよび行動 Event
および活動 Activity
を紐付けて移行をおこないたいとします
Salesforceに慣れていないと若干わかりづらい概念ですが、活動に外部IDを含めたカスタム項目を定義した上で、行動でリードとの関連を含むレコード値を設定すると適切に紐付けができる…ということのようです
続けて、活動に外部IDを設定します
注意としては、これはリード側で定義しているものとは異なり、活動(行動)を一意に識別するための値を後に設定します
今回は、項目名が K_CALL_RESULT__c
であったと仮定します
また、行動側の設定についても確認しておきます
こちらはSalesforceのデフォルト項目ですので確認のみでOKです
行動にはデータ型が 参照関係
と表示されている項目があり、ここでオブジェクト間の関連を定義します
今回はリードとの関連を設定したいので、 Who
という項目に対して値の設定をおこないます
上記画面では WhoId
と出ているのですが、正式な項目名は項目の詳細画面の以下に表示される Who
が正しい値のようです(よくわかってない)
移行データの作成
必要な項目の定義と確認ができたらデータを用意します
移行に際しては、データ移行対象のオブジェクト(今回は Lead
と Event
)ごとにCSVファイルを作成します
リードのサンプルデータを以下に示します
先程定義した K_RECORD_NUMBER__c
に対して、移行元のシステムで採番したID値を設定します
"K_RECORD_NUMBER__c","Name"
"1","Aさん"
"2","Bさん"
"3","Cさん"
行動のサンプルデータを以下に示します
"K_CALL_RESULT__c","Lead:Who.K_RECORD_NUMBER__c","Subject"
"10000","1","Aさんが電話をしました(1回目)"
"10001","1","Aさんが電話をしました(2回目)"
"10002","2","Bさんが電話をしました(1回目)"
"10003","3","Cさんが電話をしました(1回目)"
"10004","3","Cさんが電話をしました(2回目)"
"10005","3","Cさんが電話をしました(3回目)"
K_CALL_RESULT__c
についてはリードと同様に、行動を一意に識別するための外部IDですが、 Lead:Who.K_RECORD_NUMBER__c
は見慣れない形式になっています
これは、 多態的な項目でのリレーション をおこないたい場合の特殊な記法で、 ${親オブジェクトID}:${子項目ID}.${親項目ID}
の書式で指定します
サンプルデータの例だと、 (Eventの)Who項目の関連を、LeadのK_RECORD_NUMBER__cとレコード値を突合して設定する
的な意味になるようです
今回は、2つのファイルが path-to-csv/
ディレクトリ配下に存在するものとします
移行スクリプト
jsforce というすばらしいOSSがあるのでこちらをありがたく使います
執筆時点では 2.0 ブランチで最新版を開発しており、今回使いたいBulk API 2.0への対応もそちらでおこなわれているため、 npm i jsforce@latest
でインストールしてください(私は 2.0.0-beta.18
を使用しました)
また、これはオプションですが、ファイル操作を楽にするため fs-extraも活用します
移行スクリプトのサンプルを以下に示します
loadAndWaitForResults メソッドを使ってリードと行動を直列に登録していくことで、紐付けが適切に行われます
また、 operation
プロパティに upsert
を指定すると、 externalIdFieldName
で指定した外部ID名で一意性判定をおこない、レコード追加および更新をおこなえます
デターに
const { Connection } = require("jsforce");
const { createReadStream } = require("fs-extra");
const { existsSync, writeJSON } = require("fs-extra");
const { join } = require("path");
const main = async () => {
const targetDir = process.argv[2];
if (!existsSync(targetDir)) {
throw new Error(`not found: targetDir=${targetDir}`);
}
const conn = new Connection();
console.log("login");
await conn.login(
process.env.SALESFORCE_USER,
process.env.SALESFORCE_PASSWORD
);
const params = [
["Lead", "upsert", "lead.csv", "K_RECORD_NUMBER__c"],
["Event", "upsert", "event.csv", "K_CALL_RESULT__c"],
];
for (const [object, operation, fileName, externalIdFieldName] of params) {
const targetFile = join(targetDir, fileName);
console.log(`import start ${object}: ${targetFile}`);
const input = createReadStream(targetFile);
const { successfulResults, failedResults, unprocessedRecords } =
await conn.bulk2.loadAndWaitForResults({
object,
operation,
input,
externalIdFieldName,
pollTimeout: 1800000,
});
await writeJSON(`${targetFile}.success.json`, successfulResults, {
spaces: 2,
});
await writeJSON(`${targetFile}.failed.json`, failedResults, {
spaces: 2,
});
await writeJSON(`${targetFile}.unprocess.json`, unprocessedRecords, {
spaces: 2,
});
console.log(
`result: success=${successfulResults.length}, failed=${failedResults.length}, unprocessed=${unprocessedRecords.length}`
);
}
console.log("complete!");
};
main().catch((reason) => {
console.error(reason);
process.exitCode = 1;
});
環境変数に SALESFORCE_USER
および SALESFORCE_PASSWORD
を指定した上で、移行スクリプトを実行します
$ node ikou.js path-to-csv/
login
import start Lead: path-to-csv/lead.csv
result: success=3, failed=0, unprocessed=0
import start Event: path-to-csv/event.csv
result: success=6, failed=0, unprocessed=0
complete!
Web画面でレコードができていたらOKです
登録がうまくできない場合は、スクリプト中で出力している各種JSONファイルを確認してください
外部ID指定によるUpsertをおこなっているため、基本的に全ての登録がSuccessになるまでデータ修正 & スクリプト実行を繰り返せばよいものと思います🐕🦺
そんだけ😌
Discussion