How to create Firestore Bundles in Node.js? - node.js

As per suggestion on Firebase documentation on Firestore bundles, I haven't found the best way to create bundle based on the example given above. I don't know what I have to include in docSnapshot. I know what I have to fill in collection. But what is the case if CollectionGroup? There is no detail explanation about it, either. Can someone from Firebase team explain what I have to include in docSnapshot? I have collection "Music_Epic", for example?
const bundleId = "latest-Epic";
const bundle = firestore.bundle(bundleId);
const docSnapshot = await firestore.doc("Music_Epic/").get(); //What should be here?
const querySnapshot = await firestore.collection("Music_Epic").get();
// Build the bundle
// Note how querySnapshot is named "latest-stories-query"
const bundleBuffer = bundle.add(docSnapshot); // Add a document
bundleBuffer.add("latest-Epic-query", querySnapshot); // Add a named query.
bundleBuffer.build();

The add() takes either a DocumentSnapshot or (queryName, QuerySnapshot) as parameters. Collection Groups queries also return a QuerySnapshot so you can follow the same method used for querying a single collection.
const bundle = firestore.bundle();
const querySnapshot = await firestore.collection("Music_Epic").get();
// For collectionGroup queries:
// const querySnapshot = await firestore.collectionGroup("col_name").get();
const bundleBuffer = bundle.add('latest_music_epic_query', querySnapshot);
bundleBuffer.build();

Related

Are "get" functions in Google cloud functions for firebase reading each document everytime?

I observed a huge amount of read on my firebase console and I was wondering if this might come from my "referral function".
This function works perfectly fine but I was wondering whether or not this function could end up with a crazy load of read in case of app scaling.
My question: does this function imply that every time a user comes in, it will account for a number of read equivalent to the number of users in my collection ?
Thus, as this function is an onUpdate, will it redo the job every time a document is updated ?
I would not mind some resources on the topic because I found it unclear on Firebase's website.
I hope my questions are clear!
Thank you very much!
export const onReferralInfoUpdate = functions.
firestore.document('users/{userUid}')
.onUpdate(async (change, context) => {
const before = change.before.data();
const after = change.after.data();
const currentUserUid = after["uid"];
if (before.godfather_code == after.godfather_code){
console.log('Text did not change')
return null
}
const godfatherUserSnapshot = await db.collection('users').where("referral_code", "==", after.godfather_code).get();
const godfather = godfatherUserSnapshot.docs[0].data();
const godfatherUid = godfather["uid"];
const userRef = db.collection('users').doc(after.uid);
const godfather_code = after.godfather_code
await userRef.update({godfather_code})
console.log(`the text before was >> ${before.godfather_code} << and after is ${after.godfather_code}` )
let batch = db.batch();
const updateGodfather = db.collection('users').doc(godfatherUid);
batch.update(updateGodfather, {
reward: admin.firestore.FieldValue.increment(100),
godChildUid: admin.firestore.FieldValue.arrayUnion(currentUserUid),
});
return batch.commit();
});
Yes, the where("referral_code", "==", after.godfather_code).get() will fetch all the documents matching the query every time onUpdate() function triggers and you'll be charged N reads (N = number of matched documents). The Admin SDK doesn't have any caching like Client SDKs.
Does this function imply that every time a user comes in, it will account for a number of read equivalent to the number of users in my collection ?
Not numbers of documents in the users collection, only the documents matching your query as mentioned.

Firestore Snapshot - Do we need to check if a returned doc .exists?

Imagine this situation:
const refs = [...];
const docs = await firestore.getAll(...refs);
Is it possible that a returned doc from the getAll() doesn't exist?
I mean, for me it hasn't got sense to get a doc that doesn't exist as result...
Is there any special situation where a returned doc does not exist?
docs.forEach((doc) => console.log(doc.exists));
Yes. The data() will return undefined if the document does not exist. Though you can filter the array and remove any non-existing documents as shown below:
const refs = [...];
const docsSnaps = await firestore.getAll(...refs);
const docs = docSnaps.filter(d => d.exists).map(d => d.data())
It's the QuerySnapshot in which all documents returned exists since they match the query.

Firebase functions multi child change

hi firebase functions I want to change multiple children, but cannot change all children under user id. can you help me with this topic
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
var database = admin.database();
exports.newNodeDetected = functions.database.ref('system/{userId}/value')
.onWrite((Change) =>{
var oldDeger = Change.before.val();
var newDeger = Change.after.val();
if (newDeger === '0'){
database.ref(`system/${userId}/value`).set('1');
}
})
enter image description here
You can update multiple children in several ways using standard object manipulation and direct writes as demonstrated in your question.
You may be interested in using update rather than set when updating multiple fields.
You will also notice I added a Context field which allows you to access information such as the userID from the reference path and other information: Source
exports.newNodeDetected = functions.database.ref('system/{userId}/value')
.onWrite((Change, Context) =>{
var oldDeger = Change.before.val();
var newDeger = Change.after.val();
if (newDeger === '0'){
database.ref(`system/${Context.params.userId}/value`).set('1');
database.ref(`system/${Context.params.userId}/otherField`).update({another:field});
}
})
Resource: Medium Article: Realtime Set Vs Update

Using Firebase Cloud Functions to fan out data

I'm extremely new to using Firebase cloud functions, and I am struggling to find the error in my code. It is supposed to trigger on a firestore write and then copy that document into all of the user's feeds who follow that user who posted.
My current code is below:
exports.fanOutPosts = functions.firestore
.document('posts/{postId}')
.onCreate((snap, context) => {
var db = admin.firestore();
const post = snap.data();
const userID = post['author'];
const postCollectionRef = db.collection('friends').document(userID).collection('followers');
return postCollectionRef.get()
.then(querySnapshot => {
if (querySnapshot.empty) {
return null;
} else {
const promises = []
querySnapshot.forEach(doc => {
promises.push(db.collection('feeds').document(doc.key).collection('posts').document(post.key).update(data));
});
return Promise.all(promises);
}
});
});
So this successfully deploys to Firebase, but it receives this error when a document is created:
TypeError: db.collection(...).document is not a function
at exports.fanOutPosts.functions.firestore.document.onCreate (/workspace/index.js:22:60)
Line 22 is const postCollectionRef = db.collection('friends').document(userID).collection('followers');
I am unsure why this line is causing errors with the .get, but if anyone could point me in the right direction it would be much appreciated!
Given that this is the nodejs API, you'll want to use doc() instead of document(). Other languages might use document().
I found this info via the Admin SDK on CollectionReference https://googleapis.dev/nodejs/firestore/latest/CollectionReference.html
According to the reference, the collection should be defined as the following:
const postCollectionRef = db.collection(`friends/${userId}/followers`);
Using template literals will allow you to dynamically add variables into the collection ref.
I would also take a look into the else logic to use template literals within your return statement.

How to update a quantity in another document when creating a new document in the firebase firestore collection?

When I create a new document in the note collection, I want to update the quantity in the info document. What am I doing wrong?
exports.addNote = functions.region('europe-west1').firestore
.collection('users/{userId}/notes').onCreate((snap,context) => {
const uid = admin.user.uid.toString();
var t;
db.collection('users').doc('{userId}').collection('info').doc('info').get((querySnapshot) => {
querySnapshot.forEach((doc) => {
t = doc.get("countMutable").toString();
});
});
let data = {
countMutable: t+1;
};
db.collection("users").doc(uid).collection("info").doc("info").update({countMutable: data.get("countMutable")});
});
You have... a lot going on here. A few problems:
You can't trigger firestore functions on collections, you have to supply a document.
It isn't clear you're being consistent about how to treat the user id.
You aren't using promises properly (you need to chain them, and return them out of the function if you want them to execute properly).
I'm not clear about the relationship between the userId context parameter and the uid you are getting from the auth object. As far as I can tell, admin.user isn't actually part of the Admin SDK.
You risk multiple function calls doing an increment at the same time giving inconsistent results, since you aren't using a transaction or the increment operation. (Learn More Here)
The document won't be created if it doesn't already exist. Maybe this is ok?
In short, this all means you can do this a lot more simply.
This should do you though. I'm assuming that the uid you actually want is actually the one on the document that is triggering the update. If not, adjust as necessary.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.addNote = functions.firestore.document('users/{userId}/notes/{noteId}').onCreate((snap,context) => {
const uid = context.params.userId;
return db.collection("users").doc(uid).collection("info").doc("info").set({
countMutable: admin.firestore.FieldValue.increment(1)
}, { merge: true });
});
If you don't want to create the info document if it doesn't exist, and instead you want to get an error, you can use update instead of set:
return db.collection("users").doc(uid).collection("info").doc("info").update({
countMutable: admin.firestore.FieldValue.increment(1)
});

Resources