@globio/cli 0.1.7 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/index.js +29 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/commands/migrate.ts +22 -1
- package/src/lib/api.ts +21 -0
package/README.md
CHANGED
|
@@ -35,6 +35,12 @@ npx @globio/cli migrate firebase-storage \
|
|
|
35
35
|
--all
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
+
After migration, GlobalDoc indexes are created automatically for every field in your collections.
|
|
39
|
+
Queries using `where()` clauses will work immediately.
|
|
40
|
+
|
|
41
|
+
Note: GlobalDoc requires explicit indexes unlike Firestore's automatic indexing.
|
|
42
|
+
The migrate command handles this for you automatically.
|
|
43
|
+
|
|
38
44
|
## Commands
|
|
39
45
|
|
|
40
46
|
### Auth
|
package/dist/index.js
CHANGED
|
@@ -475,6 +475,20 @@ async function docSet(collection, docId, data, profile) {
|
|
|
475
475
|
profile
|
|
476
476
|
});
|
|
477
477
|
}
|
|
478
|
+
async function createIndex(collection, field, fieldType = "string", profile) {
|
|
479
|
+
void fieldType;
|
|
480
|
+
try {
|
|
481
|
+
await apiCall(`/doc/${collection}/indexes`, {
|
|
482
|
+
method: "POST",
|
|
483
|
+
body: {
|
|
484
|
+
field_path: field,
|
|
485
|
+
index_type: "asc"
|
|
486
|
+
},
|
|
487
|
+
profile
|
|
488
|
+
});
|
|
489
|
+
} catch {
|
|
490
|
+
}
|
|
491
|
+
}
|
|
478
492
|
|
|
479
493
|
// src/lib/firebase.ts
|
|
480
494
|
async function initFirebase(serviceAccountPath) {
|
|
@@ -550,6 +564,8 @@ async function migrateFirestore(options) {
|
|
|
550
564
|
};
|
|
551
565
|
let lastDoc = null;
|
|
552
566
|
let processed = 0;
|
|
567
|
+
let firstDocData = null;
|
|
568
|
+
let indexFieldCount = 0;
|
|
553
569
|
while (processed < total) {
|
|
554
570
|
let query = firestore.collection(collectionId).limit(100);
|
|
555
571
|
if (lastDoc) {
|
|
@@ -561,6 +577,14 @@ async function migrateFirestore(options) {
|
|
|
561
577
|
}
|
|
562
578
|
for (const doc of snapshot.docs) {
|
|
563
579
|
try {
|
|
580
|
+
if (!firstDocData) {
|
|
581
|
+
firstDocData = doc.data();
|
|
582
|
+
for (const [field, value] of Object.entries(firstDocData)) {
|
|
583
|
+
const fieldType = typeof value === "number" ? "number" : typeof value === "boolean" ? "boolean" : "string";
|
|
584
|
+
await createIndex(collectionId, field, fieldType, profileName);
|
|
585
|
+
}
|
|
586
|
+
indexFieldCount = Object.keys(firstDocData).length;
|
|
587
|
+
}
|
|
564
588
|
await docSet(collectionId, doc.id, doc.data(), profileName);
|
|
565
589
|
results[collectionId].success++;
|
|
566
590
|
} catch {
|
|
@@ -576,6 +600,11 @@ async function migrateFirestore(options) {
|
|
|
576
600
|
console.log(
|
|
577
601
|
chalk7.green(` \u2713 ${results[collectionId].success} documents migrated`)
|
|
578
602
|
);
|
|
603
|
+
if (indexFieldCount > 0) {
|
|
604
|
+
console.log(
|
|
605
|
+
chalk7.gray(` Indexes created for ${indexFieldCount} fields`)
|
|
606
|
+
);
|
|
607
|
+
}
|
|
579
608
|
if (results[collectionId].failed > 0) {
|
|
580
609
|
console.log(chalk7.red(` \u2717 ${results[collectionId].failed} failed`));
|
|
581
610
|
console.log(
|
package/jsr.json
CHANGED
package/package.json
CHANGED
package/src/commands/migrate.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
orange,
|
|
9
9
|
printBanner,
|
|
10
10
|
} from '../lib/banner.js';
|
|
11
|
-
import { docSet } from '../lib/api.js';
|
|
11
|
+
import { createIndex, docSet } from '../lib/api.js';
|
|
12
12
|
import { initFirebase } from '../lib/firebase.js';
|
|
13
13
|
import { createProgressBar } from '../lib/progress.js';
|
|
14
14
|
import { config } from '../lib/config.js';
|
|
@@ -81,6 +81,8 @@ export async function migrateFirestore(options: MigrateFirestoreOptions) {
|
|
|
81
81
|
|
|
82
82
|
let lastDoc: unknown = null;
|
|
83
83
|
let processed = 0;
|
|
84
|
+
let firstDocData: Record<string, unknown> | null = null;
|
|
85
|
+
let indexFieldCount = 0;
|
|
84
86
|
|
|
85
87
|
while (processed < total) {
|
|
86
88
|
let query = firestore.collection(collectionId).limit(100);
|
|
@@ -96,6 +98,20 @@ export async function migrateFirestore(options: MigrateFirestoreOptions) {
|
|
|
96
98
|
|
|
97
99
|
for (const doc of snapshot.docs) {
|
|
98
100
|
try {
|
|
101
|
+
if (!firstDocData) {
|
|
102
|
+
firstDocData = doc.data();
|
|
103
|
+
for (const [field, value] of Object.entries(firstDocData)) {
|
|
104
|
+
const fieldType =
|
|
105
|
+
typeof value === 'number'
|
|
106
|
+
? 'number'
|
|
107
|
+
: typeof value === 'boolean'
|
|
108
|
+
? 'boolean'
|
|
109
|
+
: 'string';
|
|
110
|
+
await createIndex(collectionId, field, fieldType, profileName);
|
|
111
|
+
}
|
|
112
|
+
indexFieldCount = Object.keys(firstDocData).length;
|
|
113
|
+
}
|
|
114
|
+
|
|
99
115
|
await docSet(collectionId, doc.id, doc.data(), profileName);
|
|
100
116
|
results[collectionId].success++;
|
|
101
117
|
} catch {
|
|
@@ -114,6 +130,11 @@ export async function migrateFirestore(options: MigrateFirestoreOptions) {
|
|
|
114
130
|
console.log(
|
|
115
131
|
chalk.green(` ✓ ${results[collectionId].success} documents migrated`)
|
|
116
132
|
);
|
|
133
|
+
if (indexFieldCount > 0) {
|
|
134
|
+
console.log(
|
|
135
|
+
chalk.gray(` Indexes created for ${indexFieldCount} fields`)
|
|
136
|
+
);
|
|
137
|
+
}
|
|
117
138
|
if (results[collectionId].failed > 0) {
|
|
118
139
|
console.log(chalk.red(` ✗ ${results[collectionId].failed} failed`));
|
|
119
140
|
console.log(
|
package/src/lib/api.ts
CHANGED
|
@@ -50,3 +50,24 @@ export async function docSet(
|
|
|
50
50
|
profile,
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
export async function createIndex(
|
|
55
|
+
collection: string,
|
|
56
|
+
field: string,
|
|
57
|
+
fieldType: 'string' | 'number' | 'boolean' = 'string',
|
|
58
|
+
profile?: string
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
void fieldType;
|
|
61
|
+
try {
|
|
62
|
+
await apiCall(`/doc/${collection}/indexes`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
body: {
|
|
65
|
+
field_path: field,
|
|
66
|
+
index_type: 'asc',
|
|
67
|
+
},
|
|
68
|
+
profile,
|
|
69
|
+
});
|
|
70
|
+
} catch {
|
|
71
|
+
// Index may already exist. Ignore duplicate-style failures.
|
|
72
|
+
}
|
|
73
|
+
}
|