Trouble in inserting sub-documents using mongoose - mongodb

I created an Enterprise database using mongoose in node-express project.Now I need to add employee sub document in the enterprise_employee field of the enterprise database, but it throws an error.
Following code snippet is my schema
var mongoose= require('mongoose');
var Enterprise= new mongoose.Schema({
enterprise_id:{
type:String
},
enterprise_name:{
type:String
},
enterprise_email:{
type:String
},
enterprise_employee: [{employee_id:Number, employee_name:String}]
});
module.exports={
Enterprise:Enterprise
};
This code snippet is the route for adding employee sub-document
var mongoose = require('mongoose');
var Enterprise = mongoose.model('Enterprise_gpy');
var addEmployee = function(req, res){
Enterprise.findOne({"enterprise_id":req.body.enterprise_id},function(err, res){
if(err){
console.log('NO SUCH ORGANISATION');
res.json(err);
} else {
Enterprise.enterprise_employee.push({
"employee_id": req.body.employee_id,
"employee_name":req.body.employee_name
});
}
});
}
module.exports={
addEmployee:addEmployee
};
This the error thrown
throw er; // Unhandled 'error' event
^ TypeError: Cannot read property 'push' of undefined

Seems like what you need is an update operation that uses the $push operator to add the elements to the array field. The following example demonstrates this:
Enterprise.findOneAndUpdate(
{ "enterprise_id": req.body.enterprise_id },
{
"$push": {
"enterprise_employee": {
"employee_id": req.body.employee_id,
"employee_name":req.body.employee_name
}
}
},
{ "new": true }, // return the modified document
function(err, enterprise) {
if (err) {
console.log('NO SUCH ORGANISATION');
res.json(err);
} else {
console.log(enterprise); // modified document
}
}
);

I think this is because your schema needs to define the enterprise_employee as an array. You have to explicitly tell Mongoose that it should be an 'Array' type.
Try this:
enterprise_employee: {
type: Array,
fields: [
{
employee_id: String,
employee_name: String
}
]
}

Related

Change type of value in collection using update

I am looking for a way, how to update multiple documents in MongoDB. I want to modify similar structure like this one:
{[
"_id": 'mongo_id',
"name": "Name"
]}
to the structure like this, basically just change string attribute to object attribute with string property :
{
"_id": 'mongo_id',
"name": {
"type_1": "Name",
"type_2": ""
}
}
Is there a way how to do it in single mongo query or I have to create some kind of worker for example in node.js?
If you do not have any schemas involved to put constrains on your collections or if you have and name is defined as mixed type (from mongoose types as an example) then you can do whatever you want to any of the properties other than _id.
for example this update will change name to the object you want:
db.getCollection('<collectionName>').update({
_id: "mongo_id")
}, {
name: {
"type_1": "Name",
"type_2": ""
}
})
It looks like the best solution is create little worker to get all documents and update them in collection. I used node.js and mongodb npm package to create worker similar to this one:
var mongo = requiere('mongodb');
mongo.connect(process.env.MONGO_URL, function(err, database) {
if (err) {
throw err;
}
db = database;
db.collection('<collectionName>').find({}).toArray(function(err, array) {
if (err) {
console.log(err);
return process.exit(1);
}
console.log('length:', array.length);
var promises = [];
array.forEach(item => {
promises.push(db.collection('<collectionName>').update(
{
_id: item._id
},
{
'$set': {
'name': {
'type_1': item.name,
'type_2': '',
}
}
}))
});
return Promise.all(promises).then(function() {
console.log('Done');
return process.exit(0);
}).catch(function(err) {
console.log('err:', err);
return process.exit(1);
});
});
});

Updated date field is not updated

I have defined this schema
var docSchema = mongoose.Schema({
name:{type:String,required:true},
}, { timestamps: { createdAt: 'createdAt',updatedAt:'updatedAt' }, collection : 'docs', discriminatorKey : '_type' });
I update the documents using this route
router.post('/:id', auth, function(req,res,next) {
var id = req.params.id;
docA.findByIdAndUpdate(id, req.body, {new: true}, function(err, doc) {
if(err)
res.json(err);
else if(doc==null)
res.status(404).send({
message: "Document not found"
});
else
res.json(doc);
});
});
I noticed updatedAt is not updated when I save some edits to the documents.
Besides this problem, thinking about it, it could be helpful to keep this data in form of array of updated date like:
updatedAt : [
"2016-10-25T12:52:44.967Z",
"2016-11-10T12:52:44.967Z",
"2016-12-01T12:52:44.967Z"
]
SOLUTION(?):According to #chridam suggestions, my current workaround to keep an array of update Dates is:
docSchema.pre(`findOneAndUpdate`, function(next) {
if(!this._update.updateHistory) {
console.log("findOneAndUpdate hook: updateHistory not present")
this._update.updateHistory=[];
}
this._update.updateHistory.push(new Date);
return next();
});
docSchema.pre('save', function (next) {
if(!this.updateHistory) {
console.log("Save hook: updateHistory not present")
this.updateHistory=[];
}
this.updateHistory.push(new Date);
next();
});
This is a known issue, please refer to the original thread on the plugin here, where dunnkers commented:
It's actually impossible to hook middleware onto update,
findByIdAndUpdate, findOneAndUpdate, findOneAndRemove and
findByIdAndRemove in Mongoose at the moment.
This means that no plugin is actually run when using any of these
functions.
Check out the notes section in the Mongoose documentation for
middleware. Issue Automattic/mongoose#964 also describes this.
As a suggested workaround, factoring in your schema changes:
var docSchema = mongoose.Schema({
"name": { "type": String, "required": true },
"updateHistory": [Date]
}, {
"timestamps": {
"createdAt": 'createdAt',
"updatedAt": 'updatedAt'
},
"collection" : 'docs',
"discriminatorKey": '_type'
});
router.post('/:id', auth, function(req,res,next) {
var id = req.params.id;
docA.findByIdAndUpdate(id, req.body, {new: true}, function(err, doc) {
if(err)
res.json(err);
else if(doc==null)
res.status(404).send({
message: "Document not found"
});
else {
doc.updateHistory.push(new Date());
doc.save().then(function(doc){
res.json(doc);
}, function(err) {
// want to handle errors here
})
}
});
});
Another approach would be to attach a hook to the schema:
docSchema.pre("findOneAndUpdate", function() {
this.updatedAt = Date.now();
});

Mongoose find by data field from schema into services

If i have a schema like this in my mongoose
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var lovSchema = new mongoose.Schema({
name : { type: String },
values : [{ type: String }]
});
module.exports = mongoose.model('lovs', lovSchema);
Generally in mongoose we find document with reff to _id from collections.
function getOneById(id){
var deferred = Q.defer();
model.findOne({ _id: id })
.exec(function (err, item) {
if(err) {
console.log(err);
deferred.reject(err);
}
else
console.log(item);
deferred.resolve(item);
});
return deferred.promise;
} // gentOneById method ends
But I want to find the document by Name which is a data field in my schema.I tried by modifying _id with name but ended with this error...
{
"message": "Cast to ObjectId failed for value \"regions\" at path \"_id\"",
"name": "CastError",
"type": "ObjectId",
"value": "regions",
"path": "_id"
}
Just use name with a String value:
function getOneByName(name){
var deferred = Q.defer();
model.findOne({ name: name})
.exec(function (err, item) {
if(err) {
console.log(err);
deferred.reject(err);
}
else
console.log(item);
deferred.resolve(item);
});
return deferred.promise;
} // gentOneById method ends
You can test like this:
model.findOne({ name: "Bob"})
And see if you still get an error...

MongoDB not respecting $set { name: "a value" } in update query

I'm writing my own API in express to perform mongo update queries and I'm having trouble updating the "name" field specifically.
TagHandles.update(
{"uuid":req.params.id},
// {$set: { name : "piers" } },
{$set: { type : "works" } },
{upsert:true,safe:false},
function(err, data){
if (err){
console.log("ERROR");
console.log(err);
console.log(data);
} else {
console.log("SUCCESS");
console.log(err);
console.log(data);
}
res.send(err || data);
});
The TagHandles is a mongoose model with the following Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TagHandle = new Schema({
type: String,
uuid: String,
handle: String
}, {
collection: 'tagHandles'
});
var TagHandles = mongoose.model('tagHandles', TagHandle);
Apparently mongoose prevents you from updating any fields not listed as part of the schema. So to correct, I added the line:
name: String
to the mongoose schema.

How to update embedded document in mongoose?

I've looked through the mongoose API, and many questions on SO and on the google group, and still can't figure out updating embedded documents.
I'm trying to update this particular userListings object with the contents of args.
for (var i = 0; i < req.user.userListings.length; i++) {
if (req.user.userListings[i].listingId == req.params.listingId) {
User.update({
_id: req.user._id,
'userListings._id': req.user.userListings[i]._id
}, {
'userListings.isRead': args.isRead,
'userListings.isFavorite': args.isFavorite,
'userListings.isArchived': args.isArchived
}, function(err, user) {
res.send(user);
});
}
}
Here are the schemas:
var userListingSchema = new mongoose.Schema({
listingId: ObjectId,
isRead: {
type: Boolean,
default: true
},
isFavorite: {
type: Boolean,
default: false
},
isArchived: {
type: Boolean,
default: false
}
});
var userSchema = new mongoose.Schema({
userListings: [userListingSchema]
});
This find also doesn't work, which is probably the first issue:
User.find({
'_id': req.user._id,
'userListings._id': req.user.userListings[i]._id
}, function(err, user) {
console.log(err ? err : user);
});
which returns:
{ stack: [Getter/Setter],
arguments: [ 'path', undefined ],
type: 'non_object_property_call',
message: [Getter/Setter] }
That should be the equivalent of this mongo client call:
db.users.find({'userListings._id': ObjectId("4e44850101fde3a3f3000002"), _id: ObjectId("4e4483912bb87f8ef2000212")})
Running:
mongoose v1.8.1
mongoose-auth v0.0.11
node v0.4.10
when you already have the user, you can just do something like this:
var listing = req.user.userListings.id(req.params.listingId);
listing.isRead = args.isRead;
listing.isFavorite = args.isFavorite;
listing.isArchived = args.isArchived;
req.user.save(function (err) {
// ...
});
as found here: http://mongoosejs.com/docs/subdocs.html
Finding a sub-document
Each document has an _id. DocumentArrays have a special id method for looking up a document by its _id.
var doc = parent.children.id(id);
* * warning * *
as #zach pointed out, you have to declare the sub-document's schema before the actual document 's schema to be able to use the id() method.
Is this just a mismatch on variables names?
You have user.userListings[i].listingId in the for loop but user.userListings[i]._id in the find.
Are you looking for listingId or _id?
You have to save the parent object, and markModified the nested document.
That´s the way we do it
exports.update = function(req, res) {
if(req.body._id) { delete req.body._id; }
Profile.findById(req.params.id, function (err, profile) {
if (err) { return handleError(res, err); }
if(!profile) { return res.send(404); }
var updated = _.merge(profile, req.body);
updated.markModified('NestedObj');
updated.save(function (err) {
if (err) { return handleError(res, err); }
return res.json(200, profile);
});
});
};