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 } ]
});
Related
I want to query a collection "chat" whose documents have an array field "participants". This array has always two objects with the properties id and name. I want to find the document that matches the two ids of the embeded objects. For example, I want to find the following document in which the ids are "1" and "2".
//chat
//find ids 1 and 2.
//this is the document that I am looking for and should be returned.
{
_id : ytyutu876788
participants : [ {id : 1, name : Murray}, {id : 2, name : Pier} ]
}
//In this document id 1 is present but not together with id 2.
{
_id : d6t6d6tdt6
participants : [ {id : 1, name : Murray}, {id : 3, name : Gustav} ]
}
The query that I have tried to use so far is the following which returns an empty array even though I am sure that the document that I want to find exists in the collection so I know that it is the query what I am doing wrong:
try {
const userId = req.body.userId
const friendId = req.body.friendId
console.log(userId)
console.log(friendId)
await client.connect()
const query = await client.db('instagram_clone').collection('chat').find(
{"participants.id" : {userId, friendId}}
).toArray()
console.log(query)
You may use $all operator like
const query = await client.db('instagram_clone')
.collection('chat')
.find({"participants.id": {$all:[userId, friendId]}})
.toArray();
Or you may like to use $and operator like
const query = await client.db('instagram_clone')
.collection('chat')
.find({$and :[{"participants.id": userId}, {"participants.id": friendId}]})
.toArray();
This another query that does the job, in which I used the $elemmatch operator.
const firstQuery = await client.db('instagram').collection('chat').find({
participants : {
$all: [
{
$elemMatch: {
id: friendId
}
}, {
$elemMatch: {
id: userId
}
}
]
}
}).toArray();
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.
I need to edit my prescriptions quantity .Here is my sample code.
collection.update(
{ "_id" : 1,"medications._id" : 23,"medications.prescriptions._id":77 },
{ $set : { "medications.$0.prescriptions.$1.quantity" : 30 } },
false,
true
)
How to edit this. It does not shown any error .
Instead you can follow a more easy to understand way.
var updateData = {
medications.prescriptions.quantity: 0
}
collection.update({ "_id" : 1,"medications._id" : 23,"medications.prescriptions._id":77 }, updateData, function(err, response){
if(err){
res.json(err);
}
//your success handler
});
you can use only one positional operator
And see this documentation:
https://docs.mongodb.com/manual/reference/operator/update/positional/
I have faced the same issue for the as array inside Array update require much performance impact. So, mongo db doest not support it. Redesign your database as shown in the given link below.
https://pythonolyk.wordpress.com/2016/01/17/mongodb-update-nested-array-using-positional-operator/
I noticed a strange behavior with the mongodb node.js driver findOneAndUpate()...
I mistakenly gave it just a objectId string....thinking it would default to searching by _id field of a document.... so, when I used
User.prototype.updatePetArray = function(user, petElement) {
return this.collection.findOneAndUpdate(user,
{ $push: { pets: petElement } },
{ returnOriginal: false,
maxTimeMS: QUERY_TIME});
}
it pulled up and modified this document, which does not have this number at all:
{ "_id" : ObjectId("56d4e2a381c9c28b3056f792"), "username" : "bob123", "location" : "AT", ...}
Why did it modify this document when 56d4d35881c9c28b3056f78a is not in the document?
After I test it following your code with one non-exist ObjectID,
var col = db.collection('users');
col.findOneAndUpdate('56cd129222222', {fname: 'Daved'}, function(err, r) {
if (err)
console.log(err);
else
console.log(r);
db.close();
});
As a result the first document in the collection was changed .
Per this doc
findOneAndUpdate(filter, update, options, callback)
filter: Specify an empty document { } to update the first document returned in the collection.
So I think this non-exist ObjectId is consider to the same behavior with {}
After reviewing the source code of findAndModify, eventually, the
// Execute the command
self.s.db.command(queryObject
is invoked, and the queryObject is
var queryObject = {
'findandmodify': self.s.name
, 'query': query
};
So I test runCommand in mongo shell with non-exist ObjectId as below, as result, the first document is returned.
> db.users.runCommand({findandmodify: 'users', query: '123ddae344', update: true
})
{
"lastErrorObject" : {
"updatedExisting" : true,
"n" : 1
},
"value" : {
"_id" : ObjectId("56c687275d81735019263d1f"),
"fname" : "Daved"
},
"ok" : 1
}
The docs keep saying the filter parameter should be an object.
The wrong behavior is likely to some side effect of mis-interpreting the value, being a string not an object (and maybe a truthy value, non-empty string).
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.