Mongoose: Multi find() in forEach - Where is final then() - node.js

I use multi find() to populate 'categories' and 'pic' for 'post'. But I don't know Where is the data returned finally in full to 'res.send(posts)'.
Or use another method, 'Promise.all' for example, Please help me solve the problem
Post.find().then(posts=> {
async.forEach(posts, function(post, done) {
Cat.find().where('posts').in([post.id]).then(categories=> {
post.categories = categories;
var id=mongoose.Types.ObjectId(post.id);
File.findOne({'related.ref': id}).then(pic=>{
post.pic=pic;
});
})
//res.send(posts);????
});
});

You could use async-await for your route handler:
async (req, res) => {
const posts = await Post.find()
for (post of posts) {
const categories = await Cat.find().where('posts').in([ post.id ])
post.categories = categories
const id = mongoose.Types.ObjectId(post.id)
const pic = await File.findOne({ 'related.ref': id })
post.pic = pic
}
res.send(posts)
}

Related

Confusion in Query API of mongoose

Can anyone please tell me why the given below code(PART-1) works fine whereas gives error that posts.skip is not a function? in PART-2
I am wondering that why in PART-1 code it returned Query instance whereas I am getting an array of posts.
PART-1
const query = MyModel.find(); // `query` is an instance of `Query`
query.setOptions({ lean : true });
query.collection(MyModel.collection);
query.where('age').gte(21).exec(callback);
PART-2
const posts = await PostModel.find({});
posts.skip((page - 1) * 10).limit(10);
Basically I am trying to refactor this code so that I don't have to each time use pagination logic and achieve that by writing a pagination util function where the function will have first params a Query instance of mongoose
getAllPost: async (req, res, next) => {
const pagination = req.query.pagination ? parseInt(req.query.pagination) : 10
const currentPage = req.query.page ? parseInt(req.query.page) : 1
try {
const posts = await PostModel.find({}).lean()
.skip((currentPage - 1) * pagination)
.limit(pagination)
.populate('userId', ['name.firstName', 'name.lastName', 'email', 'isAdmin'])
.sort({ updatedAt: -1 })
.exec()
if (!posts.length) {
return res.status(HttpStatus.NOT_FOUND).json({ message: 'No posts found' })
}
return res.status(HttpStatus.OK).json({ posts: posts })
} catch (error) {
HANDLER.handleError(res, error)
}
},
Util function like: (For example)
module.exports = {
paginate: async (req, query, next) => {
const options= {
pagination: req.query.pagination ? parseInt(req.query.pagination) : 10,
currentPage: req.query.page ? parseInt(req.query.page) : 1
}
query.skip((currentPage-1)*pagination).limit(pagination)
query.exec((err, result)=>{ return result; })
}
}
I finally fixed this by implementing this paginate helper:
i.e /utils/paginate.js
module.exports = {
paginate: (req) => {
const query = {}
const pagination = req.query.pagination ? parseInt(req.query.pagination) : 10
const currentPage = req.query.page ? parseInt(req.query.page) : 1
query.skip = (currentPage - 1) * pagination
query.limit = pagination
return query
}
}
and use this helper function to paginate, by apssing as an argument to .find() in mongoose, for example:
const helper = require('../utils/paginate');
getAllPost: async (req, res, next) => {
try {
const posts = await PostModel.find({}, {}, helper.pagnate(req)).lean()
.populate('userId', ['name.firstName', 'name.lastName', 'email', 'isAdmin'])
.sort({ updatedAt: -1 })
.exec()
if (!posts.length) {
return res.status(HttpStatus.NOT_FOUND).json({ message: 'No posts found' })
}
return res.status(HttpStatus.OK).json({ posts: posts })
} catch (error) {
HANDLER.handleError(res, error)
}
}

How to allow users to add in parameter in api?

How to allow users to add in parameter in api? i.e http://localhost:8080/amk_codes?collada_north=1.361692308&collada_east=103.8527273
I have the following codes:
app.get("/amk_codes", async (req, res) => {
const rows = await readamk_codes();
res.send(JSON.stringify(rows))
})
async function readamk_codes() {
try {
const results = await client.query("select collada_north,collada_south,collada_east from amk_codes");
return results.rows;
}
catch(e){
return [];
}
}
The parameters in url are located in req.query object. In your example you can access to collada_north and collada_south values with req.query.collada_north and req.query.collada_south.
app.get("/amk_codes", async (req, res) => {
var collada_north = req.query.collada_north
var collada_south = req.query.collada_south
const rows = await readamk_codes();
res.send(JSON.stringify(rows))
})
...
Of course you have to check if parameters are present in the request ( they could also be null I think ).

how to query mongodb based on object to get documents that contains one or more equal properties (Search)

Im trying to figure out how to Post a search object to mongo, and find all documents that matches one or more properties stated in the search object.
eg if i have post a json object as this:
// searchObject
{
"kind": "employee"
"name": "casper",
"email": "daniel#mail.com"
}
i want to get all documents that contains "name": "casper"or "email":"daniel#mail.com"or both from the employee collection.
this is what i have so far. but i dont know how to loop through my properties.
router.post('/search', async (ctx) => {
const searchObject = Object.assign(ctx.request.body);
const collection = searchObject.kind
const result = await store[collection].find({
$and: [{ searchObject }]
})
console.log('result', result)
})
Try this:
router.post('/search', async ctx => {
const { kind, ...searchObject } = ctx.request.body;
const collection = searchObject.kind;
const conditions = Object.keys(searchObject).reduce((acc, key) => {
acc.push({ [key]: searchObject[key] });
return acc;
}, []);
const result = await store[collection].find({
$or: conditions,
});
console.log('result', result);
});
add if (key !== "kind") condition in reduce function
router.post('/search', async ctx => {
const searchObject = ctx.request.body;
const collection = searchObject.kind;
const conditions = Object.keys(searchObject).reduce((acc, key) => {
if (key !== "kind") acc.push({ [key]: searchObject[key] });
return acc;
}, []);
const result = await store[collection].find({
$or: conditions,
});
console.log('result', result);
});

Mongo/Express: How to return all documents in collection if no query params are passed?

I'm trying to return all documents from my Mongo collection if no query parameters are passed. Currently I have 3 optional query parameters that could be passed by the user.
localhost:3000/api/projects
//should return all projects. Currently this is returning []
localhost:3000/api/projects?id=1
//should return projects with id of "1". Working properly.
localhost:3000/api/projects?name=myproject
//should return projects with name of "myproject". Working properly.
localhost:3000/api/projects?created_by=John
//should return projects created by "John". Working properly.
Within my route, I'm trying to determine my request has any query values. If it does not, then I want to return all documents in the collection. As stated above, this is not returning anything.
router.get('/', async (req, res) => {
if (req.query !== '') {
const project = await Projects.find({
$or: [
{ _id: req.query.id },
{ name: req.query.name },
{ created_by: req.query.created_by }]
});
res.json(project);
}
else {
const project = await Projects.find();
res.json(project);
}
});
Try as below:
router.get('/', async (req, res) => {
let searchQuery = {}
if(req.query.id){
searchQuery._id = req.query.id
}
if(req.query.name){
searchQuery.name = req.query.name
}
if(req.query.created_by){
searchQuery.created_by = req.query.created_by
}
const project = await Projects.find(searchQuery);
res.json(project);
});
You can write your api handler like this:
router.get('/', async (req, res)=>{
let options = {...req.query};
try{
const project = await Projects.find(options);
res.json(project);
}catch(e){
console.log(e);
}
});
This will fetch the documents on the basis of your query. If there is no query params req.query will be empty object and hence it will find all documents.
Hope this helps!!
router.get('/', async (req, res) => {
const id = req.query.id || null;
const name = req.query.name || null;
const created_by = req.query.created_by || null;
const query = { id, name, created_by };
const project = await Projects.find(query);
res.json(project);
});
I didn't test it, but I would solve your problem this way.

Node js, Wait, until get all data from MongoDB

I have a problem with async function and callbacks in Node js. I need to get my friends' all posts and display it. But if i do that without setTimeout(), it returns only some part of data. How can i solve this problem without putting setTimeout? Of course it's absurd to wait for 5-6 or 10 seconds to get all data. I also tried with Promises, but again response is incomplete. Please someone can help me?
//Sending request with axios to Controller
axios.post(packages.proxy+'users/getFriendsPosts',{id: user_id},config)
.then(res => {
// Code for displaying result
})
//User Controller
router.post("/getFriendsPosts", getFriendsPosts);
//Send request body to userService.js
function getFriendsPosts(req, res, next) {
userService.getFriendsPosts(req.body, function(posts, user){
res.json({posts,user});
})
.catch(err => next(err));
}
//userService.js
module.exports = {
getFriendsPosts,
};
async function getFriendsPosts(user,callback){
var arr = [];
var array = [];
MongoClient.connect(url, async function(errr, db) {
if (errr) throw errr;
var dbo = db.db("drone-x");
//Find user
dbo.collection("users").find({_id: ObjectId(user.id)}).toArray(async function(err, result) {
if (err) throw err;
result.forEach(async function(element, index) {
if(element.friends.length != 0){
element.friends.forEach(async function(elem) {
//Find user's friends
dbo.collection("users").find({_id: ObjectId(elem.id)}).toArray(async function(error, res) {
if (error) throw error;
//push user's friends to arr
arr.push(res);
res.forEach(async function(elements) {
//Find user's friends posts
dbo.collection("posts").find({userId: elements._id.toString()}).toArray(async function(errors, results) {
if (errors) throw errors;
//push user's friends posts to array
array.push(results);
//callback results through setTimeout
setTimeout(async function(){ await callback(array, arr); db.close(); }, 2000);
});
});
});
});
}
else
{
await callback("0");
}
});
});
});
}
If i don't use setTimeout function, it just returns 2-3 data, but with setTimeout, it returns all data. And if data will be raise, then i need to increase the setTimeout time. But of course it's not good idea. Someone can help me?
You should use try catch in this code
getFriendsPosts = async (user,callback) => {
const arr = [];
const array = [];
const db = await MongoClient.connect(url);
const dbo = db.db("drone-x");
const results = await dbo.collection("users").find({_id: ObjectId(user.id)})
const resultPromise = _.map(results, async element => {
const friends = _.get(element, 'friends', [])
if(friends.length != 0) {
const friendPromise = _.map(friends, async friend => {
const ress = await dbo.collection("users").find({_id: ObjectId(friend.id)})
arr.push(ress);
const resPromise = _.map(ress, async res => {
const posts = await dbo.collection("posts").find({userId: res._id.toString()})
const postPromise = _.map(posts, post => {
array.push(post);
})
await Promise.all(postPromise)
})
await Promise.all(resPromise)
})
await Promise.all(friendPromise)
}
})
await Promise.all(resultPromise)
return { arr , array }
}
I am not recommand this way it take too much time. You should use mongoose and use aggregation for long Query.

Resources