Add and Remove Within Mongoose - node.js

{
"_id" : 1,
"_name" : "peter",
"favourites" : [
{
"user_id" : 1
},
{
"user_id" : 2
}
]
}
I have this schema within Mongo, and I'm using Node/Express/Mongoose to manage my stack. I'm new to this particular stack and I have a 'favourite' button on the website I'm building. What's the best way to add/remove from favourites?
Do I really need to setup two routes, one for POST and another for PUT? I'm not sure what the best way is to solve this particular problem, I've done some searching and I've not found anything that relevant.

I'm not going to give you the full answer as it's always good to learn, so I've give you 90% of what I think is what you're looking for. Do a check for the name and if it doesn't exist within the favourites array, do the following:
db.collection.update(
{ "username": "walterwhite" },
{ $push:
{
favourites: [ { "user_id": "7" } ]
}
}
)

First of all you will need routes for any of the actions. It's probably the best practice to use the HTTP Verbs (GET/POST/PUT/DELETE etc.). Then your routes would look something like this: (this could be wrapped in a file that contains all routes and later required in the main app file (ex. app.js).)
module.exports = function (router) {
router.route('/favourites')
.get(function (req, res, next) {
// Code for getting all items
})
.post(function (req, res, next) {
// Code for inserting new item
})
.delete(function (req, res, next) {
// Code for deleting an item
});
}
Then you will need to build a form with fields corresponding to the item's properties and use POST as a submit method. You will need it for inserting (adding) new items. The request sent from the form, will send you to the post route, where you will handle your logic for appending item to the list.
To implement the deletion you will have to send a DELETE, request passing an identifier of the item you'd want to delete. You will handle this code in the delete route.
Of course, the get route is for getting all items in the collection for further processing or visualizing.

you must create an object id associated to every user_id. you can create such id's using.you can associate multiple models with mongoose population
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
"favourites" : [
{
"_id" : ObjectId("57338c9cc5c8cf74181b4cff"),
"user_id" : 1
},
{
"_id" : ObjectId("5734588e5a54d45434693a09")
"user_id" : 2
}
]
either render the page or send a json object as response with the object id in it as per your setup.
user_id-2
user_id-1
now you can make an ajax request with the object id when clicked on a particular <a> tag.you can achieve this with jquery. Then remove the user associated with that id from your favorites.
If you add more details.I can give you a specific answer.

Related

MongoDB - Use results of multiple fetch queries in one update query

I am building a platform for students to give mock test on. Once the test is complete, a results are to be generated for them relative to other students who attempted the said test.
Report contains multiple parameters i.e. rank, rank within their batch, and stuff like average marks people got on the given test are updated.
To get each of this data, I need to perform a separate query on the database and then I got to update 1. the result of current user who attempted the test 2. the result of everyone else (i.e. everyone's rank changes on new attempts)
So I need to perform multiple queries to get the data and run 2-3 update queries to set the new data.
Given mongodb calls are asynchronous, I can't find a way to gather all of that data at one place to be updated.
One way is to put the next query within the callback function of the previous query but I feel like there should be a better way than that.
Maybe you could use Promise.all()
Example:
const initialQueries = []
initialQueries.push("Some promise(s)")
Promise.all(initialQueries).then(results => {
// After all initialQueries are finished
updateQueries()
}).catch(err => {
// At least one failed
})
Use db.collection.bulkWrite.
It allows multiple document insertions, updates (by _id or a custom filter), and even deletes.
const ObjectID = require('mongodb').ObjectID;
db.collection('tests').bulkWrite([
{ updateOne : {
"filter" : { "_id" : ObjectID("5d400af131602bf3fa09da3a") },
"update" : { $set : { "score" : 20 } }
}
},
{ updateOne : {
"filter" : { "_id" : ObjectID("5d233e7831602bf3fa996557") },
"update" : { $set : { "score" : 15 } }
}
}
]);
New in version 3.2.

error:can't set header after they are send while referencing collections node js

I have two collection assign and final. My code is like this
app.post('/checkuser',function(req,res){
//var id="ObjectId('"+req.body.id+"')";
var mongo=require('mongodb');
var ObjectID=require('mongodb').ObjectID;
var obj_id=new ObjectID(req.body.id);
//var username=req.body.username;
mongoClient.connect(url,function(err,db){
db.collection('assign',function(err,collection){
collection.findOne({"_id":obj_id},function(err,result){
res.send(result);
db.final.findOne({"username":result.user.username},function(err,results){
res.send(results);
})
})
})
})
})
The data in the assign collection is in this format
{
"_id" : ObjectId("59674fb5a13752326c939dcd"),
"user" : {
"username" : "c",
"password" : "v"
}
}
and the data in final collection is like this
{
"_id" : ObjectId("59674cfc081305f9c8b5bc58"),
"username" : "c",
"name" : "MAC",
"asset" : "PRO",
"place" : "Bangalore"
}
I want to access the data for the username:"c" by providing the id in assign collection.
With the above code, I get error - Can't set header after they are sent.
What is the correct way of implementing this?
You're calling res.send() multiple times for the same res. You can only send a response once for a single HTTP request.
If you want to respond with multiple pieces of information, you will need to combine them in some way before passing it to res.send().
I found out that the error basically means that the data is already sent and then the header is getting manipulated.Also, res object cannot be called multiple times in the same function. So, the second query should be executed in the success function of the first function.
Here is a possible solution:
mongoClient.connect(url,function(err,db){
db.collection('assign',function(err,collection){
collection.findOne({"_id":obj_id},function(err,result){
db.final.findOne({"username":result.user.username},function(err, result, results){
res.send({ "first": result, "second": results });
})
})
})
})
Sorry, I can't verify that, but what it is aiming to do is pass the first result into the next function and then send an object with both results in it. If it works, it should at least get you moving again.
You are into a chaotic territory because you are nesting fairly deep. I recommend learning about and practicing Promises and async/await. Aim to make a function for each thing, and each function should have only one purpose. You should also research "callback hell", what it is and how to avoid it.

Mongoose: How to find documents by sub-collection's document property value

I’m using Mongoose version 4.6.8 and MongoLab (MLab). I have a Mongoose schema called “Group” that has a collection of User subdocuments called “teachers”:
var GroupSchema = new Schema({
//…more properties here…//
teachers: [{
type: Schema.ObjectId,
ref: 'User'
}]
});
This is a document from the “groups” collection on MongoLab:
{
//…more properties here…//
"teachers": [
{
"$oid": "5799a9c759feea9c208c004c"
}
]
}
And this is a document from the “users” collection on MongoLab:
{
//…more properties here…//
"username": "bob"
}
But if I want to get a list of Groups that have a particular teacher (User) with the username of “bob”, this doesn’t work (the list of groups is empty):
Group.find({"teachers.username": "bob"}).exec(callback);
This also returns no items:
Group.find().where('teachers.username').equals('bob').exec(callback);
How can I achieve this?
Without some more knowledge of your set up (specifically whether you want anybody named Bob or a specific Bob whose id you could pick up first) - this might be some help although I think it would require you to flatten your teachers array to just their ID's, not single-key objects.
User.findById(<Id of Bob>, function(err, user){
Group.find({}, function(err, groups){
var t = groups.map(function(g){
if(g['teachers'].indexOf(user.id))
return g
})
// Do something with t
})
})
You can use populate to do that.
Try this:
Group.find({})
.populate({
path : 'teachers' ,
match : { username : "bob" }
})
.exec(callback);
populate will populate based on the teachers field (given path) and match will return only those who have username bob.
For more information on mongoose populate options, Please read Mongoose populate documentation.
I think the solution in this case is to get a teacher’s groups through the User module instead of my first inclination which was to go through the Groups module. This makes sense because it is in line with how modern APIs represent a one-to-many relationship.
As an example, in Behance’s API, an endpoint for a user’s projects is:
GET /v2/users/user/projects
And a request to this endpoint (where the User’s username is “matiascorea”) would look like this:
https://api.behance.net/v2/users/matiascorea/projects?client_id=1234567890
So in my case, instead of finding the groups by teacher, I would need to simply find the User (teacher) by username, populate the teacher’s groups, and use them:
User.findOne({username: 'bob'})
.populate('groups')
.exec(callback);
And the API call for this would be:
GET /api/users/user/groups
And a request to this endpoint would look like this:
https://example.com/api/users/bob/groups

PATCH method to MongoDB using Node

I want to create a PATCH method for my API but there is something I don't understand. Imagine I have the following document in my MongoDB database:
{
_id : ObjectId(1234...),
name : "bob",
age : 30
}
Now I would like to update this document but I don't know what keys my API will receive. So imagine I make a request in order to change the age but also add a last_name.
The request result would be like this:
{
_id : ObjectId(1234...),
name : "bob",
last_name : "smith",
age : 44
}
The main problem here is that I don't know the arguments I will receive.
My goal is to update the values of the existing keys and add the keys that are not in the document.
Any idea?
Thanks
You want to use the $set operator.
What this does is only updates the keys that are sent in the update query. Without $set, it will overwrite the whole object, which is obviously not what you want.
app.patch('/user/:id', function (req, res) {
var updateObject = req.body; // {last_name : "smith", age: 44}
var id = req.params.id;
db.users.update({_id : ObjectId(id)}, {$set: updateObject});
});
I'm assuming a couple things here:
You are using express.
You are using either the mongodb driver or mongojs npm module

Extracting value from nested array in Mongoose

I'm working with Mongoose for the first time and I'm trying to accomplish what seems to be a simple task. I have a users document that contains a clients property, which consists of an array of client id's. As an example, my document looks like this:
{
email: "nick#movementstrategy.com",
password: "$2a$10$xZVzMYgyoyT.biOMDrBlRe3HNHY5A6lXga6uc8b/cnIAX/khQ7ep2",
modified: ISODate("2013-01-16T00:13:56.894Z"),
created: ISODate("2013-01-16T00:13:56.894Z"),
_id: ObjectId("50f5f0c4d6bbbcc6ce000002"),
clients: [
"50f6e118e0ccf9a1e9000001",
"50f6e12be0ccf9a1e9000002"
],
__v: 0
}
I've created a middleware that removes dependencies for a client when I call remove();
clientSchema.pre('remove', function(next) {
Sweepstakes.remove({client_id: this._id}).exec();
Submission.remove({client_id: this._id}).exec();
// find users with this._id present in clients, and remove from array
next();
});
Now, I simply need to locate all users who have the client id present in clients, remove the id, and update the user.
I know that I could easily query for all users with the id present, and then loop through and save out each user individually... But that seems inefficient, and my gut tells me that there is a better way to accomplish what I am trying to do -- just having a hard time locating it in the documentation.
What is the most efficient way to do this using Mongoose?
Probably something like this:
Users.update({condition}, {$pull : { clients: this._id} }, function(err, numAffected) {
//handle errors and whatever
next();
});
You can add clients : {$in : [this._id]} as condition to limit the update.

Resources