Firebase V9 Database. Batching operations? - javascript

How do I batch multiple operations? I found this resource for Firestore https://firebase.google.com/docs/firestore/manage-data/transactions does database have something similar.
I wanted to push a new message collection when chat is created.
export const newChat = () => {
return push(ref(db, "chats"),{
...
})
.then((data) => {
update(ref(db, `chats/${data.key}`), {
chat_id: key,
});
//push(ref(db, `messages/${data.key}`), {
//text: "hello"
//});
})
.catch((error) => {
return error;
});
};

What you're describing can be accomplished with:
const newRef = push(ref(db, "chats"));
set(newRef, {
chat_id: newRef.key,
...restOfYourObject
});
The call to push does not cause any network traffic yet, as push IDs are a pure client-side operation.

Related

Toggle true/false state on complete button in ReactJs using firebase database

I am building to do list and I have complete button where I want to toggle true or false state and updating in firebase. I was searching on the platform, there were simillar solutions but not exacly what I needed.
So far I have this function:
const completedHandler = (id) => {
const docRef = doc(db, "todos", id);
updateDoc(docRef, {
isCompleted: !initialValues.isCompleted
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error.message);
});
};
The problem is that this is updating the state only once, and it won't change it again, for example if the state is false, it is updating in firebase as true, and that's it.
I am not sure where is the issue. Any help is appreciate.
Since the value that you write to the database depends on the current value of the field in the database, you'll need to first read the data and then write the new value based on that. The best way to do this is with a transaction and would look something like:
import { runTransaction } from "firebase/firestore";
try {
const docRef = doc(db, "todos", id);
await runTransaction(db, async (transaction) => {
const todoDoc = await transaction.get(docRef);
if (!todoDoc.exists()) {
throw "Document does not exist!";
}
const newValue = !todoDoc.data().isCompleted;
transaction.update(docRef, { isCompleted: newValue });
});
console.log("Transaction successfully committed!");
} catch (e) {
console.log("Transaction failed: ", e);
}

How delete firestore document

I use this code below and run well in Firebase RealTime Database:
const onDelete = (id) => {
if (
window.confirm("Are you sure that you wanted to delete the contact ?")
) {
Db.child(`contacts/${id}`).remove((err) => {
if (err) {
alert(err);
} else {
alert("Contact Deleted Successfully");
console.timeStamp(id);
}
});
}
};
Now I create another database in Firebase Firestore but cannot delete.
const onDelete = (id) => {
if (window.confirm("Are you sure that you wanted to delete contact ?")) {
firebase
.firestore()
.collection("contacts")
.doc(`${id}`)
.delete()
.then(() => {
console.log("Document successfully deleted!");
})
.catch((error) => {
console.error("Error removing document: ", error);
});
}
};
There's no difference in the databases here: in both cases you have to specify the full path to the data to be deleted.
If you can somehow specify line numbers for the Realtime Database, most likely you've used those line numbers as keys in your database. And if you can't do the same on Firestore, it's likely you didn't use the line numbers as document IDs there. If that is indeed the case, you'll need to either maintain a mapping from line numbers to document IDs, or use the line numbers for the document IDs in Firestore too.
const onDelete = async (id) => {
if (
window.confirm("Are you sure that you wanted to delete the contact ?")
) {
firebase
.firestore()
.collection("contacts")
.get()
.then(async (querySnapshot) => {
await deleteDoc(doc(db, "contacts", querySnapshot.docs[id].id));
alert("Contact Deleted Successfully");
});
}
};

Retrieve multiple users info from firebase auth using Node js

I am using Firebase authentication to store users. I have two types of users: Manager and Employee. I am storing the manager's UID in Firestore employee along with the employee's UID. The structure is shown below.
Firestore structure
Company
|
> Document's ID
|
> mng_uid: Manager's UID
> emp_uid: Employee's UID
Now I want to perform a query like "Retrieve employees' info which is under the specific manager." To do that I tried to run the below code.
module.exports = {
get_users: async (mng_uid, emp_uid) => {
return await db.collection("Company").where("manager_uid", "==", mng_uid).get().then(snaps => {
if (!snaps.empty) {
let resp = {};
let i = 0;
snaps.forEach(async (snap) => {
resp[i] = await admin.auth().getUser(emp_uid).then(userRecord => {
return userRecord;
}).catch(err => {
return err;
});
i++;
});
return resp;
}
else return "Oops! Not found.";
}).catch(() => {
return "Error in retrieving employees.";
});
}
}
Above code returns {}. I tried to debug by returning data from specific lines. I got to know that the issue is in retrieving the user's info using firebase auth function which I used in forEach loop. But it is not returning any error.
Thank you.
There are several points to be corrected in your code:
You use async/await with then() which is not recommended. Only use one of these approaches.
If I understand correctly your goal ("Retrieve employees' info which is under the specific manager"), you do not need to pass a emp_uid parameter to your function, but for each snap you need to read the value of the emp_uid field with snap.data().emp_uid
Finally, you need to use Promise.all() to execute all the asynchronous getUser() method calls in parallel.
So the following should do the trick:
module.exports = {
get_users: async (mng_uid) => {
try {
const snaps = await db
.collection('Company')
.where('manager_uid', '==', mng_uid)
.get();
if (!snaps.empty) {
const promises = [];
snaps.forEach(snap => {
promises.push(admin.auth().getUser(snap.data().emp_uid));
});
return Promise.all(promises); //This will return an Array of UserRecords
} else return 'Oops! Not found.';
} catch (error) {
//...
}
},
};

Optimize Firestore real time updates in chat app

I’m using Firestore real time updates to create realtime chats in my React Native app. I read this may not be the best way to build a chat, but I decided to do so cause I’m using Firebase already and the chat is not the main purpose of the app.
 So, in the context of a real time chat, how would I optimize the Firestore connection? 
It usually works really well but I have experienced a few problems so far:
message comes in slowly
message doesn’t show after being sent
Push notification arrives before the message
These problems usually occur when internet connection is not great (though Whatsapp messages still work fine), but sometimes also on a good connection…
Here is how I query the data (real-time listener added in componentDidMount, removed in componenWillUnmount):
onLogUpdate = (querySnapshot) => {
allMessages = this.state.allMessages
cb = (allMsgs) => {
allMsgs.sort((a,b) => {
a = new Date(a.timestamp);
b = new Date(b.timestamp);
return a>b ? -1 : a<b ? 1 : 0;
})
messages = _.takeRight(allMsgs, this.state.messageLimit)
this.setState({ loading: false, messages, allMessages: allMsgs })
}
async.map(querySnapshot._changes, (change, done) => {
if (change.type === "added") {
const msgData = change._document.data()
if (msgData.origin == 'user') {
this.usersRef.doc(msgData.byWhom).get()
.then(usr => {
msgData.user = usr.data()
done(null, msgData)
})
.catch(err => { console.log('error getting user in log:', err) })
} else {
done(null, msgData)
}
} else {
done(null, 0)
}
}, (err, results) => {
const res = results.filter(el => { return el != 0 })
allMessages = _.concat(allMessages, res)
cb(allMessages)
})
}
And this is how I add new messages:
// in utils.js
exports.addLogMessage = (msgObj, moment_id, callback) => {
logMessagesRef.add(msgObj)
.then(ref => {
momentsRef.doc(moment_id).get()
.then(doc => {
const logMsgs = doc.data().logMessages ? doc.data().logMessages : []
logMsgs.push(ref.id)
momentsRef.doc(moment_id).update({ logMessages: logMsgs })
})
.then(() => {
if (callback) {
callback()
}
})
})
.catch(err => { console.log('error sending logMessage:', err) })
}
// in chat Screen
sendLogMessage = () => {
if (this.state.newMessage.length > 0) {
firebase.analytics().logEvent('send_log_message')
this.setState({ newMessage: '' })
const msgObj = {
text: this.state.newMessage,
origin: 'user',
timestamp: Date.now(),
byWhom: this.state.user._id,
toWhichMoment: this.state.moment._id
}
addLogMessage(msgObj, this.state.moment._id)
}
}
Any suggestions would be highly appreciated :)
I was working on something similar and after i submitted the data it wouldnt show in the ListView. I solved it by pushing the new entry into the preexisting state that was being mapped, and since setState() calls the render() method, it worked just fine. I did something like this:
sendLogMessage().then(newData => {
joined = this.state.data.concat(newData);
this.setState({ data: joined})
})
This is ofcourse assuming that you're using this.state.data.map to render the chat log. This will work when the message that I send cant be seen by me and as for the messages updating as the database updates, you may wanna make use of .onDataChange callback provided by Firebase API. I hope I helped.

Cloud Functions for Firebase: how to use a Transaction promise?

I am trying to write a function in Cloud Functions that triggers every time a user gets created and which then saves that user into a list of users and finally increments a user counter.
However I am not sure if I am using promises correctly.
exports.saveUser = functions.auth.user().onCreate(event => {
const userId = event.data.uid
const saveUserToListPromise = db.collection("users").doc(userId).set({
"userId" : userId
})
var userCounterRef = db.collection("users").doc("userCounter");
const transactionPromise = db.runTransaction(t => {
return t.get(userCounterRef)
.then(doc => {
// Add one user to the userCounter
var newUserCounter = doc.data().userCounter + 1;
t.update(userCounterRef, { userCounter: newUserCounter });
});
})
.then(result => {
console.log('Transaction success!');
})
.catch(err => {
console.log('Transaction failure:', err);
});
return Promise.all([saveUserToListPromise, transactionPromise])
})
I want to make sure that even if many users register at once that my userCounter is still correct and that the saveUser function won't be terminated before the transaction and the save to the list has happened.
So I tried this out and it works just fine however I don't know if this is the correct way of achieving the functionality that I want and I also don't know if this still works when there are actually many users triggering that function at once.
Hope you can help me.
Thanks in advance.
The correct way to perform multiple writes atomically in a transaction is to perform all the writes with the Transaction object (t here) inside the transaction block. This ensures at all of the writes succeed, or none.
exports.saveUser = functions.auth.user().onCreate(event => {
const userId = event.data.uid
return db.runTransaction(t => {
const userCounterRef = db.collection("users").doc("userCounter")
return t.get(userCounterRef).then(doc => {
// Add one user to the userCounter
t.update(userCounterRef, { userCounter: FirebaseFirestore.FieldValue.increment(1) })
// And update the user's own doc
const userDoc = db.collection("users").doc(userId)
t.set(userDoc, { "userId" : userId })
})
})
.then(result => {
console.info('Transaction success!')
})
.catch(err => {
console.error('Transaction failure:', err)
})
})

Categories

Resources