Firestore - only able to create two collections at a time - javascript

I am uploading an initial data to firebase-firestore. The data is to be used as a fixture to develop my frontend further. I do not have errors, at least haven't caught one yet. The following two functions, are being used to upload data:
Edited to explain better
async function uploadData() {
const userId = 'AKFROjo1isTPRfjYYDsSehwZdIi1';
const itemsListRef = await db.collection('users').doc(userId).collection('userData').doc('itemsList');
await itemsListRef.set({ created: firebase.firestore.FieldValue.serverTimestamp() });
await summaryList.forEach(async summary => {
await itemsListRef.collection('summaryList').add(summary);
});
await coreCompetencyList.forEach(async coreCompetency => {
await itemsListRef.collection('coreCompetencyList').add(coreCompetency);
});
}
and a second function as -
async function uploadData2() {
const userId = 'AKFROjo1isTPRfjYYDsSehwZdIi1';
const itemsListRef = await db.collection('users').doc(userId).collection('userData').doc('itemsList');
await itemsListRef.set({ created: firebase.firestore.FieldValue.serverTimestamp() });
await educationList.forEach(async education => {
await itemsListRef.collection('educationList').add(education);
});
await categoryList.forEach(async category => {
await itemsListRef.collection('categoryList').add(category);
});
}
It is being called as :
async function main() {
try {
await uploadData();
await app.delete();
} catch (e) {
console.log('Data upload failed, reason:', e, '\n\n');
}
}
main().then(r => console.log('Done.'));
I am surprised that I cannot put all 4 calls in a single function

It's not a bug. This is the way it was intended to work.
In actuality, collections are not really entities that require creation or deletion like folders in a filesystem. They are just virtual containers for documents that spring into existence immediately when the first document in created under it, and automatically disappear when the last document in deleted. You can think of them more like units of organization that make indexing and security rules possible to implement.

Related

Mongoose find not working inside async function

I referred this stack overflow solution but still this did not help me.
I have a find method that works perfectly inside a controller but it does not work inside a async function.
Below is the cron job where i am calling this fuction inside my server.js:
// this performs the job everyday at 10:00 am
cron.schedule("00 10 * * *",async function () {
await notifyBookDefaulties();
});
Below i my async function:
const notifyBookDefaulties = async () => {
console.log("Start");
const getList = async () => {
console.log("Fetching");
const books = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }});
console.log(books);
console.log("Fetched");
};
try {
getList();
console.log("completed");
} catch (error) {
console.log(error);
}
console.log("End");
};
After execution i see the following output in my console:
Start
Fetching
completed
End
and i get the same output if i do const books = await Book.find({});
and if i use await getList() then i get the following output in my console:
Start
Fetching
and if i put a try/catch around const books = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }}); there is no error generated nor any output.
and if i remove const books = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }}) then i see the foolowing in my console
Start
Fetching
Fetched
completed
End
Below is my controller that works correctly and fetches the desired documents:
const dueBooks = async (req, res) => {
const result = await Book.find({"users.dueDate": { $lt: new Date().toISOString() }});
res.status(200).json(result[0]);
};
I tried to few things but it did work. Thanks in advance.
There appear to be two problems. One is you don't instantiate your MongoDB connection when you run your cron like your controller does.
In addition to that, you should be using the await on your getList since you need to wait for that promise inside the try / catch block.

Learning Promises, Async/Await to control execution order

I have been studying promises, await and async functions. While I was just in the stage of learning promises, I realized that the following: When I would send out two requests, there was no guarantee that they would come in the order that they are written in the code. Of course, with routing and packets of a network. When I ran the code below, the requests would resolve in no specific order.
const getCountry = async country => {
await fetch(`https://restcountries.com/v2/name/${country}`)
.then(res => res.json())
.then(data => {
console.log(data[0]);
})
.catch(err => err.message);
};
getCountry('portugal');
getCountry('ecuador');
At this point, I hadn't learned about async and await. So, the following code works the exact way I want it. Each request, waits until the other one is done.
Is this the most simple way to do it? Are there any redundancies that I could remove? I don't need a ton of alternate examples; unless I am doing something wrong.
await fetch(`https://restcountries.com/v2/name/${country}`)
.then(res => res.json())
.then(data => {
console.log(data[0]);
})
.catch(err => err.message);
};
const getCountryData = async function () {
await getCountry('portugal');
await getCountry('ecuador');
};
getCountryData();
Thanks in advance,
Yes, that's the correct way to do so. Do realize though that you're blocking each request so they run one at a time, causing inefficiency. As I mentioned, the beauty of JavaScript is its asynchronism, so take advantage of it. You can run all the requests almost concurrently, causing your requests to speed up drastically. Take this example:
// get results...
const getCountry = async country => {
const res = await fetch(`https://restcountries.com/v2/name/${country}`);
const json = res.json();
return json;
};
const getCountryData = async countries => {
const proms = countries.map(getCountry); // create an array of promises
const res = await Promise.all(proms); // wait for all promises to complete
// get the first value from the returned array
return res.map(r => r[0]);
};
// demo:
getCountryData(['portugal', 'ecuador']).then(console.log);
// it orders by the countries you ordered
getCountryData(['ecuador', 'portugal']).then(console.log);
// get lots of countries with speed
getCountryData(['mexico', 'china', 'france', 'germany', 'ecaudor']).then(console.log);
Edit: I just realized that Promise.all auto-orders the promises for you, so no need to add an extra sort function. Here's the sort fn anyways for reference if you take a different appoach:
myArr.sort((a, b) =>
(countries.indexOf(a.name.toLowerCase()) > countries.indexOf(b.name.toLowerCase())) ? 1 :
(countries.indexOf(a.name.toLowerCase()) < countries.indexOf(b.name.toLowerCase()))) ? -1 :
0
);
I tried it the way #deceze recommended and it works fine: I removed all of the .then and replaced them with await. A lot cleaner this way. Now I can use normal try and catch blocks.
// GET COUNTRIES IN ORDER
const getCountry = async country => {
try {
const status = await fetch(`https://restcountries.com/v2/name/${country}`);
const data = await status.json();
renderCountry(data[0]); // Data is here. Now Render HTML
} catch (err) {
console.log(err.name, err.message);
}
};
const getCountryData = async function () {
await getCountry('portugal');
await getCountry('Ecuador');
};
btn.addEventListener('click', function () {
getCountryData();
});
Thank you all.

Simple Firestore query doesn't give me complete documents in a collection

My Firestore data model looks like this
Organizations/{orgId}/cloudAccounts/{cloudAccountId}/resources/{resourceId}
I am running a simple query like this
let documents = await db.collection("Organizations")
.doc("orgIdabc")
.collection("cloudAccounts")
.doc("cloudAccountIdabc")
.collection("resources")
.get();
console.log("LENGTH: ", documents.docs.length);
Now the problem is that when I try to log the document length, it's always giving me a different length. I'll share the output as well.
LENGTH: 18
LENGTH: 19
LENGTH: 19
LENGTH: 6
LENGTH: 3
LENGTH: 19
LENGTH: 12
LENGTH: 19
LENGTH: 19
Now the actual length is 19, but as you can see I am getting different lengths.
I've no idea what's the problem here, any help would be really appreciated.
Updated
as per request I am adding the complete code snippet
Also I've come to know that this problem is only happening on Admin SDK and not the client SDK
const { db } = require("./firestore");
const functions = require("firebase-functions");
exports.runScanOnAllCloudAccounts = functions.https.onRequest((req, res) => {
runScanOnAllCA();
return res.status(200);
});
async function runScanOnAllCA() {
try {
for (let i = 0; i < 10; i++) {
let documents = await db.collection("Organizations")
.doc("orgIdabc")
.collection("cloudAccounts")
.doc("cloudAccountIdabc")
.collection("resources")
.get();
console.log("LENGTH: ", documents.docs.length);
}
} catch (err) {
console.log("err: ", err);
}
}
Update 02 ===========>
I've updated the code to use Promise() heavy approach, as recommended by some users and I still get different document lengths.
I feel that people are missing the point here, Promises are just a way to run asynchronous code, we can resolve this by using async await that was already being used in the previous version of the code.
Still the following code snippet doesn't solve the problem.
const { db } = require("./firestore");
const functions = require("firebase-functions");
exports.runScanOnAllCloudAccounts = functions.https.onRequest(
async (req, res) => {
runScanOnAllCA(res)
.then(resolve => {
console.log(resolve);
})
.catch(err => {
console.log(err);
});
// return res.status(200);
}
);
async function runScanOnAllCA(res) {
return new Promise(async (resolve, reject) => {
db.collection("Organizations")
.doc("sumair-hello-world_axtr8")
.collection("cloudAccounts")
.doc("4ZQgjt94pvEQTlvxSJ75")
.collection("resources")
.get()
.then(querySnapshot => {
resolve(querySnapshot.docs.length);
})
.catch(err => {
reject(err);
});
});
}
You should use the Admin SDK in order to interact with Firestore from a Cloud Function.
Secondly, it is known that using await in loops can lead to "erratic" results. See, for example, https://www.google.com/search?client=firefox-b-d&q=for+and+await
Finally, note that you are incorrectly calling your asynchronous runScanOnAllCA() function. You should either use then() or make your Cloud Function async and use await, see the code below.
You should adapt your CF as follows for using the Admin SDK:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.runScanOnAllCloudAccounts = functions.https.onRequest(async (req, res) => {
await runScanOnAllCA(); //<--- Note the await here and the async above, since you are calling an async function
res.status(200); //<--- No need to use return in an HTTPS Cloud Function, just terminate it with res.redirect(), res.send(), or res.end().
});
async function runScanOnAllCA() {
try {
const db = admin.firestore();
//The loop was removed. Use another way if needed, e.g. Promise.all() or the techniques presented in the links above.
let documents = await db.collection("Organizations")
.doc("orgIdabc")
.collection("cloudAccounts")
.doc("cloudAccountIdabc")
.collection("resources")
.get();
console.log("LENGTH: ", documents.docs.length);
} catch (err) {
console.log("err: ", err);
}
}

Firestore code is not waiting for response

I'm new to React Native, Javascript and Firestore. The issue is that I'm trying to read a firestore database and wait for the response to display information on the next page before it is rendered.
Here is the controller code:
_signIn = async () => {
try {
// Google sign in and connect to Firebase
... This code is working as expected
// Connect to the Firebase Firestore and store the connection
CesarFirestore.setFirestore(firebase.firestore());
let cFS = CesarFirestore.getInstance();
cFS.saveLoginDetails(this.cUser);
// THIS IS THE CALL TO GET THE STATUS OF THE USER
await cFS.readAvailability(this.cUser);
this.setState({loggedIn: true});
} else {
... INVALID login coding works fine
}
} catch (error) {
... CATCH coding works fine
}
}
};
From the above marked line, the following code is executed:
async readAvailability(cUser) {
let tuesdayDate = new CesarDate().getTuesday();
let availableRef = CesarFirestore.getFirestore().collection('available');
let availableQuery = await availableRef
.where('tuesdayDate', '==', tuesdayDate)
.where('userId', '==', cUser.getUserId())
.get()
.then(snapshot => {
if (snapshot.empty) {
console.log('No matching documents.');
cUser.setTuesdayAvailability('NOT_FOUND');
}
snapshot.forEach(doc => {
cUser.setTuesdayAvailability(doc.get('status'));
});
})
.catch(err => {
console.log('Error getting documents', err);
});
}
So the readAvailability(cUser) code should wait for the availableQuery to return results. The results would be then stored in the cUser class which is available to the rest of the application.
What happens is sometimes the result is available and sometimes the result is null. I thinking this is because doc is undefined which I have confirmed via the debugger.
Any help would be great and thank you in advance
If you await for a function, it must return a value, or a promise that resolves to a value later on. Since you're instead consuming the promise (in your then() handler), there is no way for the interpreter to know what to wait on.
async readAvailability(cUser) {
let tuesdayDate = new CesarDate().getTuesday();
let availableRef = CesarFirestore.getFirestore().collection('available');
let snapshot = await availableRef
.where('tuesdayDate', '==', tuesdayDate)
.where('userId', '==', cUser.getUserId())
.get()
if (snapshot.empty) {
console.log('No matching documents.');
cUser.setTuesdayAvailability('NOT_FOUND');
}
snapshot.forEach(doc => {
cUser.setTuesdayAvailability(doc.get('status'));
});
return true;
}
I'd usually actually make readAvailability return the user it modified, but the above should work too.

Defining asynchrosnous function and using it in a forEach loop

I am trying to compile tex files into PFD using data from a firestore database. After completion the script doens't terminate. I found one can use process.exit() to make it quit. However, it terminates the child processes still cimpling the tex files. So, I need an asynchronous function.
The guides I found on how to make them did not particularly help me. I am still very new to javascript and any bloat is still highly confusion to me.
The guides I found on how to make them did not particularly help me. I am still very new to javascript and any bloat is still highly confusion to me.
prepending below mentioned makePDF function with async and the call of the function with await does not work and is, to my understanding, outdated.
I tried implementing a promise, but don't understand how their syntax works. Does simply appending .then() to the function call in the for loop suffice to make the loop wait for the functions completion?
How do I make this specific asynchronous?
Does it matter that it already uses asynchronous functions in its body?
I understand that a promise is used to return what ever the producer has produced to a consumer. Now, my producer doesn't produce anything to be returned. Does this matter at all?
My function called from the for loop:
function makePDF(object){
let input = fs.readFileSync('main.tex', 'utf8');
const outNameTex = object.ID + '.tex';
const outNamePDF = object.ID + '.pdf';
makeTEX(object, input, outNameTex);
const infile = fs.createReadStream(outNameTex);
const outfile = fs.createWriteStream(outNamePDF);
const pdf = latex(infile);
pdf.pipe(outfile);
pdf.on('error', err => console.error(err));
pdf.on('finish', () => {console.log('PDF generated!')});
}
And my function with the loop:
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('user');
db.collection('objects').where('printed', '==', false).get().then((snapshot) => {
snapshot.forEach((doc) => {
console.table(doc.data());
makePDF(doc.data());
})
process.exit();
})
.catch((err) => {
console.log('Error getting documents', err);
});
} else {
console.log('no user');
}
});
It outputs a table for each document, but no PDF generated.
async/await can be tricky to use with for loops, that is because async functions return a promise... if you convert the async/await syntax to native promise syntax you might figure out what the issue is.
What you want to do is use Array.map to map/convert each doc to a promise that resolves once the makePDF is done, then use Promise.all to wait for all the promises to resolve..
The solution should look something like this
function makePDF(object){
return new Promise((resolve, reject) => {
let input = fs.readFileSync('main.tex', 'utf8');
const outNameTex = object.ID + '.tex';
const outNamePDF = object.ID + '.pdf';
makeTEX(object, input, outNameTex);
const infile = fs.createReadStream(outNameTex);
const outfile = fs.createWriteStream(outNamePDF);
const pdf = latex(infile);
pdf.pipe(outfile);
pdf.on('error', reject);
pdf.on('finish', () => {console.log('PDF generated!'); resolve();});
}
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('user');
db.collection('objects').where('printed', '==', false).get().then((snapshot) => {
const promiseArr = snapshot.docs.map((doc) => {
console.table(doc.data());
return makePDF(doc.data());
})
Promise.all(promiseArr)
.then(() => {
process.exit();
})
})
.catch((err) => {
console.log('Error getting documents', err);
});
} else {
console.log('no user');
}
});

Categories