Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Node script to upgrade GeoFirestore v3 (and older) docs to GeoFirestore v4
const admin = require("firebase-admin");
const { GeoFirestore, GeoCollectionReference } = require("geofirestore");
/**
* Generate new private key by selecting a project from below:
* https://console.firebase.google.com/u/0/project/_/settings/serviceaccounts/adminsdk
*/
const SERVICE_ACCOUNT = require("./serviceAccountKey.json");
/**
* Set the collection to update here.
*/
const COLLECTION_NAME = "restaurants";
/**
* If you use a custom key, define it here, otherwise leave this.
*/
const CUSTOM_KEY = "coordinates";
admin.initializeApp({
credential: admin.credential.cert(SERVICE_ACCOUNT),
databaseURL: "https://" + SERVICE_ACCOUNT["project_id"] + ".firebaseio.com",
});
const firestore = admin.firestore();
const geofirestore = new GeoFirestore(firestore);
const collection = firestore.collection(COLLECTION_NAME);
const geocollection = new GeoCollectionReference(collection);
let totalUpdated = 0;
function updateCollection(query) {
return new Promise((resolve, reject) =>
deleteQueryBatch(query.limit(500), resolve, reject)
);
}
function deleteQueryBatch(query, resolve, reject) {
query
.get()
.then((snapshot) => {
// When there are no documents left, we are done
if (snapshot.size === 0) {
console.log(`DONE, UPDATED ${totalUpdated}`);
return 0;
}
console.log(
`UPDATING ${snapshot.size} GEODOCUMENTS, ALREADY UPDATED ${totalUpdated}`
);
// Update documents in a batch
const batch = geofirestore.batch();
snapshot.docs.forEach((doc) => {
const geodoc = geocollection.doc(doc.id);
batch.set(geodoc, doc.data().d, { customKey: CUSTOM_KEY });
});
return batch.commit().then(() => snapshot.size);
})
.catch((e) => {
console.warn(
"This script was unable to convert your collection for GeoFirestore v4.",
e.details
);
return 0;
})
.then((numUpdated) => {
totalUpdated += numUpdated;
if (numUpdated === 0) {
resolve();
return;
}
process.nextTick(() => deleteQueryBatch(query, resolve, reject));
})
.catch((err) => reject(err));
}
(async () => {
await updateCollection(collection.orderBy("g").orderBy("l").orderBy("d"));
})();
@junkycoder

This comment has been minimized.

Copy link

@junkycoder junkycoder commented Aug 31, 2020

It works! ... in my case :)

Just had problem to run script by pure Node, but it is simple to rewrite imports to require

const admin = require("firebase-admin");
const { GeoFirestore, GeoCollectionReference } = require("geofirestore");

and remove type annotations like : admin.firestore.Query and : Promise<any>.

@MichaelSolati

This comment has been minimized.

Copy link
Owner Author

@MichaelSolati MichaelSolati commented Aug 31, 2020

@junkycoder good catch! I just fixed it.

@billp72

This comment has been minimized.

Copy link

@billp72 billp72 commented Sep 6, 2020

How would I use something like this in React?

@billp72

This comment has been minimized.

Copy link

@billp72 billp72 commented Sep 6, 2020

I gave it a shot in my react app. I had to change admin to firebase.initializeApp(firebaseConfig);
I got "This script was unable to convert your collection for GeoFirestore v4."

@MichaelSolati

This comment has been minimized.

Copy link
Owner Author

@MichaelSolati MichaelSolati commented Sep 7, 2020

This isn't designed to work as part of an app as it needs unfettered access to your collections. This should be run as a node script with the admin sdk.

@billp72

This comment has been minimized.

Copy link

@billp72 billp72 commented Sep 7, 2020

@billp72

This comment has been minimized.

Copy link

@billp72 billp72 commented Sep 7, 2020

@MichaelSolati

This comment has been minimized.

Copy link
Owner Author

@MichaelSolati MichaelSolati commented Sep 7, 2020

You'd run the script once per collection in order to update it. After that you should use the 4+ library to add/set/anything rather than 3/2/1.

@billp72

This comment has been minimized.

Copy link

@billp72 billp72 commented Sep 7, 2020

@MichaelSolati

This comment has been minimized.

Copy link
Owner Author

@MichaelSolati MichaelSolati commented Sep 7, 2020

This script was designed to migrate collections created be geofirestore 3/2/1 to be compatible for 4. So if you used v3 before you'd run the script then use v4 of geofirestore.

@smolugu

This comment has been minimized.

Copy link

@smolugu smolugu commented Sep 21, 2020

Hi, I am using 3.4.1. currently. Are there any major improvements in 4.0.

Also I am using this in google cloud functions in firebase project. Can you tell me how to run this script?

Regards,
Shyam.

@MichaelSolati

This comment has been minimized.

Copy link
Owner Author

@MichaelSolati MichaelSolati commented Sep 21, 2020

There are no major improvements in performance, just restructured data. As far as running the script I'd advise you go through the code above and make sure it makes sense for you. I don't want to be responsible for accidentally corrupting your data, so please read it and make sure you want to execute it. This is not code you'd run on a cloud function, it's something you'd run locally.

@smolugu

This comment has been minimized.

Copy link

@smolugu smolugu commented Sep 23, 2020

@MichaelSolati Thank you!

Also, I am getting empty results when I use where condition as below
const value = await query.where('notifications', '==', true).get();

I thought "notifications" field needs to be in all firestore documents of a collection. so updated all documents with "notifications" field but still the query returns empty set of documents.

Can you please let me know if it is possible to filter the query results using where clause?

Regards,
Shyam.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.