Mongoose won't update sub-document - node.js

I've been banging my head against this for a few hours now and just can't seem to figure it out. I decided to use Mongo on a learning project to see how I like it. Mongoose came as the recommended way to use it, so I dove into that too. I have two schemas:
var PluginSchema = new Schema({
name: { type: String, required: true },
slug: { type: String },
description: { type: String },
created_date: { type: Date, default: Date.now },
active: { type: Boolean, default: true },
user: { type: Schema.Types.ObjectId, ref: 'User' },
versions: [PluginVersion.schema]
});
PluginSchema.methods.getLatestVersion = function(callback) {
if(this.versions.length > 0) {
this.versions.sort(function(a, b) {
if(semver.gt(a.version, b.version)) {
return 1;
} else if(semver.lt(a.version, b.version)) {
return -1;
} else {
return 0;
}
});
callback(this.versions[this.versions.length-1]);
} else {
callback(undefined);
}
};
and
var PluginVersionSchema = new Schema({
version: { type: String, required: true },
downloads: { type: Number, default: 0 },
size: { type: Number, required: true },
updatedChecks: { type: Number, default: 0 },
fileName: { type: String, required: true },
uploadedDate: { type: Date, default: Date.now }
});
The issue here is the 'versions' relationship. At some point I want to update a version of the Plugin. The thing I want to update is the updatedChecks field. Basically just updatedChecks += 1.
Plugin.findById(req.params.plugin_id)
.populate('user')
.populate('versions')
.exec(function(err, plugin) {
if(err) {
res.status(404);
res.send("Plugin not found.");
} else {
plugin.getLatestVersion(function(version) {
if(version !== undefined) {
pluginData = {
stuff: "that",
gets: "returned",
tothe: "user"
};
// This says 'Affected: 1'. As expected
PluginVersion.update({_id: version._id}, {
updatedChecks: version.updatedChecks + 1
}, function(err, affected) {
console.log("Affected: " + affected);
if(err) { res.send(err); }
res.status(200);
res.json(pluginData);
});
} else {
res.status(404);
res.send("No versions found for this plugin.");
}
});
}
});
So far, so good. However, when I try to access that version again via the Plugin schema, the updatedChecks value hasn't changed! I checked the _id value on the version I'm updating versus the version that gets pulled from the Plugin.versions field and they are they same. Do I need to remove the version from Plugin.versions and re-insert a new one with the updated value? I also tried just updating the value and calling save() but that didn't seem to work either.

I ended up getting this work by accessing the plugin version directly from the Plugin object.
var pluginIndex = false;
for(var i = 0; i < plugin.versions.length; i++) {
if(plugin.versions[i]._id === version._id) {
pluginIndex = i;
}
}
if(pluginIndex !== false) {
plugin.versions[pluginIndex].updatedChecks++;
plugin.versions[pluginIndex].markModified('updatedChecks');
plugin.versions[pluginIndex].save(function() {
plugin.markModified('versions');
plugin.save(function() {
res.status(200);
res.json(pluginData);
});
});
}
I also has a little help from: Mongoose save() not updating value in an array in database document

The problem here is, your updating query is written wrong. It must be
PluginVersion.update({
_id: version._id
}, {
$set: {
updatedChecks: version.updatedChecks + 1
}
}, function(...) {
...
});
or
PluginVersion.update({
_id: version._id
}, {
$inc: {
updatedChecks: 1
}
}, function(...) {
...
});
You can find more update operators here:
http://docs.mongodb.org/manual/reference/operator/update/

Related

In Mogoose i found my document with find, but using update not

I'm using nodejs and mongo (with mongooose) to build a simple aplication, but i have this little problem.
If i search my documento using a find method, i found the document (and yes, i can update in this), but if i use a update method, o cant find it. Why?
My controller method
var query = {
_id: ObjectId(req.params.runner_id)
};
Runner.find(query, function(e,o) {
console.log(o); //here i found
})
Runner.update(query, req.body, function (err, qtd) {
console.log(qtd); //here note
if (err) {
...
} else {
...
}
})
My Schema
module.exports = mongoose.model("Runner", new Schema({
cellphone: {
type: String,
require: "qual o telefone?",
unique: true,
validate: {
validator: function (v) {
var re = /^\d{11}$/;
return (v == null || v.trim().length < 1) || re.test(v);
},
message: "telefone inválido"
}
},
created_at: {
type: Date,
default: Date.now
},
advisors: [{
name: {
type: String,
require: "entre com o nome"
},
email: {
type: String,
require: "entre com o e-mail"
},
advisor: {
type: ObjectId,
require: "qual a assessoria?"
}
}]
}));
My output
with update -> { ok: 0, n: 0, nModified: 0 }
with find -> [ { _id: 5a0b99a9328fec0e4111ca52,
cellphone: '85999981114',
__v: 0,
advisors: [ [Object] ],
created_at: Wed Nov 15 2017 01:34:33 GMT+0000 (UTC) } ]
app.get('/', function(req, res){
var query = {
_id: ObjectId(req.params.id)
};
Runner.find(query, function(e,o) {
console.log(o); //here i found
})
}
app.put('/:id', function(req, res){
var Id=req.params.id;
Runner.findById(Id, function (err, qtd) {
console.log(qtd); //here qtd will found
if (err) {
...
} else {
...
}
}

node mongoose : aggregate not giving expected result

withe the following Group Schema,
group.model.js
const Role = new mongoose.Schema({
name: { type: String, required: true }, // ensure uniqueness withn group instance using addToSet
description: { type: String, required: false }
});
const GroupSchema = new mongoose.Schema({
name: { type: String, index: { unique: true, required: true, dropDups: true } },
description: { type: String, required: false },
roles: [Role],
createdAt: {
type: Date,
default: Date.now
}
});
I am trying to list all roles ( subdocument) got a specific group
group.controller.js
function listRoles(req, res) {
const group = req.group;
console.log('GROUP: %j', group);
const limit = parseInt(req.query.limit, 10) || 50;
const skip = parseInt(req.query.skip, 10) || 0;
Group.aggregate([
{ $match: { _id: req.params.groupId } },
{ $unwind: '$roles' },
{ $skip: skip },
{ $limit: limit }
], (err, result) => {
if (err) {
res.status(500);
res.json({ message: 'Error. Cannot list roles', errror: err });
}
res.status(200);
console.log('RESULT: %j', result);
res.json(result);
});
}
I should get an array with one role, but I get an empty array
what's wrong with my aggregate code ? thanks for feedback
note: I tried to aggregate only with the $match in the pipe and I also get an empty array... so I guess. the issue comes from the req.params.groupId should be an ObjectId .. how can I cast it ?
console.log
GROUP: {"_id":"5923e2e83afd4149bdf16c61","name":"Admin","description":"Administration group","__v":1,"createdAt":"2017-05-23T07:21:12.470Z","roles":[{"name":"Role1","description":"description role1","_id":"5923e2e83afd4149bdf16c62"}]}
RESULT: []
To better diagnose this, I'd recommend removing steps from your aggregation pipeline and seeing what the result is. However, I suspect your problem is because you have no match at the first stage because you're comparing a string to an ObjectId. Try this:
const mongoose = require('mongoose')
// and in the aggregation:
{ $match: { _id: mongoose.Types.ObjectId(req.params.groupId) } }

Failed to update single property by mongoose

Frist I have read and try the solution in the post of mongoose-and-partial-select-update.
However when I try to use the traditional style, query would work.
My schema:
var userSchema = mongoose.Schema({
local: {
email: {
type: String,
index: {
unique: true,
dropDups: true
}
},
password: String,
displayName: String,
avatar: {
type: String,
default: "./img/user.png"
},
role: {
type: String,
default: "student"
},
ask_history: [
{
question_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'questionAnswer'
},
favorite: Boolean,
ask_time: Date
}
],
interest: [String]
}
})
Working Update function:
User.findById(id, function(err, User) {
if (err) {
throw done(err);
}
if (!User) {
return;
}
User.local.role = "admin";
User.save(function(err, updatedUser) {
if (err) {
throw err
} else {
//good
}
})
});
However if I do this:
User.update({_id : id},
{$set{
local:{role:"admin"}
}
},function(...){...}
});
Code above will overwrite user into:
{
_id : "...",
local: {
role : "admin"
}
}
I read that $ will make the update only changing property, where I did wrong?
The positional operator $ works with array of subdocuments.
In your case you have a single sub-document, so the following should work:
User.update({_id : id},
{ $set
{
"local.role": "admin"
}
}, function(...){...}
});

mongoose findByIdAndUpdate instead of save

I'm trying to update the value of an element inside an array of my document.
The common way of update via save works as expected, but trying to use an static method, like findByIdAndUpdate doesn't work as expected.
Here bellow I paste the code I'm currently using:
var UserSchema = new mongoose.Schema({
nickname: { type: String, trim: true},
username: { type: String, trim: true },
notifications: [{
a: {
_id: { type: mongoose.Schema.Types.ObjectId, ref: 'x' },
x: { type: mongoose.Schema.Types.ObjectId, ref: 'y' }
},
b: {
_id: { type: mongoose.Schema.Types.ObjectId, ref: 'y' },
x: { type: mongoose.Schema.Types.ObjectId, ref: 'y' }
},
read: { type: Number, default: 0 }, // 0 - Unread, 1 - read
ts: { type: Date, default: Date.now }
}]
}, { timestamps: { createdAt: 'created_at' } });
// This works as expected
UserSchema.statics.rNSave = function (user_id, notification_id) {
var vm = this;
return new Promise(function (resolve, reject) {
vm.findById(user_id, function (err, data) {
if (err) {
reject(new Error(err));
} else {
var notifications = data.notifications, i = 0;
for (i; i < notifications.length; i += 1) {
if (data.notifications[i]._id.toString() === notification_id) {
data.notifications[i].read = 1;
data.save({ validateBeforeSave: false }, function (err, updatedData) {
if (err) {
reject(new Error(err));
} else {
resolve();
}
});
return;
}
}
return reject('Error');
}
});
});
};
// This one is not working
UserSchema.statics.rNStatic = function (user_id, notification_id) {
return this.findByIdAndUpdate({ _id: user_id, notifications: { $elemMatch: { _id: notification_id }}}, { $set: { 'notifications.$.read': 1 }}).exec();
};
Any help with this?
Thanks in advice.

Auto increase in mongoose

I have a Model : OrderEventSchema
var OrderEventSchema = new Schema(
{
orderId: { type: String, required: true },
sequence: { type: Number },
workflowId: { type: String, required: true },
type: { type: String },
transitionId: { type: String },
placeId: { type: String },
eventType: { type: String },
payLoad: { type: Schema.Types.Mixed }
}, options
);
Handle Post save :
OrderEventSchema.post('save', function(doc) {
getNextSequence(doc.orderId)
.then(function(nextSequence) {
doc.sequence = nextSequence;
doc.save();
})
})
function getNextSequence (orderId) {
return new Promise(function(resolve, reject) {
orderEvent.findOne({
orderId : orderId
}).sort({'sequence' : -1}).limit(1).exec(function(err, doc) {
if (err) {
reject(err);
} else {
resolve(doc.sequence ? doc.sequence + 1 : 1);
}
});
})
}
Now, everything I save, it will get a infinite loop, can anyone help me, how can I increase the sequence without using another module, how can I improve this code.
Thank you so much.

Resources