I am using this query search with Mongodb to get only '_id' value matched from 'clientID'. It works well at roto3T.
db.getCollection('orders').find({"clientID":"1234"}, {$_id:true});
But I want to use this at mongoose. So I put like below.
But It doesn't work well. How can I make the query search well in Mongoose?
Thank you so much!
await Order.find({ clientID: { $_id: true },},
(error, order) => {
if (error) {
return next(error);
}
return res.send({order,});
},);
if you want to use Async Await,
try {
let order = await Order.find({"clientID":"1234"}, "_id");
return res.send({order});
} catch(error) {
return next(error);
}
or otherwise if you want to use callback
return Order.find({clientID:"1234" },{ '_id': 1 },(error,order) => {
if (error) return next(error);
return res.send({order});
});
You can't use both to retrieve your data
Related
Recently I start using MongoDB with Mongoose on Nodejs.
This code works as it should, and returns me all data i need :
const getAllPosts = async () => {
try {
return (await PostModel.find().populate('user')).reverse();
} catch (error) {
console.log(error);
throw Error('Error while getting all posts');
}
};
But now I only need individual posts, which in the tags (represented as an array in the PostModel) contain the data that I will pass in the request.
For example, I will make a GET request to /posts/tag111 and should get all posts that have "tag111" in the tags array.
Any ways to do this?
If you are using expressjs, your route should be something like:
whatever.get('/:tag', postController.getAllPostTagged)
The function you use in your route (called getAllPostTagged above) should be similar to this one, in which you get the path param tag from req:
const postController = {
getAllPostTagged = async(req, res) => {
try {
return (await PostModel.find({tags: req.params.tag}));
} catch (error) {
console.log(error);
throw Error('Error while getting all posts');
}
}
}
The key here is to know where the params are obtained (from req.params) .
I have looked at MongoDB documentation for deleteMany() but it seems the only error it throws is WriteConcernError.
I am using Insomnia to make my requests.
Here is my request:
DELETE HTTP://localhost:5000/api/users/delete/usernames?usernames=["a","b","c"]
As you can see I have an array in query string
so I pass that to my function
# user.controller.js
function _deleteByUsernames(req, res, next) {
userService.deleteByUsernames(JSON.parse(req.query.usernames))
.then(() => res.status(200).json({"message": "User(s) successfully deleted!"}))
.catch(err => next(err));
}
# user.service.js
async function _deleteByUsernames(usernames) {
try {
console.log(usernames);
await User.deleteMany({username: {$in: usernames}});
} catch (err) {
throw err;
}
}
I know there no documents with usernames a, b and c
but deleteMany() doesn't return any error something like "Coulnd't find given parameter" etc.
because I don't want to response with "User(s) successfully deleted".
How can I catch that error if there is one.
Or How should I handle that?
You may change your functions to below,
# user.controller.js:
put async/await in function, and add code in try/catch block, and pass res as param in service function deleteByUsernames,
async function _deleteByUsernames(req, res, next) {
try {
await userService.deleteByUsernames(res, JSON.parse(req.query.usernames));
} catch (err) {
next(err);
}
}
# user.service.js:
deleteMany(), This function calls the MongoDB driver's Collection#deleteMany() function. The returned promise resolves to an object that contains 3 properties:
ok: 1 if no errors occurred
deletedCount: the number of documents deleted
n: the number of documents deleted. Equal to deletedCount.
async function _deleteByUsernames(res, usernames) {
let response = await User.deleteMany({ username: { $in: usernames } });
// ERROR
if (response.ok != 1) {
res.status(400).json({ "message": "User(s) not deleted, Something want wrong!" });
}
// SUCCESS
else {
res.status(200).json({
"message": `${response.deletedCount} User(s) successfully deleted out of ${response.n}"
});
}
}
Code is not tested, you can workaround and see what happens!
I think there is no error for no found parameters.
I don't know this is better than nothing for now.
I am not going to mark this as answered because I don't think this is the answer
async function _deleteByUsernames(usernames) {
return await User.deleteMany({username: {$in: usernames}})
.then(result => {
console.log(result);
return (result.deletedCount === 0 ?
"None of the selected user(s) deleted!":
(result.deletedCount !== usernames.length ?
`${result.deletedCount} out of ${usernames.length} selected user(s) successfully deleted!`:
"All selected user(s) successfully deleted!"))
})
.catch(err => {
return `Delete failed with error: ${err}`;
})
}
You can save your delete result in a variable and check for the error
async function _deleteByUsernames(usernames) {
try {
console.log(usernames);
let userDeleteResult = await User.deleteMany({username: {$in: usernames}});
if(!userDeleteResult ){
res.json({status: false, error: 'Some error'}) // or pass the error object here
}
} catch (err) {
throw err;
}
}
I am using "mongodb": "^3.1.6",.
I have a method using the drivers insertOne method (shops is my mongoDb database collection):
/**
* Adds a new shop to the shops collection
* #param {Shop} doc - the new shop to add
*/
static async addShop(shop) {
try {
return await shops.insertOne(shop, {}, (err, result) => {
if (err) {
throw e
}
return result
})
// TODO this should return the new shop
} catch (e) {
console.error(`Something went wrong in addShop : ${e}`)
throw e
}
}
Now the method inserts the document into the collection as expected, but does not return the insert result. How do I return the result value of the callback function?
For reference - I wrote this unit test that I want to get to pass:
test("addShop returns the added shop", async () => {
const testShop = {
name: "Test shop for jest unit tests",
}
const newShop = await ShopsDAO.addShop(testShop)
const shoppingCart = global.DBClient.db(process.env.NS)
const collection = shoppingCart.collection("shops")
expect(newShop.name).toEqual(testShop.name)
await collection.remove({ name: testShop.name })
})
Thanks for the help.
I suggest you not to mix promises and callbacks as it is a bad way to organize your code. According to docs, if you do not pass callback insertOne will return Promise. So I suggest you to rewrite function smth like that:
static async addShop(shop) {
try {
return shops.insertOne(shop)
} catch (insertError) {
console.error(`Something went wrong in addShop : ${insertError}`)
throw insertError
}
}
This addShop will return promise, use await to retrieve data from it:
const newShop = await ShopsDAO.addShop(testShop)
P.S. You can also omit await with return statement
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.
I have a fairly straightforward CRUD app which renders the results of two queries onto one page. The problem that arose once I got this to "work" was that the page required a refresh in order to display the results. On first load, no results were displayed.
I came to figure out that this is a problem/symptom of Node's asynchronous nature. I've been trying to approach this problem by using async/await, and from hours of messing with things, I feel like I'm quite close to the solution, but it's just not working out - I still need a manual refresh to display/render the results on the .ejs page.
The code:
var entries = [];
var frontPageGoals = [];
app.get('/entries', async (req,res) => {
if (req.session.password) {
const entriesColl = await
db.collection('entries')
.find()
.sort({date: -1})
.toArray((err, result) => {
if (err) { console.log(err) }
else {
for (i=0; i<result.length; i++) {
entries[i] = result[i];
}
}
});
const goalsColl = await
db.collection('goals')
.find()
.toArray((err, result) => {
if (err) {console.log(err)}
else {
for (i=0; i<result.length; i++) {
frontPageGoals[i] = result[i];
}
}
});
res.render('index.ejs', {entries: entries, frontPageGoals: frontPageGoals});
}
else {
res.redirect('/');
}
});
Now, I can conceive of a few problems here, but honestly I'm just at my wits end trying to figure this out. For example, I'm sure it's problematic that the empty lists which will contain the results to be passed when the page renders are outside the actual async function. But after trying to move them a dozen different places within the async area... still no dice.
Any help would be hugely appreciated! This is basically the last big "thing" I need done for this app.
I'm not 100% sure about your database driver, but assuming that the toArray() returns a promise (which it does in the default mongodb driver), the await will actually return the value you expect in your callback, result in your case, or in case there was an error, which you expected it as err in your callback, it will be thrown, thus forcing you to use try-catch blocks, in your case, you would just use console.log(err) in the catch block, since you aren't doing any handling
Here's your code after updating :
app.get("/entries", async (req, res) => {
if (req.session.password) {
try {
const entries = await db
.collection("entries")
.find()
.sort({ date: -1 })
.toArray();
const frontPageGoals = await db
.collection("goals")
.find()
.toArray();
res.render("index.ejs", {
entries: entries,
frontPageGoals: frontPageGoals
});
} catch (err) {
console.log(err);
}
} else {
res.redirect("/");
}
});
EDIT
However, if you don't know about promises -which async/await are basically promises-, and wanna just do it using callbacks -not advised-, you would have to just send your response in the callback, and nest the 2nd query in the first query's callback, here is the code,, with some comments to hopefully help you out:
app.get("/entries", (req, res) => {
if (req.session.password) {
// First query
db.collection("entries")
.find()
.sort({ date: -1 })
.toArray((err, entryResult) => {
if (err) {
console.log(err);
} else {
// In the callback of the first query, so it will
// execute 2nd query, only when the first one is done
db.collection("goals")
.find()
.toArray((err, frontPageResult) => {
if (err) {
console.log(err);
} else {
// In the callback of the 2nd query, send the response
// here since both data are at hand
res.render("index.ejs", {
entries: entryResult,
frontPageGoals: frontPageResult
});
}
});
}
});
} else {
res.redirect("/");
}
});
I have removed the async keyword since you no longer need it
I renamed the callback arguments, instead of just result, because both callbacks would have the same argument name, and you would have had to store it in a temp variable