Upserting with Mongoose - node.js

I have a schema that is defined like
var UserSchema = mongoose.Schema({
user: {
ipAddress: String,
pollIDs: [{
id: String
}]
}
});
var User = module.exports = mongoose.model('User', UserSchema);
What I want to create is a route that checks the requests ip address, see if it exists in the database, if it doesn't create a new document with the ipAddress property set accordingly and the current req.body.poll_id to be an element in the pollIDs array.
However, if there is a document with that ip address I want the req.body.poll_id to be pushed into the pollIDs array.
I would demonstrate my first attempt, but I know that I've messed up the parameters on the findOneAndUpdate call.

Should be as simple as:
User.findOneAndUpdate(
{'user.ipAddress': req.body.ipAddress},
{$push: {'user.pollIDs': {id: req.body.poll_id}}},
{upsert: true, new: true},
(err, doc) => {...});
The upsert will take the query object and apply the update operation to it in the case where it needs to insert a new document.

Related

How to get latest object of array from users in Mongodb

my mongodb structure
//First user
_id:ObjectId("12345")
name:"prudhvi"
authors:Array
0:Object
authorId:"77777"
authortitle:"medicine"
1:Object
authorId:"66666"
authortitle:"Hospital"
//second user
_id:ObjectId("67890")
name:"venkat"
authors:Array
0:Object
authorId:"55555"
authortitle:"Doctor"
1:Object
authorId:"44444"
authortitle:"Nurse"
Can someone please help here i have two users, On that i need to get only the latest object of authors array. Here my latest Object is 1:Object, If in case one more is added, I need to get 2:Object of data of all users.
I tried like this but i am getting all objects of authors array, But i need to get latest object
userRouter.post('/getAuthors', function (req, res) {
Collections.user.find(req.body.user, function (err, result) {
if (err) res.status(500).send("There was a problem finding the user");
if (result.length > 0) {
res.status(200).send(result[0].authors);
}
}).select({ "authors": 1 });
});
Try using this
Collections.user.find().limit(1).sort({$natural:-1})
Take a look at $natural and cursor.sort
In your mongoose schema you can set timestamps. it will automatically set createdAt time stamp when you create a object from that schema and if you edit that particular object it set updatedAt timestamp.
As a example schema,
const mongoose = require('mongoose');
const markSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
mark: { type: Number },
student: { type: String },
},{
timestamps: true
});
module.exports = mongoose.model('Mark', markSchema);
like this you can set timestamps.

Mongoose / Express Middleware update reference documents at insert time

Given the below code, and the fact that I'm using mongoose populate() in my API, how can I update the user reference document with the checkin _id, at the same time?
I feel it's like the chicken/problem. Thanks!
router.post('/', (req, res, err) => {
var checkin = new Checkin(req.body);
var user = new User(checkin.user);
checkin.save({
'user': checkin.user,
'checkin_comment': checkin.checkin_comment,
'rating_score': checkin.rating_score
});
});
The Checkin model has the following:
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
.. and the User one has this:
checkins: [{
type: Schema.Types.ObjectId,
ref: 'Checkin'
}],
Regarding your model definition, it seems that attribute checkins on the User model is optional. The user can exists without checkin reference.
If that's the case then create the user first, then the checkin and update the user with its id.
Edit: Given your comments, we assume that the user already exists in DB and its id available in userId.
So something like:
checkin.save()
.then((checkinDoc) => {
return User.findOneAndUpdate(
{_id: userId},
{$push:{checkins: checkinDoc._id}},
{new: true}
);
});

Change discriminator value/discriminated type in Mongoose

I've represented a class hierarchy in Mongoose via two models and a discriminator key (simple example):
var options = {discriminatorKey: 'kind'};
var UserSchema = new mongoose.Schema({
username: {type: String, index: true},
// some other fields
}, options);
// some schema methods
var User = mongoose.model('User', UserSchema);
var PowerUserSchema = new mongoose.Schema({
username: {type: String, index: true},
// some other fields
rank: {type: String}
}, options);
var PowerUser = User.discriminator('PowerUser', PowerUserSchema);
So far this works fine, however I ran into the situation, where I would like to "promote" a User to PowerUser. My initial idea was to set the "kind" property of a user and call save() on the instance, hoping that once the value is retrieved next time, the correct mongoose type will be returned:
var user = ... // retrieve user
user.kind = 'PowerUser';
user.save();
user = ... // retrieve user again
This doesn't appear to work, since the "kind" value is not saved to the instance. I came across this suggestion, which unfortunately did not update the discriminator value either.
My question now is: Am I even on the right track? Is updating the discriminator value even allowed for a situation like this, or should I better structure my data in a different way (e.g. use a single schema for both, with a "type" entry specifying what each instance is; this would have the effect that for the demotion case, no information is lost.)
Additionally, pro(de)moting a user should not break all the instances in my database where (Power)Users are referenced.
Thanks!
In the end I got this to work by doing:
var user = ... // retrieve user
var powerUser = PowerUser.hydrate(user.toObject());
powerUser.kind = 'PowerUser';
powerUser.save();
powerUser = ... // retrieve user again
On a side note, demoting a PowerUser back to User does not appear to be working that way.
have you tried using findOneAndUpdate on the Model
User.findOneAndUpdate({_id: _user._id}, {$set: {kind: "PowerUser"}, {new: true}, function (err, doc) {
should.not.exist(err);
should.exist(doc.kind);
doc.kind.should.equal('PowerUser');
done();
});
you could use a static method like this in case you also need to remove properties already set. the value new: true is to get the new modified file and strict: false so you can unset values that dont already exist on UserSchema
changes = {kind: "PowerUser"}
UserSchema.statics.switchKind = function (id, changes, callBack) {
const unset = {
rank: undefined,
someOtherField: undefined
};
return this.findOneAndUpdate({_id: id}, {$set: changes, $unset: unset}, {new: true, strict: false}, callBack);
};
As of Mongoose 6, you can modify the value of the discriminator key by using the overwriteDiscriminatorKey option:
For some reason it wasn't validating correctly for me with any of the "update" methods (e.g. findByIdAndUpdate) (bug report), so I had to do a replace, which means you need to provide the whole document:
const user = ... // retrieve user
const newUser = {
...user.toObject(), ...{ kind: 'PowerUser', name: 'New Name' }
};
User.replaceOne({ _id: user._id }, newUser, {
overwriteDiscriminatorKey: true,
runValidators: true
});
This will validate it against the PowerUser schema to make sure your document is valid.

Mongoose - inserting subdocuments

I have a user model, and a log model. The log model is a subdocument of user model. So in my user model I have:
var mongoose = require('mongoose');
var Log = require('../models/log');
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
Log
]
});
Then in my 'Log' model I have:
var mongoose = require('mongoose');
var logSchema = new mongoose.Schema({
logComment: {
type: String,
},
});
module.exports = mongoose.model('Log', logSchema);
So upon creation of a 'user', the 'logsHeld' always begins empty. I want to know how to add subdocuments to this user model.
I've tried doing this POST method:
router.post('/createNewLog', function(req, res) {
var user = new User ({
logssHeld: [{
logComment: req.body.logComment
}]
});
user.save(function(err) {
if(err) {
req.flash('error', 'Log was not added due to error');
return res.redirect('/home');
} else {
req.flash('success', 'Log was successfully added!');
return res.redirect('/home');
}
});
});
But this doesn't work. It also includes a 'new User' line, which I don't think I need given this would be for an existing user.
You need to use the logSchema instead of the Log model as your subdocument schema in User model. You can access the schema as follows:
var mongoose = require('mongoose');
/* access the Log schema via its Model.schema property */
var LogSchema = require('../models/log').schema; // <-- access the schema with this
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [LogSchema]
});
Picking up from your comments in another answer where you are facing another issue
WriteError({"code":11000,"index":0,"errmsg":"E11000 duplicate key
error index: testDB.users.$email_1 dup key:
you are getting this because there's already a document in your users collection that has most probably a null value on the email field. Even though your schema does not explicitly specify an email field, you may have an existing old and unused unique index on users.email.
You can confirm this with
testDB.users.getIndexes()
If that is the case and manually remove the unwanted index with
testDB.users.dropIndex(<index_name_as_specified_above>)
and carry on with the POST to see if that has rectified the error, I bet my $0.02 that there is an old unused unique index in your users collection which is the main issue.
Try using logSchema which references only the subdocument schema, Log refers to the entire contents of ../models/log
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true
},
logsHeld: [
logSchema
]
});
Documentation: http://mongoosejs.com/docs/subdocs.html
Try push to insert item in array in mongoose
var user = new User;
user.logssHeld.push({
logComment: req.body.logComment
});
user.save(function(err, doc) {
//DO whatever you want
});
see the docs here

Mongoose findByIdAndUpdate removes not updated properties

I have the following Mongoose model:
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
facebook: {
name: String,
email: String,
customerId: String
}
});
var User = mongoose.model('User', userSchema);
When I update a part of this document using findByIdAndUpdate
User.findByIdAndUpdate(id, {
$set: {
facebook: {
name: name
}
}
});
name gets updated, while email and customerId get removed (unset?).
I didn't find this documented.
Is there a way to update only specific document properties with findByIdAndUpdate?
FindByIdAndUpdate is actually Issues a mongodb findAndModify update command by a documents id.
The point is you are setting an object to overwrite the old object. if you want to update a field you need to modify your update object.
User.findByIdAndUpdate(id, {
$set: {
'facebook.name':name
}
});
This will only update the name field keeping rest of the field of the old object.

Resources