How to fetch by id from Firestore? - node.js

I have been trying so hard for a couple of hours but it seems so difficult for such a simple thing. i need to fetch by id from Firebase and here is the code that I am using but which is not working:
fetch_selected_restaurant = () => {
var ref = firebase.firestore().collection('restaurants').where("res_id", "==", "5").get();
}

Keep in mind that reading firestore/firebase database is an asynchronous operation.
If you want to read document by id, you have to call it like that:
var ref = firebase.firestore().collection('restaurants').doc(yourDocId).get()
If you remember, few lines above I mentioned that almost all operations with firestore are asynchronous, reading document is not an exception. After you call get() it returns Promise. I see you stored this promise in ref variable, that`s fine.
Now in order to get a result, you have to ask this promise for results. On this step you can get what you want:
ref.then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});

Related

Mongoose async/ await correct method

I'm new to mongoose and node could anyone please point out which is the right method, my intention is to send the list of all products in my MongoDB to client.
method 1
items = await Product.findById({})
.then((items) => {
if (items == null) {
res.status(404).send("no items");
} else {
res.send(items);
next();
}
})
.catch((err) => {
res.status(404).send({ error: err });
});
method 2
items = await Product.find({}).exec();
res.status(200).send(items)
next();
Also could you point out how can i use again one more await call if i have to process items and send after that.Thanks in advance.
Method 2 should looks like that:
router.get('/:param', async function(req, res) {
try {
let items = await Product.find({}).lean()
res.status(200).json(items);
} catch (e) {
res.status(404).json(e);
}
});
You don't need .exec()
You need to use .lean() if you want to receive js object in items, not mongoose's.
Try not to request all docs collection at once, especially if collection have more then 1000+ documents. Otherwise do it via .cursor or limit(Number) to avoid problems with DB.
Also as already mentioned above, try to use async/await and ES6 syntax. It's just looks better.
And please, mark question as answered, just to avoid your question to be flagged.

Accessing firestore data at a global scope

I'm trying to access the firestore data and push it to an array. This is super basic but for some reason I cannot figure out why this isn't working:
var db = admin.firestore();
let arr = [];
var Ref = db.collection('Test').doc('Document');
var getDoc = Ref.get()
.then(doc => {
if (!doc.exists) {
console.log('No such document!');
} else {
let data = doc.data().Name;
arr.push(data);
}
})
.catch(err => {
console.log('Error getting document', err);
});
console.log(arr) // expecting >>> ['Joe'] (ie: data that is in firestore object)
Why doesn't arr contain the firestore object?
Thanks for the help.
It doesn't contain the data from firestore yet.
The get() operates asynchronously by returning a Promise and then continuing program execution. The next line is the console.log(arr), but arr isn't populated yet - it is populated when the Promise completes (calls the then() part).
If you're using a sufficiently modern version of node (node 8 and up, which you should be using at this point), then you can use await to wait for the asynchronous operation to complete before continuing to the next line.
I haven't tested it, but your code might look something like this after a rewrite:
doc = await Ref.get();
if (!doc.exists) {
console.log('No such document!');
} else {
let data = doc.data().Name;
arr.push(data);
}
console.log(arr)
This will work because the await waits for the async get() to complete and return the doc. The rest of it is processed synchronously.

Get async value from firestore

I am struggling with async operations. I am trying to simply get a value from firestore and storing it in a var.
I manage to receive the value, I can even save it in the var when I do that specifically (use the var within the get function) but I don't seem to manage the await properly when trying to save this in a flexible way:
async function getValues(collectionName, docName,) {
console.log("start")
var result;
var docRef = await db.collection(collectionName).doc(docName).get()
.then(//async// (tried this as well with async) function (doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
result = doc.data().text;
console.log(result);
return //await// (this as well with async) result;
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
result = "No such document!";
return result;
}
console.log("end");
}).catch (function (err) {
console.log('Error getting documents', err);
});
};
helpMessage = getValues('configuration','helpMessage');
Note: doc.data().text -> "text" is the name of the field where my value is stored in. Do I have to use .value here?
The result I get in the console is: info: Document data: { text: 'The correct text from the database' }
info: The correct text from the database
But using helpMessage in my code I get {}
Image from the Telegram bot where I am trying to use the helpMessage as a response to the '/help' command.
I have checked: getting value from cloud firestore,
Firebase Firestore get() async/await, get asynchronous value from firebase firestore reference and most importantly How do I return the response from an asynchronous call?. They either deal with multiple documents (using forEach), don't address the async nature of my problem or (last case), I simply fail to understand the nature of it.
Additionally, both nodejs and firestore seems to be developing rapidly and finding good, up-to-date documentation or examples is difficult. Any pointers are much appriciated.
You have things the wrong way around. It's much easier than you think it is.
function getValues(collectionName, docName) {
return db.collection(collectionName).doc(docName).get().then(function (doc) {
if (doc.exists) return doc.data().text;
return Promise.reject("No such document");
}};
}
If a function returns a promise (like db.collection(...).doc(...).get()), return that promise. This is the "outer" return above.
In the promise handler (inside the .then() callback), return a value to indicate success, or a rejected promise to indicate an error. This is the "inner" return above. Instead of returning a rejected promise, you can also throw an error if you want to.
Now you have a promise-returning function. You can use it with .then() and .catch():
getValues('configuration','helpMessage')
.then(function (text) { console.log(text); })
.catch(function (err) { console.log("ERROR:" err); });
or await it inside an async function in a try/catch block, if you like that better:
async function doSomething() {
try {
let text = await getValues('configuration','helpMessage');
console.log(text);
} catch {
console.log("ERROR:" err);
}
}
If you want to use async/await with your getValues() function, you can:
async function getValues(collectionName, docName) {
let doc = await db.collection(collectionName).doc(docName).get();
if (doc.exists) return doc.data().text;
throw new Error("No such document");
}
Since getValues function returns a promise, you need to await getValues function while calling it.
Change getValues like so -
function getValues(collectionName, docName,) {
console.log("start")
var result;
return db.collection(collectionName).doc(docName).get()
.then(function (doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
result = doc.data().text;
console.log(result);
return result;
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
result = "No such document!";
return result;
}
}).catch (function (err) {
console.log('Error getting documents', err);
});
};
Then use getValues like so -
helpMessage = await getValues('configuration','helpMessage');
Explanation -
async, await are just syntactic sugar for Promises. async functions return a promise (or AsyncFunction more accurately) which needs to be resolved to use its enclosed value.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Finally managed to get it working. Thanks for the input Tomalak!
getValues(help.collectionName, help.docName)
.then((text) => {
console.log(text);
help.message = text;
})
.catch((err) => { console.log("Error: ", err); });
function getValues(collectionName, docName) {
return db.collection(collectionName).doc(docName).get().then((doc) => {
if (doc.exists) {
return doc.data().text;
}
else {
return Promise.reject("No such document");
}});
}
bot.help((ctx) => ctx.reply(help.message));
Unfortunately, I can not pin-point the exact reason this worked. Some little fixes (missed comma in the console.log) and formatting definitely helped me understanding the structure though. Hope someone else finds this useful, when starting to play around with node and firebase.

How to check if a collection already exists in ArangoDB

Say I have a collection Col1 that already exists in my database. So, doing something like:
var col = db.collection('Col1');
col.save({"name":"something"});
will work perfectly fine.
But if a collection Col2 that doesn't already exist in my database is tried with the same thing i.e
var col = db.collection('Col2');
col.save({"name":"something"})
will work perfectly fine as well. Only that it doesn't exist and will be not shown in my database. Had it thrown some error or stuffs I could have used try and catch statements for result. But since that is off the plate, how do I know if a collection already exists ?
There are two things going on here that may be confusing.
First of all, arangojs (unlike the internal JS API of ArangoDB) is asynchronous for everything that needs to talk to the actual ArangoDB server. Asynchronous functions are marked as "async" in the documentation.
You can pass a node.js-style callback (like the async functions in built-in node.js modules, e.g. fs, http, etc) to these methods. Alternatively you can simply omit the callback and the method will return a promise for the result. You can learn more about how promises work in Mozilla's JavaScript reference documentation (this is not specific to Mozilla -- their reference is just very good and generally correct).
The other thing you're running into is the distinction between collection objects in arangojs and actual collections in ArangoDB. The driver lets you create collection objects for collections regardless of whether they exist yet or not. When trying to use them if the collection doesn't actually exist, you will of course see an error.
var col = db.collection('whatever');
col.create() // create the collection if it doesn't exist
.catch(function () {}) // ignore any errors
.then(function () {
return col.get(); // make sure the collection exists now
})
.then(function () {
return col.save({some: 'data'});
})
.then(function (result) {
// everything went fine
})
.catch(function (e) {
console.error('Something went wrong', e.stack);
});
Or using async/await (if you use Babel or read this answer one year from now):
var col = db.collection('whatever');
try {
await col.create(); // create the collection if it doesn't exist
} catch (e) {} // ignore any errors
try {
await col.get(); // make sure the collection exists now
const result = await col.save({some: 'data'});
// everything went fine
} catch (e) {
console.error('Something went wrong', e.stack);
}
Or using node.js-style callbacks because you're oldschool or really like pyramids:
var col = db.collection('whatever');
col.create(function () { // create the collection if it doesn't exist
// ignore any errors
col.get(function (err) { // make sure the collection exists now
if (err) {
console.error('Something went wrong', err.stack);
return;
}
col.save({some: 'data'}, function (err, result) {
if (err) {
console.error('Something went wrong', err.stack);
return;
}
// everything went fine
});
});
});
col.save does not execute the save operation immediately but returns a promise. So it will always succeed. The solution is to wait for the promise to be resolved and then react upon whether an error occurred or not:
var col = db.collection('Col2');
col.save({"name":"something"}).then(
meta => console.log('Document saved:', meta._rev),
err => { console.error('Failed to save document:', err.errorNum, err.response.body.errorMessage); }
);
https://docs.arangodb.com/3.1/Manual/DataModeling/Collections/DatabaseMethods.html#collection states
returns a single collection or null db._collection(collection-name)
so you can use
var col2 = db._collection('Col2');
if (col2) {
// collection exists
col2.save({"name":"something"});
}
This is old, but there is an exists() function.
Example in typescript/node
const metaResult = db.collection('myCollection');
if(!await metaResult.exists()) {
await db.createCollection('myCollection');
}

Mongoose error on promise with save?

When i try to get promise back with save operation on instance of model. i get error: undefined is not a function
instance.save().exec().then(..)
However, if i try to get promise with model like this, then it works.
model.find(..).exec().then(..)
Is there no way to get promise for save action. Currently i just pass callback to save function. However, for the sake of consistency i'd like to do all db operations in the same manner.
Model#save returns a promise, so you should skip the .exec():
instance.save().then(...);
Something like this?
let mongooseInstance = new MongooseInstance(Obj);
return mongooseInstance
.save()
.then(savedObj => {
if (savedObj) {
savedObj.someProperty = null;
success.data = savedObj;
return Promise.resolve(success);
} else {
return Promise.reject(error);
}
});
and maybe with a catch?
mongooseInstance
.save()
.then(saved => console.log("saved", saved))
.catch(err => console.log("err while saving", err));

Resources