Firexportガイド
Firestore のドキュメント名(ドキュメント ID)を変更する方法
Firestore では、ドキュメントの名前を変更できません。ドキュメント ID はパスの一部で、Firebase コンソールにも SDK にも ID を変える手段はありません。名前を変えるには、新しい ID でドキュメントを作り直して古いドキュメントを削除し、サブコレクションの中身もすべて一緒に移す必要があります。ドキュメントを削除しても、サブコレクションには手が付かないからです。方法は 3 つあり、どれが合うかは、ドキュメントの下にどれだけのデータがあるかによります。
2026年10月27日 更新。 方法 1 の拡張機能 Firexport は私たちのものです。方法 2 と 3 には不要です。
結論から言うと
| 1. コンソール + 拡張機能 | 2. Admin SDK スクリプト | 3. コンソールで手作業 | |
|---|---|---|---|
| 準備 | Chrome 拡張機能をインストールし、Google アカウントを接続 | Node.js と Google Cloud の認証情報 | なし |
| サブコレクション | すべての階層まで移動 | すべての階層まで移動 | 元の場所に残る |
| フィールドの値 | そのまま保たれる | 保たれる(ただし 2.0 のような double は整数になる) | 入力し直す |
| すべてか無か | はい(1 回の書き込み) | はい(1 つのバッチで、10 MiB まで) | いいえ |
| 規模 | ドキュメント 250 件まで | 上限なし | フィールド数個 |
1. Firexport で Firebase コンソールから ID を変更
Firexport は、Firestore のパネルビュー(Panel view)で、各ドキュメントの ⋮ メニューにドキュメント ID を変更を追加する無料の Chrome 拡張機能です。ドキュメントとそのサブコレクションの中身すべてを 1 回の書き込みで新しい ID に移すので、すべて行われるか、何も行われないかのどちらかです。接続した Google アカウントで書き込むので、サービス アカウント キーもコードの準備も不要です。
- Chrome ウェブストアから Firexport をインストールします。
- Firebase コンソールで Firestore Database > データ(Data)をパネルビュー(Panel view)で開き、ドキュメントがあるコレクションをクリックします。
- 一覧でドキュメントにカーソルを合わせ、ID の左に現れる ⋮ をクリックして、ドキュメント ID を変更を選びます。
- 初回は、ウィンドウから Google アカウントを接続するよう求められます。コンソールにログインしているアカウントを選び、Google のウィンドウでアクセスを許可します。
- 新しいドキュメント IDを入力し、ID を変更をクリックします。サブコレクションのないドキュメントはすぐに移動します。
- サブコレクションがある場合、Firexport はまずそれを読み込み、移動するドキュメントの件数、それに必要な書き込みの回数、確認に使った読み取りの回数を表示します。移動をクリックします。そのあとコンソールが新しい ID のドキュメントを開きます。
移動が止まる場合
- 新しい ID か、その下にすでにドキュメントがある場合。Firexport は上書きしません。書き込みは拒否され、何も変わりません。
- Firexport が読み込んだあとで、いずれかのドキュメントが変更された場合。そのまま書き込むと、古い内容が新しい ID に作られ、新しい内容は削除されてしまいます。そのため何も書き込まれず、もう一度試せます。
- ドキュメントが 250 件を超える場合、または 1 回の書き込みで運べる量(10 MiB)を超える場合。Firexport は何かを書き込む前に止まります。方法 2 を使ってください。
- Firestore が許可しない ID。
/を含む ID、.や..だけの ID、先頭と末尾がどちらも__の ID、1,500 バイトを超える ID です。前後の空白も ID の一部なので、入力欄は空白をそのまま残します。
Firexport はコンソールの表示ではなく Firestore から読み込んだものを書き込むので、すべてのフィールドが値もタイプもそのまま保たれます。書き込みは接続したアカウントの権限で行われるので、セキュリティ ルールは適用されません。ドキュメントはブラウザから直接 Google に送られ、私たちには一切送られません。ドキュメント ID の変更はどのプランでも無料です。
コンソールから Firestore のドキュメント ID を、サブコレクションごと 1 回の書き込みで変更しましょう。
2. Admin SDK スクリプトで移動
下に 250 件を超えるドキュメントがあるドキュメントや、多くのドキュメントの名前を変える場合は、Firebase Admin SDK を使う Node.js スクリプトで同じ移動ができます。下のスクリプトは、ドキュメントとその下のすべてのサブコレクションを読み込み、それぞれを新しいパスに作成してから古いものを削除します。新しいパスは別のコレクションにあってもかまわないので、同じスクリプトでドキュメントを移動することもできます。Firestore に書き込めるアカウントが必要です。プロジェクトのオーナーか編集者、または Cloud Datastore ユーザーのロールです。
- SDK をインストール:
npm install firebase-admin - ローカルの認証情報でサインイン:
gcloud auth application-default login。サーバーでは代わりにGOOGLE_APPLICATION_CREDENTIALSでサービス アカウント キーを指定します。 - 下のスクリプトを
rename-document.mjsとして保存し、your-project-idを置き換えて、node rename-document.mjs users/alice users/alice-smithを実行します。
// Usage: node rename-document.mjs users/alice users/alice-smith → moves users/alice and everything under it
// Firestore has no rename: this creates the document at the new path with all its subcollections, then deletes the old ones.
import { initializeApp } from 'firebase-admin/app';
import { getFirestore } from 'firebase-admin/firestore';
initializeApp({ projectId: 'your-project-id' });
const db = getFirestore();
db.settings({ useBigInt: true }); // integers past 2^53 would otherwise lose their last digits on the way through
const [from, to] = process.argv.slice(2);
if (!from || !to) throw new Error('Usage: node rename-document.mjs <document path> <new document path>');
if (to === from || to.startsWith(`${from}/`)) throw new Error('The new path must be outside the document you move');
// true: one batch, so every write happens or none does (up to 10 MiB of data in all).
// false: for more than that. Copies everything first and deletes the old documents only once every copy is written.
const ONE_BATCH = true;
// The document and every document under it, in subcollections of subcollections too. listDocuments() also returns
// places that have subcollections but no fields, so the walk reaches what is under them; the places themselves aren't copied.
async function collect(ref, found) {
const snapshot = await ref.get();
if (snapshot.exists) found.push(snapshot);
for (const collection of await ref.listCollections()) {
for (const child of await collection.listDocuments()) await collect(child, found);
}
return found;
}
const docs = await collect(db.doc(from), []);
if (docs[0]?.ref.path !== from) throw new Error(`${from} doesn't exist`);
const target = (doc) => db.doc(to + doc.ref.path.slice(from.length));
if (ONE_BATCH) {
// create() fails if a document is already at the new path, and lastUpdateTime fails if one changed after it was read.
// Either way Firestore rejects the whole batch and nothing is written.
const batch = db.batch();
for (const doc of docs) batch.create(target(doc), doc.data());
for (const doc of docs) batch.delete(doc.ref, { lastUpdateTime: doc.updateTime });
await batch.commit();
} else {
// BulkWriter keeps under Firestore's rate limits and retries what can be retried, but it isn't all or nothing.
const writer = db.bulkWriter();
const failures = [];
for (const doc of docs) {
writer.create(target(doc), doc.data()).catch((error) => failures.push(`${doc.ref.path}: ${error.message}`));
}
await writer.flush();
if (failures.length) {
failures.forEach((failure) => console.log(failure));
throw new Error('Some copies failed, so nothing was deleted. Delete the copies that were written, then try again.');
}
for (const doc of docs) {
writer.delete(doc.ref, { lastUpdateTime: doc.updateTime }).catch((error) => failures.push(`${doc.ref.path}: ${error.message}`));
}
await writer.close();
failures.forEach((failure) => console.log(`Not deleted, ${failure}`));
}
console.log(`Moved ${docs.length} documents from ${from} to ${to}`);listDocuments()は、自身のフィールドはないもののサブコレクションを持つ場所(コンソールで斜体で表示される ID)も返すので、スクリプトはその下のドキュメントにも届きます。その場所自体はコピーされませんが、サブコレクションが移ると新しいパスに現れます。ONE_BATCHがオンなら、すべての作成と削除が 1 つのバッチに入ります。新しいパスにすでにドキュメントがある場合や、スクリプトが読み込んだあとに変更されたドキュメントがある場合、Firestore はバッチを拒否し、何も書き込まれません。- 1 つのバッチで運べるのは 10 MiB までです。それより大きなツリーでは
ONE_BATCHをfalseにします。スクリプトはBulkWriterですべてをコピーし、すべてのコピーが書き込めた場合にだけ古いドキュメントを削除します。これはすべてか無かではないので、実行中はそれらのドキュメントに書き込まないでください。 - 方法 1 と同じくセキュリティ ルールは適用されません。タイムスタンプ、参照、ジオポイント、バイトはタイプを保ち、
useBigIntによって 253 を超える整数も正確に保たれます。Admin SDK が保てないものが 1 つあります。2.0のように小数部のない double はただの数値として読み込まれ、整数の2として書き戻されます。
3. コンソールで手作業でコピー
フィールドが少なくサブコレクションのないドキュメントなら、コンソールだけで足ります。互いに結びついていない 2 回の書き込みになり、値はすべて入力し直します。
- ドキュメントを開き、各フィールドの名前、タイプ、値を控えます。
- 同じコレクションでドキュメントを追加(Add document)をクリックし、ドキュメント ID(Document ID)に新しい ID を入力して、同じフィールドを同じタイプで追加します。保存(Save)をクリックします。
- 新しいドキュメントを確認してから、古いドキュメントの ⋮ メニューを開いてドキュメントを削除(Delete document)をクリックします。
入力しながらタイプに注意してください。文字列のフィールドに数値を入力すると文字列のままで、タイムスタンプはタイムスタンプのタイプで入力し直す必要があります。サブコレクションは一緒に移りません。サブコレクションはそれぞれ、同じ方法でドキュメントを 1 件ずつコピーする必要があります。
新しい ID で変わること
どの方法で移しても、新しい ID のドキュメントは新しいドキュメントです。そのため、次のことが起こります。
- 作成日時。移動した各ドキュメントの作成日時と更新日時は新しくなります。
- 参照。ほかのドキュメントにある参照フィールドは古いパスを指したままです。
ownerIdのように文字列で保存した ID も同じです。自分で更新してください。 - Cloud Functions とリスナー。移動した各ドキュメントで作成と削除のトリガーが発火し、コレクションをリッスンしているアプリには、ドキュメントが 1 件削除されて別の 1 件が追加されたように見えます。
- 費用。移動した各ドキュメントは、請求上 1 回の書き込みと 1 回の削除になり、さらにそれを見つけて読み込むための読み取りがかかります。
FAQ
Firestore でドキュメントの名前を変更できますか?
直接はできません。Firestore には名前を変更する操作がなく、ドキュメント ID は作成後に変えられません。名前を変えるには、新しい ID で同じデータのドキュメントを作成し、古いドキュメントを削除します。Firexport の「ドキュメント ID を変更」と上の Admin SDK スクリプトは、その両方を 1 回の書き込みで行います。
ドキュメントの名前を変えると、サブコレクションも移動しますか?
サブコレクションも移した場合だけです。サブコレクションはドキュメントのデータではなくパスに属しているので、フィールドをコピーしてもサブコレクションは元の場所に残り、古いドキュメントを削除してもサブコレクションは削除されません。Firexport と上のスクリプトは、どの階層のサブコレクションもすべてドキュメントと一緒に移します。
Firestore でコレクションの名前を変更できますか?
いいえ。コレクションはその中のドキュメントによってのみ存在するので、名前を変えるには、すべてのドキュメントを新しいコレクション名に移す必要があります。コンソールも Firexport もそれを 1 回の操作では行えません。上の移動をドキュメントごとに実行するスクリプトなら行えます。
古いドキュメントへの参照はどうなりますか?
古いパスを指したままですが、そのパスは移動後には存在しません。Firestore は、ほかのドキュメントにある参照フィールドや保存された ID を更新しないので、名前を変えたあとで探して更新してください。
Firestore のドキュメントの名前を変えるには、どれくらい費用がかかりますか?
移動するドキュメント 1 件につき書き込み 1 回と削除 1 回、さらにそのドキュメントとサブコレクションを見つけるための読み取りがかかります。Firexport は「移動」を押す前にその数を表示します。無料の Spark プランでは、1 日 20,000 回の書き込みと 20,000 回の削除から差し引かれます。
