Firestore Add value to array field - node.js

Im trying to use Firebase cloud functions to add the id of a chatroom to the users document in an array field. I cant seem to figure out the way to write to an array field type. here is my cloud function
exports.updateMessages = functions.firestore.document('messages/{messageId}/conversation/{msgkey}').onCreate( (event) => {
console.log('function started');
const messagePayload = event.data.data();
const userA = messagePayload.userA;
const userB = messagePayload.userB;
return admin.firestore().doc(`users/${userA}/chats`).add({ event.params.messageId }).then( () => {
});
});
here is the way my database looks
any tips greatly appreciated, Im new to firestore.

From the docs, they added a new operation to append or remove elements from arrays. Read more here: https://firebase.google.com/docs/firestore/manage-data/add-data#update_elements_in_an_array
Example:
var admin = require('firebase-admin');
// ...
var washingtonRef = db.collection('cities').doc('DC');
// Atomically add a new region to the "regions" array field.
var arrUnion = washingtonRef.update({
regions: admin.firestore.FieldValue.arrayUnion('greater_virginia')
});
// Atomically remove a region from the "regions" array field.
var arrRm = washingtonRef.update({
regions: admin.firestore.FieldValue.arrayRemove('east_coast')
});

Firestore currently does not allow you to update the individual fields of an array. You can, however, replace the entire contents of an array as such:
admin.firestore().doc(`users/${userA}/chats`).update('array', [...]);
Note that this might override some writes from another client. You can use transactions to lock on the document before you perform the update.
admin.firestore().runTransaction(transaction => {
return transaction.get(docRef).then(snapshot => {
const largerArray = snapshot.get('array');
largerArray.push('newfield');
transaction.update(docRef, 'array', largerArray);
});
});

This is 2021 and after many updates of firebase firestore, the new method to add data in array without removing another data is
var washingtonRef = db.collection("cities").doc("DC");
// Atomically add a new region to the "regions" array field.
washingtonRef.update({
regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia")
});
// Atomically remove a region from the "regions" array field.
washingtonRef.update({
regions: firebase.firestore.FieldValue.arrayRemove("east_coast")
});

Related

Firebase function - how to create multiple documents from different collections

I am fairly new to writing functions and cannot figure out how to solve my issue or even search for it properly.
I have three collections:
current_projects
vendors
vendor_projects
when a new project is created I want the function to take all documents in vendors, add certain fields from them to vendor_projects, and include the project_id field that is created in current_projects.
Do I need a for loop to accomplish this or is there other syntax that could be utilized?
My current function is below. This creates on document using the new project_id field but doesnt take any of the fields from vendors. Any input is greatly appreciated.
exports.createProjVen = functions.firestore.document("/Current_projects/{id}")
.onCreate((snap, context)=>{
console.log(snap.data());
const id = snap.data().id;
// const collection = context.params.Current_projects;
// const id = context.params.id;
const projectVendors = admin.firestore().collection("project_vendors");
// const vendors = admin.firestore().collection("vendors");
return projectVendors.doc(id).set({
actual: "",
budget: "",
id: "23121",
project_id: id,
toggle: "true",
type: "Fixtures",
vendor_company: "tes",
vendor_contact: "tes",
vendor_email: "jj#j.com",
vendor_id: "test",
vendor_phone_number: "test"});
});
Adding more details:
When a new project is added it creates a record in current_projects.
I want the function to be able to query all documents in the vendor collection when a new record is created in current_projects.
From there, I want to grab the highlighted fields from the vendor documents, add the id from that was created from current_projects (highlighted in the first screen shot), and create a new document in project_vendors (third screen shot below) for every document in the vendors table.
If you are trying to created a document in project_vendors collection for every vendor in vendors after a project is created then you can map an array of promises and then use Promise.all() as shown below:
exports.createProjVen = functions.firestore.document("/Current_projects/{id}")
.onCreate((snap, context) => {
const docSnap = snap.data();
const id = context.params.id;
const vendorsSnap = await admin.firestore().collection("vendors").get();
const vendorsData = vendorsSnap.docs.map((d) => ({ id: d.id, ...d.data() }))
const promises = [];
const vendorPrsCol = admin.firestore().collection("project_vendors");
vendorsData.forEach((vendor) => {
const data = {
projectId: id,
email: vendor.email,
// other vendor fields
}
promises.push(vendorPrsCol.add(data));
})
await Promise.all(promises);
console.log("Vendor Projects added");
return null;
});

followers and following sytem

I am working on a social network web application I have established a system of following followers with firebase and node js , so I created a collection users and in it two following followers array, I managed to add them
Now I want to issue a condition to check if the user has already made a follow up not to add it a second time to the table how can i access to the tables (following, followers)in order to verify if the user is in
exports.onFollow = (req, res) => {
const followDocument = db.doc(`/users/${req.body.email}`);
const followerDocument = db.doc(`/users/${req.user.email}`);
let followData;
let followerData;
followDocument
.get()
.then((doc) => {
if (doc.exists) {
followData = doc.data();
if ('req.user.email', 'in', followData.followers.docs) {
return res.status(200).json({
error: 'user already follow'
});
} else {
followData.followers.push(req.user.email);
return followDocument.update({
followers: followData.followers
});
}
}
})
.catch((err) => {
console.error(err);
res.status(500).json({
error: err.code
});
});
It sounds like you want followers to be an array with unique values, so that each email address can only occurs once. Firestore has special arrayUnion operation for adding values to such a field.
From the documentation on updating elements in an array:
If your document contains an array field, you can use arrayUnion() and arrayRemove() to add and remove elements. arrayUnion() adds elements to an array but only elements not already present. arrayRemove() removes all instances of each given element.
var washingtonRef = db.collection("cities").doc("DC");
// Atomically add a new region to the "regions" array field.
washingtonRef.update({
regions: admin.firestore.FieldValue.arrayUnion("greater_virginia")
});
// Atomically remove a region from the "regions" array field.
washingtonRef.update({
regions: admin.firestore.FieldValue.arrayRemove("east_coast")
});
I'd recommend switching to using arrayUnion() for your use-case, as it prevents having to do the query to detect if the email address is already in the array.

How to add a new field in Array of data in Firebase realtime database using Firebase Functions?

I am new to firebase. I have data in Firebase realtime database with following structure. My requirement is to add a new field to multiple (selected) records present under “user”.
Initial Data
db—>user
—pushId_1 (Auto Generated via push command)
name: user1
pushId_2
name: user2
Required final Data with one more field added (“newField”)
db—>user
—pushId_1
name: user1
newField: sample data
pushId_2
name: user2
newField: sample data
I have written following code to do this.
exports.sampleFunction = functions.database.ref('/db/user/{pushId}/')
.onCreate((snap, context) => {
console.log('onCreate called with snapshot id = ' + snap.key);
admin.database().ref('/db/user/').once('value').then(snapshot => {
if (snapshot.numChildren() > 1) {
var updates = {};
var count = 0;
snapshot.forEach((child) => {
if (!child.hasChild('newField') && count < 2) {
updates[child.key] = { newField: 'sample data' };
count++;
}
});
//return snapshot.ref.set(updates); //set is also giving same result.
return snapshot.ref.update(updates);
}
return null;
}).catch(error => {
console.error("error in fetching data from db." + error);
});
return null;
});
Problem is, this code is deleting existing field "name" and replacing it with “newField”.
Can you please help me in appending this new field to data without deleting existing fields.
Thanks
That's because you are using set instead of updating the node.
Set replaces any content while update adds fields if missing and only replace fields if they are present in new data.
return snapshot.ref.update(updates);
Also you are setting the data wrong to update. In key you need to have path relative to the ref you are calling the update instead of nested objects.
Instead of updates[child.key] = { newField: 'sample data' };, it should be
updates[`${child.key}/newField`] = 'sample data';
now when you call update with the child parent i.e. snapshot, it knows exactly which fields to update.
See the docs for more details: https://firebase.google.com/docs/database/admin/save-data#section-update

Cloud Functions & Firestore: Iterate over document.data

The database structure looks like this:
User {id}
Settings (Collection)
device_uids(document)
{device_uid_1}: Boolean
(...)
{device_uid_n}: Boolean
I want to get the document and access all of the device_uids within that document.
I tried like this, however the console logs, that forEach is not definded:
const settings_ref = admin.firestore().collection('User').doc(uid).collection('Settings').doc('device_uids');
settings_ref.get()
.then(snap =>{
let uids = snap.data();
uids.array.forEach(element => {
let device = element.key;
if(device != device_uid){
//GO ON
}
});
})
How can I access the values individually?
You don't have a field called array in your document, so uids.array will always be undefined. If you just want to iterate all the properties of the document, it's just like iterating all the properties of a plain old JavaScript object:
const data = snap.data();
for (const key in data) {
const value = data[key];
// now key and value are the property name and value
}

NodeJS Module Pattern

I'm about to begin writing a new module for a system I'm developing. We use a MySQL database (so I'm using node-mysql) which contains a customers table.
What I want to achieve is:
Outside of the module I'm looking to do var C = new Customer(1) where 1 is the customer ID.
Now when I want to get something from this customer, I can do C.email or C.first_name which will simply return a value.
I also need to be able to set values back on this customer, C.email = 'example#example.com' or perhaps:
C.set('email', 'example#example.com')
What would be the best pattern to create such a model?
I already have something like this... Not exactly what you demanded but very close to that
I have generalized the core part and here is the code..Hope this will help....
var mysql = require('mysql');
var con = mysql.createConnection({
host:"yourHostName",
user:"yourUserName",
password:"yourPassword"
});
con.query("use databaseName");
function getCustomerDetails(custId){
con.query("select * from customer where custId = "+custId,function(err,result,fields){
if(!err)
return result;
else
console.log(err);
});
}
function updateCustomerDetails(custId,fieldName,fieldValue){
con.query("update customer set "+fieldName+" = "+fieldValue+" where custId = "+custId,function(err,result,fields){
if(!err)
return true;
else
console.log(err);
return false;
});
}
exports.getCustomerDetails = getCustomerDetails;
exports.updateCustomerDetails = updateCustomerDetails;
And then suppose you saved the module as dbAccessModule.js Then you can use the functions like this
var C = require('./dbAccessModule');
result = C.getCustomerDetails(1);
console.log(result.fieldName);
var success = C.updateCustomerDetails(1,'name','sumit');
if(success)
console.log('Table Updated successfully....');
else
// take necessary action according to your application
One thing you need to take care of is that if you are updating any field with string value
then please don't forget to surround the value of fieldValue with single quotes.
If this is not what you asked for then please ignore it....
I recently created two database modules you might be interested in checking out to see if they fit your needs - an ORM: http://bookshelfjs.org and Query Builder: http://knexjs.org
The ORM is based off of the design patterns of Backbone.js
So, you'd be able to do something like this:
// Create the base customer object
var Customer = Bookshelf.Model.extend({
tableName: 'customers'
});
// Create a new customer instance with an id of 1, fetch it, and then
// act on the result model 'customer'.
new Customer({id: 1}).fetch().then(function(customer) {
console.log(customer.get('name'))
customer.set('email', 'email#example.com')
return customer.save();
});
You could also extend the base Customer class to enable a shortened syntax, similar to what you're looking for:
// Create the base customer object, with a static findOne method.
var Customer = Bookshelf.Model.extend({
tableName: 'customers'
}, {
find: function(id) {
return new this({id: id}).fetch();
}
});
Customer.find(1).then(function(C) {
console.log(C.get('name'))
C.set('email', 'email#example.com')
});

Resources