Mongoose find-by-reference returns nothing - node.js

This is data from the mongo client:
> db.projects.find({ })[1];
{
"name" : "App 276",
"slug" : "app276",
"createdByUser" : ObjectId("52f20518b66ae3622c000002"),
"_id" : ObjectId("52fc91f508e3507c19000002"),
"screens" : [ ],
"dateUpdated" : ISODate("2014-02-13T09:34:23.102Z"),
"dateCreated" : ISODate("2014-02-13T09:34:23.102Z"),
"__v" : 0
}
This is my server code:
/** List Projects */
exports.list = function(req, res) {
console.log('list', mongoose.Types.ObjectId(req.params.userId));
return Project.find({ createdByUser: mongoose.Types.ObjectId(req.params.userId) }, function (err, projects) {
if (!err) {
return res.json(projects);
}
else {
return res.send(err);
}
});
};
and this is my server output:
list 52fc9720b85bac3c1a000002
GET /api/projects 200 42ms - 2b
The JSON output is an empty array - why?

Your requested project created by user 52f **c9720b85bac3c1a** 000002.
Your database extract seems to contain a project by user 52f **20518b66ae3622c** 000002
Have you tried with the right id ?

Take a look at Mongoose Population. It's better use this feature in this case. http://mongoosejs.com/docs/populate.html

Related

Updating nested array mongodb

I think there are multiple ways to do this, and that has me a little confused as to why I can't get it to work.
I have a schema and I would like to update Notes within it but I can't seem to do it. Additionally, if I want to return the notes how would I go about doing it?
schema :
{
_id : 1234
email : me#me.com
pass : password
stock : [
{
Ticker : TSLA
Market : Nasdaq
Notes : [
"Buy at 700",
"Sell at 1000"
]
},
{
Ticker : AAPL
Market : Nasdaq
Notes : [
"Buy at 110",
"Sell at 140"
]
},
]
}
Each user has a list of stocks, and each stock has a list of notes.
Here is what I have tried in order to add a note to the list.
router.post(`/notes/add/:email/:pass/:stock/:note`, (req, res) => {
var email = req.params.email
var pass = req.params.pass
var note = req.params.note
var tempStock = req.params.stock
userModel.findOne({email: email} , (err, documents)=>{
if (err){
res.send(err);
}
else if (documents === null){
res.send('user not found');
}else if (bcrypt.compareSync(pass , documents.pass)){
userModel.findOneAndUpdate({email : email , "stock.Ticker" : tempStock}, {$push : {Notes : note}} ,(documents , err)=>{
if(err){
res.send(err);
}else {
res.send(documents.stock);
}
})
}
})
})
Thanks :)
Currently, you are pushing the new note into a newly created Notes property inside the model instead of into the Notes of the concrete stock. I am not completely aware of the mongoose semantics but you need something like this:
userModel.findOneAndUpdate({ email: email, "stock.Ticker": tempStock }, { $push: { "stock.$.Notes": note } }, (documents, err) => {
$ gives you a reference to the currently matched element from the stock array.
For the second part, I am not sure what you mean by
Additionally, if I want to return the notes how would I go about doing it?
They should be returned by default if you're not doing any projection excluding them.
Also, as per the docs(and general practise), the callback for the findOneAndUpdate has a signature of
(error, doc) => { }
instead of
(documents, err) => { }
so you should handle that.

How to delete an object from an array in mongodb?

MongoDB collection/doc :
{
_id:something,
name:something,
todos: [
{key:1234},
{key:5678}
]
}
I want to delete the object with key:5678 using mongoose query. I did something like this but It's not deleting the object at all and returning the User with unchanged todos array.
Node Route:
router.post('/:action', async (req, res) => {
try {
if (req.params.action == "delete") {
const pullTodo = { $pull: { todos: { key: 5678 } } }
const todo = await User.findOneAndUpdate({ _id:req.body.id} },pullTodo)
if (todo) {
res.json({ msg: "Todo Deleted", data: todo });
}
}
} catch (err) {
console.log(err)
}
})
I have allso tried findByIdAndUpdate(),update() methods but none of them deleting the object from the array. Getting User as a result without deleting the object from the array.
It is working, but you forgot give an configuration to the function call of Model.findByIdAndUpdate..
const todo = await User.findOneAndUpdate({ _id:req.body.id} },pullTodo, {new: true});
// if {new: true} is enabled, then it will give the latest & updated document from the
// result of the query. By default it gives the previous document.
Do some, research first. This isn't a type of question that should be asked. It's already been answered several times in stackoverflow.
Try using Model.findOneAndRemove() instead. It also makes only one call to the database.
Example: User.findOneAndRemove({'todos':{'$elemMatch':{key}});
can you please re-visit your JSON like below and see if this design works for you.
> db.test6.find()
{ "_id" : "mallik", "name" : "mallik-name", "todos1" : { "key1" : [ 1234, 5678 ] } }
> db.test6.update({},{$pull:{"todos1.key1":5678}},{multi:true});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.test6.find()
{ "_id" : "mallik", "name" : "mallik-name", "todos1" : { "key1" : [ 1234 ] } }
>
I was adding a key property to every "Todo" using "mongoose.Types.ObjectId()" and I was querying with id string like : "5f439....." which was the problem. So I used:
1st Step: MongoId = require('mongodb').ObjectID;
2nd Step: const key = MongoId (**<actual id here>**);

Data model reference using ObjectID in Mongoose

I have two mongoose models users and requests. One user can have multiple requests. To link these two data models, I am referencing the objectID of the user in the Request model. However when I am trying to fetch all the requests for a particular user ID, I don't get any result.
Users
var UserSchema = new Schema({
name: String,
username: String
});
module.exports = mongoose.model('User', UserSchema);
Requests
var RequestSchema = new Schema({
type: String,
user_id: { type : mongoose.Schema.Types.ObjectId, ref: 'User'}
});
module.exports = mongoose.model('Request', RequestSchema);
Below is the sample code which fetches the requests based on the User ID passed in the URL
exports.getRequests = function (req, res, next) {
var userId = req.params.id;
console.log('USER ID'+userId);
Request.findOne({'user_id': 'kenan'}, function (err, request) {
if (err) return next(err);
if (!requests){
console.log('NO REQUEST FOUND');
return res.send(401);
}
res.json(request);
});
};
I know I am using findOne which will return me a single resource. However this is failing and I am not getting any result. I have in my DB the data populated. Below is the URL for fetching the requests.
http://localhost:9000/api/V1/users/547ee1e82e47bb982e36e433/requests
Please help suggest as to what I am doing wrong over here.
Thanks
You need to populate the references to the users once you do a find().
Once you find all the requests, you need to resolve the references to
the users. Use populate to resolve the references.
After resolving, filter the requests based on the name that you
are searching for.
Updated Code:
Request
.find().populate({'path':'user_id',match:{'name':'kenan'}})
.exec(function (err, resp) {
var result = resp.filter(function(i){
return i.user_id != null;
});
console.log(result);
})
OK, the above answer is correct and thanks for your response. I incorrectly populated my Requests collection. This is just for every one's else knowledge. When you have a Object ID reference as one of the fields in the collection then make sure that you are inserting the ObjectID object instead of just the numerical value.
Wrong
{ "_id" : ObjectId("54803ae43b73bd8428269da3"), "user_id" : "547ee1e82e
47bb982e36e433", "name" : "Test", "service_type" : "test", "version" : "10"
, "environment" : "test", "status" : "OK", "__v" : 0 }
Correct
{ "_id" : ObjectId("54803ae43b73bd8428269da3"), "user_id" : ObjectId("547ee1e82e
47bb982e36e433"), "name" : "test", "service_type" : "test", "version" : "10"
, "environment" : "test", "status" : "OK", "__v" : 0 }

MongooseJS - How to find the element with the maximum value?

I am using MongoDB , MongooseJS and Nodejs.
I have a Collection ( called Member ) with the following Fields -
Country_id , Member_id , Name, Score
I want to write a query which returns the Member with the max Score where Country id = 10
I couldnt find suitable documentation for this in MongooseJS.
I found this at StackOVerflow ( this is MongoDB code )
Model.findOne({ field1 : 1 }).sort(last_mod, 1).run( function(err, doc) {
var max = doc.last_mod;
});
But how do I translate the same to MongooseJS ?
Member
.findOne({ country_id: 10 })
.sort('-score') // give me the max
.exec(function (err, member) {
// your callback code
});
Check the mongoose docs for querying, they are pretty good.
If you dont't want to write the same code again you could also add a static method to your Member model like this:
memberSchema.statics.findMax = function (callback) {
this.findOne({ country_id: 10 }) // 'this' now refers to the Member class
.sort('-score')
.exec(callback);
}
And call it later via Member.findMax(callback)
You do not need Mongoose documentation to do this.
Plain MongoDb will do the job.
Assume you have your Member collection:
{ "_id" : ObjectId("527619d6e964aa5d2bdca6e2"), "country_id" : 10, "name" : "tes2t", "score" : 15 }
{ "_id" : ObjectId("527619cfe964aa5d2bdca6e1"), "country_id" : 10, "name" : "test", "score" : 5 }
{ "_id" : ObjectId("527619e1e964aa5d2bdca6e3"), "country_id" : 10, "name" : "tes5t", "score" : -6 }
{ "_id" : ObjectId("527619e1e964aa5d2bdcd6f3"), "country_id" : 8, "name" : "tes5t", "score" : 24 }
The following query will return you a cursor to the document, you are looking for:
db.Member.find({country_id : 10}).sort({score : -1}).limit(1)
It might be faster to use find() than findOne().
With find().limit(1) an array of the one document is returned. To get the document object, you have to do get the first array element, maxResult[0].
Making Salvador's answer more complete ...
var findQuery = db.Member.find({country_id : 10}).sort({score : -1}).limit(1);
findQuery.exec(function(err, maxResult){
if (err) {return err;}
// do stuff with maxResult[0]
});
This is quick and easy using the Mongoose Query Helpers.
The general form for this could be:
<Your_Model>.find()
.sort("-field_to_sort_by")
.limit(1)
.exec( (error,data) => someFunc(error,data) {...} );
tldr:
This will give you an array of a single item with the highest value in 'field_to_sort_by'. Don't forget to access it as data[0], like I did for an hour.
Long-winded: Step-by-step on what that string of functions is doing...
Your_Model.find() starts the query, no args needed.
.sort("-field_to_sort_by") sorts the everything in descending order. That minus-sign in front of the field name specifies to sort in descending order, you can discard it to sort in ascending order and thus get the document with the minimum value.
.limit(1) tells the database to only return the first document, because we only want the top-ranked document.
.exec( (error,data) => someFunc(error,data) {...} ) finally passes any error and an array containing your document to into your function. You'll find your document in data[0]
You can also use the $max operator:
// find the max age of all users
Users.aggregate(
{ $group: { _id: null, maxAge: { $max: '$age' }}}
, { $project: { _id: 0, maxAge: 1 }}
, function (err, res) {
if (err) return handleError(err);
console.log(res); // [ { maxAge: 98 } ]
});

How to compose a complex query using resultset from another query in Mongodb?

I am writing an application using Mongo (using Mongo native driver), Node and Express.
I have Students, Courses and Professors documents in mongo.
I want to retrieve a list of all 'Professor' documents whose courses a student is currently taking or has taken in the past.
Students: {courseid, ....}
Course: {professors, ....}
Professors: {....}
This is what I intend on doing:
I first issue a query to retrieve all the course ids for a student.
Then I have to compose and issue another query to get the professor id for all those courses.
And then finally I have to get all the "professor" documents associated with the professor ids.
Step 1 is not a problem and now I have all the course ids. But I am not sure how to do step2. Step2 and 3 are similar, once I figure out step 2, step3 will be easy.
Basically I want to issue one query in step2 to retrieve all the professor ids. I don't want to issue 10 separate queries for 10 course ids.
Here is what I have:
function getProfsByStudent(req, res, next)
{
db.collection('students', function(err, stuColl)
{
stuId = new ObjectID.createFromHexString(req.params.stuId);
stuColl.find({_id : userId}, { 'current_course_id' : 1 , 'past_courses.course_id' : 1 , _id : 0 })
{
db.collection('courses', function(err, courseColl)
{
courseColl.find({$or : []}) // THIS IS WHERE I AM STUCK
});
res.send(posts);
});
});
}
Update
Question updated based on the answer.
So, this is the JSON I end up with after the stuColl.find call:
[{"current_course_id":"4f7fa4c37c06191111000005","past_courses":[{"course_id":"4f7fa4c37c06191111000003"},{"course_id":"4f7fa4c37c06191111000002"}]}]
Now I want to use the above to do another find to get all professor IDs. But all I get is a null result. I think I am very close. What am I doing wrong?
stuColl.find({_id : userId}, { 'current_course_id' : 1 , 'past_courses.course_id' : 1 , _id : 0 }).toArray(function(err, courseIdsArray)
{
db.collection('courses', function(err, courseColl)
{
courseColl.find({$or : [ {_id : 'courseIdsArray.current_courses_id' }, {_id : {$in : courseIdsArray.past_courses}} ]}, {'professor_ids' : 1}).toArray(function(err, professorIdsArray)
{
res.send(professorIdsArray);
});
});
});
I think that instead of $or you should be using the $in operator
http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24in
and:
stuColl.find....
should be returning its result toArray to be used in the $in operator.
Update
I think this is what you're looking for:
db.collection("Students").find({_id : "8397c60d-bd7c-4f94-a0f9-f9db2f14e8ea"}, {CurrentCourses:1, PastCourses : 1, _id : 0}).toArray(function(err, allCourses){
if(err){
//error handling
}
else{
var courses = allCourses[0].CurrentCourses.concat(allCourses[0].PastCourses);
db.collection("Courses").find({ _id : { $in: courses}}, {"Professors" : 1, _id : 0}).toArray(function(err, professors){
if(err){
//error handling
}
var allProfs = [];
for(var i = 0; i < professors.length; i++){
allProfs = allProfs.concat(professors[i].Professors);
}
db.collection("Professors").find({ _id : { $in: allProfs }}).toArray(function(err, results){
console.log(results);
});
});
}
});
It goes through the students collection and finds the student and then through all his/her courses to finally load all the teachers.

Resources