Firebase get request querying multiple tables - firebase

I'm trying to send get request to get the information of a row and that of its comments and options (rows, comments, options are all different tables). Currently, the request returns the row and comment info, but an empty array for the options, like so:
{
"categoryId": "Category1",
"dataType": "Text",
"approveCount": 0,
"createdAt": "10:00",
"body": "testneww",
"disapproveCount": 0,
"index": 1,
"visit": "Both",
"rowId": "ID",
"comments": [
{
"rowId": "ID",
"createdAt": "2021-02-28T21:32:52.841Z",
"body": "test comment"
}
],
"options": []
}
code:
exports.getOneRow = (req, res) => {
let rowData = {};
db.doc(`/rows/${req.params.rowId}`)
.get()
.then((doc) => {
if (!doc.exists) {
return res.status(404).json({ error: 'Row not found' });
}
rowData = doc.data();
rowData.rowId = doc.id;
return db
.collection('comments')
.orderBy('createdAt', 'desc')
.where('rowId', '==', req.params.rowId)
.get();
})
.then((data) => {
rowData.comments = [];
data.forEach((doc) => {
rowData.comments.push(doc.data());
})
return db
.collection('options')
.orderBy('index', 'asc')
.where('rowId', '==', req.params.rowId)
.get();
})
.then((newData)=>{
rowData.options = [];
newData.forEach((newDoc) => {
rowData.options.push(newDoc.data());
})
return res.json(rowData);
})
.catch((err) => {
console.error(err);
res.status(500).json({ error: err.code });
});
};
I presume I'm making a silly mistake somwhere, or is it not possible to do a request like this? Any help appreciated!

The following code worked, I cant see any differences besides adding the parameters individually in 'rowData.options.push' near the end, however ive been staring at the same code for quite a while so please do let me know if im missing something else.
exports.getOneRow = (req, res) => {
let rowData = {};
db.doc(`/rows/${req.params.rowId}`)
.get()
.then((doc) => {
if (doc.exists) {
rowData = doc.data();
rowData.rowId = doc.id
return db
.collection("comments")
.where("rowId", "==", req.params.rowId)
.get();
} else{
return res.status(500).json({ error: err.code });
}
})
.then((data) => {
rowData.comments = [];
data.forEach((doc) => {
rowData.comments.push(doc.data());
});
return db
.collection("options")
.where("rowId", "==", req.params.rowId)
.orderBy("index", "asc")
.get();
})
.then((data) => {
rowData.options = [];
data.forEach((doc) => {
rowData.options.push({
index: doc.data().index,
body: doc.data().body,
rowId: doc.data().rowId,
});
});
return res.json(rowData);
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
};

Related

How to get multiple references in a firestore snapshot

I have a firestore collection containing post documents, each document contains a reference to an author (user) and a case document.
How do I get the user and the case in the same onSnapshot?
Here's what I'd like to do with await, but that doesn't seem to be an option with react-native-firebase.
export const firebasePostLooper = (snapshot) => {
let data = [];
snapshot.forEach(async (doc) => {
let newItem = {id: doc.id, ...doc.data()};
if (newItem.author) {
let authorData = await getDoc(newItem.author); // doesn't work with rnfirebase
if (authorData.exists()) {
newItem.userData = {userID: authorData.id, ...authorData.data()};
}
}
if (newItem.case) {
let caseData = await getDoc(newItem.case);
if (caseData.exists()) {
newItem.userData = {userID: caseData.id, ...caseData.data()};
}
}
data.push(newItem);
});
return data;
};
This doesn't work because getDoc() doesn't exist.
So I'm left with using .then()
export const firebasePostLooper = (snapshot) => {
let data = [];
snapshot.forEach((doc) => {
let newItem = {id: doc.id, ...doc.data()};
if (newItem.author) {
newItem.author
.get()
.then((res) => {
newItem.authorData = res.data();
if (newItem.case) {
newItem.case
.get()
.then((caseRes) => {
newItem.caseData = caseRes.data();
data.push(newItem);
})
.catch((err) => console.error(err));
}
})
.catch((err) => console.error(err));
} else {
data.push(newItem);
}
});
return data;
};
This second method doesn't seem to be working, data is empty at the return statement but data.push(newItem) contains the correct document with the 2 referenced documents.
You're returning data before it gets filled inside the promise. You should handle the returning of the data inside a .then() in order to return it after the promise has resolved and not before.
Take a look at this example where if we handle the emptyData object outside the promise chain, we just return the initial value before it has been filled.
let promise = new Promise((resolve, reject)=>{
setTimeout(resolve, 1000, 'foo');
})
let emptyData= [];
let notEmptyData = [];
promise
.then(res=>{
emptyData.push(res);
notEmptyData.push(res);
console.log("Full data: " + notEmptyData) // "Full data: foo"
});
console.log("Empty data: " + emptyData); // "Empty data: "

How to correctly return the result from Cloud Function?

I am trying to implement verification using Cloud Function and Twilio. Twilio works correctly and SMS with the code is sent, but in the Flutter application I get the error [firebase_functions / deadline-exceeded] DEADLINE_EXCEEDE. What am I doing wrong?
Cloud Function:
const accountSid = process.env.TWILIO_ACCOUNT_SID;
const authToken = process.env.TWILIO_AUTH_TOKEN;
const client = require('twilio')(accountSid, authToken);
exports.sendSms = (req, res) => {
console.log('start sendSms');
var func = client.verify.services('xxxxx')
.verifications
.create({to: req.body.data.phone, channel: 'sms'})
.then(verification => {return verification})
.then(result => {
if (result.status === 'pending')
return {status: 'success', result: "pending"};
else
return {status: 'error', error_code: -1, error_msg: result.status};
})
.catch(e => {
return {status: 'error', error_code: e.code, error_msg: e.message};
});
return Promise.all([func]).then(result => {
console.log(result); // console output [ { status: 'success', result: 'pending' } ]
return result[0];
});
};
Flutter:
HttpsCallable callable = FirebaseFunctions.instanceFor(region: 'europe-west3').httpsCallable(
'send-sms',
options: HttpsCallableOptions(timeout: const Duration(seconds: 10))
);
dynamic results = await callable.call(<String, dynamic>{'phone': phoneNumber});
The create method returns promise too so you must add the return keyword there as well.
Instead of:
var func = client.verify.services('xxxxx')
Try this:
exports.sendSms = functions.https.onCall((data, context) => {
//^^^^^^^^^^^^^^^^^^^^^^
console.log('start sendSms');
return client.verify.services('xxxxx')
.verifications
.create({to: data.phone, channel: 'sms'})
.then(verification => {return verification})
.then(result => {
if (result.status === 'pending')
return {status: 'success', result: "pending"};
else
return {status: 'error', error_code: -1, error_msg: result.status};
})
.catch(e => {
return {status: 'error', error_code: e.code, error_msg: e.message};
});
});
That being said you don't need this part:
return Promise.all([func]).then(result => {
console.log(result); // console output [ { status: 'success', result: 'pending' } ]
return result[0];
});

Cannot fix Promise in react native Promise { "_U": 0, "_V": 0, "_W": null, "_X": null, }

Why did I add Promise to the function it still gives an error ??
I cannot call this function again with the parameter I give ??
Get User by Uid in Fire.js file( I custom this file )
getUserbyUid = async ({ Uid }) => {
return new Promise((res, rej) => {
firebase.firestore()
.collection('user')
.doc(Uid)
.get()
.then(documentSnapshot => {
res(documentSnapshot.data().name);
})
.catch(error => { rej(error) });
})
}
in Feed.js i call it
async componentDidMount() {
await firebase.firestore().collection("Post").get()
.then(querySnapshot => {
querySnapshot.forEach(documentSnapshot => {
this.setState({ dataPost: this.state.dataPost.concat({ ...documentSnapshot.data(),
key: documentSnapshot.id, }) })
console.log(Fire.shared.getUserbyUid(documentSnapshot.data().uid)); <= error this if i console.log(documentSnapshot.data().uid)) it will return a Uid
but If i call in above it will return Promise { "_U": 0,"_V": 0, "_W": null,"_X": null,}
});
});
}

Inside foreach loop, the await is not working

Below this code downloadcontroller.js
// Retrieve all downloads from the database.
exports.findAll = async (req, res) => {
downloadObj
.findAll(
{
include: [
{
model: composerObj,
required: false,
},
{
model: raagaObj,
required: false,
},
{
model: taalaObj,
required: false,
},
],
where: req.query,
raw:true,
nest: true,
})
.then((data) => {
data.forEach(async (element) => {
const artistsArray = [];
let whereConstraint = {};
JSON.parse(element.artists).forEach( async (ele) => {
artistsArray.push(ele.artistId);
});;
whereConstraint = {
id : {
[Op.in]: artistsArray
}
}
element.Active = "true11";
const artistData = artistService.customfindAll(whereConstraint);
element.artistData = artistData;
console.log("artistData",artistData);
});
console.log("data",data);
console.log("3");
res.status(200).send({
status:200,
message: "ok",
data:data
});
console.log("4");
})
.catch((err) => {
res.status(500).send({
status:500,
message:
err.message || "Some error occurred while retrieving Download.",
});
});
};
Below this code, artistServer.js file
exports.customfindAll = async (whereConstraint) => {
return new Promise(function(resolve, reject) {
artistObj
.findAll({
attributes:{
include: [
[fn('IF',literal('imagePath!=""'),(fn('CONCAT', artistImagePath, 'artist/', col('imagePath'))),artistImageDefaultPath), 'imageFullPath'],
],
},
where: whereConstraint,
order: [
['artistName', 'ASC'],
],
raw:true,
nest: true,
})
.then((data) => {
resolve(data);
});
});
}
Here my problem is
const artistData = artistService.customfindAll(whereConstraint);
not waiting for the data. .So that I got the result, below have mentioned. Actually the artistData attribute column, need result data.
{
"status": 200,
"message": "ok",
"data": [
{
"id": 1,
"fileType": "1",
"customFileName": "test filename",
"artists": "[{\"artistId\":1},{\"artistId\":4},{\"artistId\":2}]",
"accompanyingArtists": "[{\"instrumentId\":1,\"accompanyingArtistId\":1},{\"instrumentId\":2,\"accompanyingArtistId\":6},{\"instrumentId\":3,\"accompanyingArtistId\":4}]",
"Active": "true11",
"artistData": {}
},
{
"id": 2,
"fileType": "1",
"customFileName": "new file name",
"artists": "[{\"artistId\":1},{\"artistId\":4},{\"artistId\":2},{\"artistId\":6},{\"artistId\":3}]",
"accompanyingArtists": "[{\"instrumentId\":1,\"accompanyingArtistId\":1},{\"instrumentId\":2,\"accompanyingArtistId\":6},{\"instrumentId\":3,\"accompanyingArtistId\":4},{\"instrumentId\":3,\"accompanyingArtistId\":3},{\"instrumentId\":4,\"accompanyingArtistId\":2}]",
"Active": "true11",
"artistData": {}
}
]
}
In console page, I artistdata attribute pending...
enter image description here
The customFindAll function returns a Promise but the forEach does not wait for it. Add an await:
const artistData = await artistService.customfindAll(whereConstraint);
Also, forEach does not wait, it just fires off the async functions and move on. To wait for the forEach to finish, you need a map and a Promise.all, like this:
await Promise.all(data.map(async (element) => {
const artistsArray = [];
let whereConstraint = {};
JSON.parse(element.artists).forEach( async (ele) => {
artistsArray.push(ele.artistId);
});;
whereConstraint = {
id : {
[Op.in]: artistsArray
}
}
element.Active = "true11";
const artistData = await artistService.customfindAll(whereConstraint);
element.artistData = artistData;
console.log("artistData",artistData);
}));
The way it works this way is that data.forEach(async (e) => ... runs the async function and discards the results (the Promises). If you use a map, like data.map(async (e) => ..., you'll get an array of Promises, then use await Promise.all(...) to wait for them.

TypeError: Cannot set property 'words' of undefined

I have this action in my vuex store:
loadExercises ({commit}) {
commit('setLoading', true)
const db = firebase.firestore()
db.collection('exercises').get()
.then(querySnapshot => {
const exercises = []
querySnapshot.forEach((doc) => {
exercises.push({
title: doc.data().title,
language: doc.data().language,
translated: doc.data().translated,
lastOpen: doc.data().lastOpen,
dueDate: doc.data().dueDate,
uid: doc.data().uid,
userId: doc.data().userId,
words: [{ word: '', uid: '', translation: '' }]
})
db.collection('exercises').doc(doc.data().uid).collection('words').get()
.then(words => {
const wordsArray = []
words.forEach(word => {
wordsArray.push(word.data())
})
let exercise = this.getters.loadedExercise(doc.data().uid)
exercise.words = wordsArray
})
.catch(error => {
commit('setLoading', false)
console.log(error)
})
})
commit('setLoading', false)
commit('setLoadedExercises', exercises)
})
.catch(error => {
commit('setLoading', false)
console.log(error)
})
}
It is supposed to fetch exercises from a firebase cloudstore db. It works on some routes but not all.
When using these two getters it works:
loadedExercises (state) {
return state.loadedExercises
},
loadedExercise (state) {
return (exerciseId) => {
return state.loadedExercises.find(exercise => {
return exercise.uid === exerciseId
})
}
}
But when I use these getters:
upcomingExercises (state, getters) {
return getters.loadedExercises.filter(exercise => {
return exercise.dueDate > 0
})
},
latestExercises (state, getters) {
return getters.loadedExercises.splice(0, 5)
},
it does not work I just get "TypeError: Cannot set property 'words' of undefined". What is it that I do wrong?
It looks to me like you aren't returning the values back to the function.
Try replacing
db.collection('exercises').get()
with
return db.collection('exercises').get()
and
db.collection('exercises').doc(doc.data().uid).collection('words').get()
with
return db.collection('exercises').doc(doc.data().uid).collection('words').get()

Resources