I have created a function where i have passed an object as an argument and there are multiple objects which is passed on one by one and the output is an object but it comes as an individual object which is fine because I have written the code in that way now i wanted to store the objects into an array so it becomes an array of objects.and store for all objects
here is the tried code which gives single object example
const {db} = require('./constant')
const momentTz = require("moment-timezone");
const { getAuth } = require("./constant");
const officeStatus = async officeActivity => {
let output = {};
const maileventQuerySnapshot = await db
.collection("MailEvents")
.where("office", "==", officeActivity.office)
.where("event", "==", "delivered")
.limit(1).get();
output["office"] = officeActivity.office;
output["First Contact"] = officeActivity.attachment["First Contact"].value;
output["Second Contact"] = officeActivity.attachment["Second Contact"].value;
output["Office Creation Date"] = momentTz
.tz(officeActivity.timestamp, officeActivity.attachment.Timezone.value)
.format("DD MMM YYYY");
output["Reports Sent"] = maileventQuerySnapshot.docs.length ? true:false
const officeSubcollectionSnapshot = await db
.collection(`Offices/${officeActivity.officeId}/Addendum`)
.where("date","==",parseInt( momentTz(new Date()).subtract(1, "days").format("DD")))
.where('month', '==', parseInt( momentTz(new Date()).subtract(1, 'days').format("MM"))-1)
.where('year', '==',parseInt( momentTz(new Date()).subtract(1, "days").format("YYYY")))
.orderBy("user").orderBy('timestamp')
.get();
output['Number of unique Checkin Yesterday'] =
officeSubcollectionSnapshot.docs.length;
const activitiesRefSnapshot = await db
.collection("Activities")
.where("office", "==", officeActivity.office)
.where("template", "==", "subscription")
.where("attachment.Template.value", "==", "check-in")
.get();
const phoneNumberArray = [];
activitiesRefSnapshot.forEach(doc => {
phoneNumberArray.push(doc.data().attachment["Phone Number"].value);
});
const userRecord = await Promise.all(phoneNumberArray.map(getAuth));
output["Number of checkin Subscription Auth"] = userRecord.filter(
doc => doc !== undefined
).length;
output["Number of Checkin Subscription No Auth"] = userRecord.filter(
doc => doc === undefined
).length;
return { output};
};
module.exports = { officeStatus };
and the other file where i have queried the office and passed objects as an argument
const {admin,db} = require('./constant');
const { officeStatus } = require("./officeStatus");
let execute = async () => {
try {
let office = await db.collection("Offices").where("status", "==", "PENDING").get();
office.forEach(doc => {
officeStatus(doc.data())
.then(e => {
console.log(JSON.stringify(e.output));
})
.catch(error => {
console.log(error);
});
});
return;
} catch (error) {
console.log(error);
}
return;
};
execute();
admin.apps[0].delete();
I have get output in this way
{}
{}
{}....
and I wanted the output in this way
[{},{},{}]
The promise inside forEach is not correct. It wouldn't give you expected results. You shouldn't use that as it is not supported. You should consider using for..of or Promise.all. In your case I would suggest to use Promise.all as you need result in array.
Promise.all(office.docs.map(doc => {
return officeStatus(doc.data())
.then(e => {
return e.output;
})
.catch(error => {
console.log(error);
});
}))
.then(res => console.log(res));
Try doing that :
// and the other file where i have queried the office and passed objects as an argument
const {admin,db} = require('./constant');
const { officeStatus } = require("./officeStatus");
let execute = async () => {
try {
let office = await db.collection("Offices").where("status", "==", "PENDING").get();
let arr = new Array();
office.forEach(doc => {
officeStatus(doc.data())
.then(e => {
arr.push(e)
})
.catch(error => {
console.log(error);
});
});
console.log(arr)
return;
} catch (error) {
console.log(error);
}
return;
};
execute();
admin.apps[0].delete();
I just created an array where we push e when we did officeStatus. Then, at the end of the code, we log arr ; the declared array.
const {admin,db} = require('./constant');
const { officeStatus } = require("./officeStatus");
let execute = async () => {
let office = await db.collection("Offices").where("status", "==", "PENDING").get()
const arrayofObject = await Promise.all(office.docs.map(doc => {
return officeStatus(doc.data())
.then(e => {
// console.log(JSON.stringify(e.output));
return e.output;
})
.catch(error => {
console.log(error);
});
}))
console.log(arrayofObject)
return;
};
execute();
admin.apps[0].delete();
Related
I have an array of URLS stored within a document that i'd like to read and display as individual cards. All I'm getting is the return of the whole array, I'm not mapping it correctly and I don't know where I'm going wrong.
Currently, it's displaying "https://website1.com, https://website2.com". Where as I would like it to be individual items.
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setInternalLinks(tempData);
});
} catch (err) {
console.error(err);
};
};
useEffect(() => {
getInternalLinks()
},[])
return (
{internalLinks.map((doc, index) => {
<Card>
<p>{doc.urls.urls}</p>
</Card>
}))
);
Firebase Collection Structure
Try adding it directly to the state:
const [internalLinks, setInternalLinks] = useState([]);
const getInternalLinks = async () => {
try {
const query = await db
.collection("internallinks")
.get()
.then((snapshot) => {
snapshot.forEach((doc) => {
const data = doc.data();
setInternalLinks([ ...internalLinks, data ]);
});
});
} catch (err) {
console.error(err);
};
};
Im tryng to display firestore data but I just get one value. I have try forEach and map. Nothing is working. Heres my code:
React.useEffect(() => {
retrieveNetwork();
}, []);
const retrieveNetwork = async () => {
try {
const q = query(collection(db, "cities", uidx, "adress"));
const querySnapshot = await getDocs(q);
let result = [];
//querySnapshot.docs.map((doc) => setGas(doc.data().home));
querySnapshot.docs.map((doc) => {
result.push(doc.data().home);
setGas(result);
});
} catch (e) {
alert(e);
}
};```
The .map method returns an array (official docs here), so you could do something like this:
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
React.useEffect(() => {
retrieveNetwork();
}, []);
const retrieveNetwork = async () => {
try {
const q = query(collection(db, "cities", uidx, "adress"));
const querySnapshot = await getDocs(q);
// an array from the docs filled only with "home" data
const results = querySnapshot.docs.map((doc) => {
return doc.data().home;
});
// only update the state once per invokation
setGas(results)
} catch (e) {
alert(e);
}
};
Angular, firestore
I have an angular function to get products from one firestore collection, then I am looping over the results of that query to lookup prices from another collection.
How can I wait until the prices forEach is done before returning from the outer promise and the outer function itself?
The returned result contains a products array, but the prices array for each product is empty.
const products = await this.billingService.getProducts();
async getProducts() {
let result = [];
let product = {};
return this.db.collection(
'products',
ref => { ref
let query: Query = ref;
return query.where('active', '==', true)
})
.ref
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(async function (doc) {
product = doc.data();
product['prices'] = [];
await doc.ref
.collection('prices')
.orderBy('unit_amount')
.get()
.then(function (docs) {
// Prices dropdown
docs.forEach(function (doc) {
const priceId = doc.id;
const priceData = doc.data();
product['prices'].push(priceData);
});
});
});
result.push(product);
return result;
});
}
I also tried this approach, but not sure how to access the results
await this.billingService.getProducts().then(results =>
getProducts() {
const dbRef =
this.db.collection(
'products',
ref => { ref
let query: Query = ref; return query.where('active', '==', true)
});
const dbPromise = dbRef.ref.get();
return dbPromise
.then(function(querySnapshot) {
let results = [];
let product = {};
querySnapshot.forEach(function(doc) {
let docRef = doc.ref
.collection('prices')
.orderBy('unit_amount')
results.push(docRef.get())
});
return Promise.all(results)
})
.catch(function(error) {
console.log("Error getting documents: ", error);
});
}
Posting as a Community Wiki answer, based in the comments.
For this case, using a forEach() is not the correct choice. As clarified in this case here, forEach() doesn't work properly with await functions, this way, not working correctly with your promises. Considering that and the fact that you want to read the data in sequence - as the results from one query will impact in the second one - you need to use a normal for, to loop through the data and arrays. This specific answer here should help you with code samples.
This version of the code works:
getProducts(): Promise<any> {
return new Promise((resolve,reject)=> {
let result = [];
let product = {};
this.db.collection(
'products',
ref => { ref
let query: Query = ref;
return query.where('active', '==', true)
})
.ref
.get()
.then(async function (querySnapshot:firebase.firestore.QuerySnapshot) {
for(const doc of querySnapshot.docs) {
const priceSnap = await doc.ref
.collection('prices')
.orderBy('unit_amount')
.get()
product = doc.data();
product['prices'] = [];
// Prices dropdown
for(const doc of priceSnap.docs) {
const priceId = doc.id;
let priceData = doc.data();
priceData['price_id'] = priceId;
product['prices'].push(priceData);
resolve(result);// returns when it reaches here
};
result.push(product);
};
});
})
}
Make the getProducts() function a promise. Thus it will only return when you resolve it ( or reject it).
getProducts() {
return new Promise((resolve,reject)=> {
let result = [];
let product = {};
this.db.collection(
'products',
ref => { ref
let query: Query = ref;
return query.where('active', '==', true)
})
.ref
.get()
.then(function (querySnapshot) {
querySnapshot.forEach(async function (doc) {
product = doc.data();
product['prices'] = [];
doc.ref
.collection('prices')
.orderBy('unit_amount')
.get()
.then(function (docs) {
// Prices dropdown
docs.forEach(function (doc) {
const priceId = doc.id;
const priceData = doc.data();
product['prices'].push(priceData);
});
resolve(result);// returns when it reaches here
});
});
result.push(product);
});
})
}
Then you can call the promise using then or await
this.billingService.getProducts().then( res => {
const products = res;
})
Using await
const products = await this.billingService.getProducts();
Hi i'm new to cloud functions, i was trying to update name in the collection when there is a Chang(update) on family document, but i cannot update name in the collection, seems like line is not executing after first console log.
exports.onClassroomUpdate = functions.firestore.document("family/{id}").onUpdate((change, context) => {
const documentId = context.params.id;
const after = change.after.data();
let users = admin.firestore().collection('users').where('family.id', '==', documentId);
return users.get().then((querySnapshot) => {
let chain = Promise.resolve();
console.log('work');
querySnapshot.forEach((doc) => {
console.log('not working');
chain = chain.then(() => {
return admin.firestore().collection('users').doc(doc.id).update({
name: after.name
});
});
});
return Promise.all([chain]);
})
}).catch(err => {
console.log(err);
return Promise.reject(err);
});
)}
db.collection("City").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
db.collection("users").where("City", "==", doc.id).get().then(function(querySnapshot) {
var jgy_usercount = querySnapshot.size;
retArr.push(jgy_usercount);
});
});
});
The retArr has the number of users per city that is stored in the array. this array i need to use elsewhere out of the function. How can retrieve it?
You could do something like this:
async function getUsersPerDistrict() {
const querySnapshot = await db.collection("district").get()
const districts = []
querySnapshot.forEach(x => { districts.push(x.data) })
const districtCounts = await Promise.all(
districts.map(async x => {
const usersFromDistrict = await db.collection("users").where("City", "==", x.id).get()
return { count: usersFromDistrict.size, name: x.name }
})
)
return districtCounts
}
Without async/await:
function getUsersPerDistrict() {
return db.collection("district").get().then(querySnapshot => {
const districts = []
querySnapshot.forEach(x => { districts.push(x.data) })
const districtCounts = Promise.all(
districts.map(x =>
db.collection("users").where("City", "==", x.id).get()
.then(usersFromDistrict => ({
count: usersFromDistrict.size,
name: x.name
}))
)
)
return districtCounts
}