Basically I have a mongodb collection called 'people'
whose schema is as follows:
people: {
name: String,
friends: [{firstName: String, lastName: String}]
}
Now, I have a very basic express application that connects to the database and successfully creates 'people' with an empty friends array.
In a secondary place in the application, a form is in place to add friends. The form takes in firstName and lastName and then POSTs with the name field also for reference to the proper people object.
What I'm having a hard time doing is creating a new friend object and then "pushing" it into the friends array.
I know that when I do this via the mongo console I use the update function with $push as my second argument after the lookup criteria, but I can't seem to find the appropriate way to get mongoose to do this.
db.people.update({name: "John"}, {$push: {friends: {firstName: "Harry", lastName: "Potter"}}});
Assuming, var friend = { firstName: 'Harry', lastName: 'Potter' };
There are two options you have:
Update the model in-memory, and save (plain javascript array.push):
person.friends.push(friend);
person.save(done);
or
PersonModel.update(
{ _id: person._id },
{ $push: { friends: friend } },
done
);
I always try and go for the first option when possible, because it'll respect more of the benefits that mongoose gives you (hooks, validation, etc.).
However, if you are doing lots of concurrent writes, you will hit race conditions where you'll end up with nasty version errors to stop you from replacing the entire model each time and losing the previous friend you added. So only go to the latter when it's absolutely necessary.
The $push operator appends a specified value to an array.
{ $push: { <field1>: <value1>, ... } }
$push adds the array field with the value as its element.
Above answer fulfils all the requirements, but I got it working by doing the following
var objFriends = { fname:"fname",lname:"lname",surname:"surname" };
People.findOneAndUpdate(
{ _id: req.body.id },
{ $push: { friends: objFriends } },
function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
});
)
Another way to push items into array using Mongoose is- $addToSet, if you want only unique items to be pushed into array. $push operator simply adds the object to array whether or not the object is already present, while $addToSet does that only if the object is not present in the array so as not to incorporate duplicacy.
PersonModel.update(
{ _id: person._id },
{ $addToSet: { friends: friend } }
);
This will look for the object you are adding to array. If found, does nothing. If not, adds it to the array.
References:
$addToSet
MongooseArray.prototype.addToSet()
Use $push to update document and insert new value inside an array.
find:
db.getCollection('noti').find({})
result for find:
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
update:
db.getCollection('noti').findOneAndUpdate(
{ _id: ObjectId("5bc061f05a4c0511a9252e88") },
{ $push: {
graph: {
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
}
})
result for update:
{
"_id" : ObjectId("5bc061f05a4c0511a9252e88"),
"count" : 1.0,
"color" : "green",
"icon" : "circle",
"graph" : [
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 2.0
},
{
"date" : ISODate("2018-10-24T08:55:13.331Z"),
"count" : 3.0
}
],
"name" : "online visitor",
"read" : false,
"date" : ISODate("2018-10-12T08:57:20.853Z"),
"__v" : 0.0
}
First I tried this code
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
}
);
But I noticed that only first friend (i.e. Johhny Johnson) gets saved and the objective to push array element in existing array of "friends" doesn't seem to work as when I run the code , in database in only shows "First friend" and "friends" array has only one element !
So the simple solution is written below
const peopleSchema = new mongoose.Schema({
name: String,
friends: [
{
firstName: String,
lastName: String,
},
],
});
const People = mongoose.model("person", peopleSchema);
const first = new Note({
name: "Yash Salvi",
notes: [
{
firstName: "Johnny",
lastName: "Johnson",
},
],
});
first.save();
const friendNew = {
firstName: "Alice",
lastName: "Parker",
};
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
{ upsert: true }
);
Adding "{ upsert: true }" solved problem in my case and once code is saved and I run it , I see that "friends" array now has 2 elements !
The upsert = true option creates the object if it doesn't exist. default is set to false.
if it doesn't work use below snippet
People.findOneAndUpdate(
{ name: "Yash Salvi" },
{ $push: { friends: friendNew } },
).exec();
An easy way to do that is to use the following:
var John = people.findOne({name: "John"});
John.friends.push({firstName: "Harry", lastName: "Potter"});
John.save();
In my case, I did this
const eventId = event.id;
User.findByIdAndUpdate(id, { $push: { createdEvents: eventId } }).exec();
Push to nested field - use a dot notation
For anyone wondering how to push to a nested field when you have for example this Schema.
const UserModel = new mongoose.schema({
friends: {
bestFriends: [{ firstName: String, lastName: String }],
otherFriends: [{ firstName: String, lastName: String }]
}
});
You just use a dot notation, like this:
const updatedUser = await UserModel.update({_id: args._id}, {
$push: {
"friends.bestFriends": {firstName: "Ima", lastName: "Weiner"}
}
});
This is how you could push an item - official docs
const schema = Schema({ nums: [Number] });
const Model = mongoose.model('Test', schema);
const doc = await Model.create({ nums: [3, 4] });
doc.nums.push(5); // Add 5 to the end of the array
await doc.save();
// You can also pass an object with `$each` as the
// first parameter to use MongoDB's `$position`
doc.nums.push({
$each: [1, 2],
$position: 0
});
doc.nums;
// This is the my solution for this question.
// I want to add new object in worKingHours(array of objects) -->
workingHours: [
{
workingDate: Date,
entryTime: Date,
exitTime: Date,
},
],
// employeeRoutes.js
const express = require("express");
const router = express.Router();
const EmployeeController = require("../controllers/employeeController");
router
.route("/:id")
.put(EmployeeController.updateWorkingDay)
// employeeModel.js
const mongoose = require("mongoose");
const validator = require("validator");
const employeeSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, "Please enter your name"],
},
address: {
type: String,
required: [true, "Please enter your name"],
},
email: {
type: String,
unique: true,
lowercase: true,
required: [true, "Please enter your name"],
validate: [validator.isEmail, "Please provide a valid email"],
},
phone: {
type: String,
required: [true, "Please enter your name"],
},
joiningDate: {
type: Date,
required: [true, "Please Enter your joining date"],
},
workingHours: [
{
workingDate: Date,
entryTime: Date,
exitTime: Date,
},
],
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
}
);
const Employee = mongoose.model("Employee", employeeSchema);
module.exports = Employee;
// employeeContoller.js
/////////////////////////// SOLUTION IS BELOW ///////////////////////////////
// This is for adding another day, entry and exit time
exports.updateWorkingDay = async (req, res) => {
const doc = await Employee.findByIdAndUpdate(req.params.id, {
$push: {
workingHours: req.body,
},
});
res.status(200).json({
status: "true",
data: { doc },
});
};
https://www.youtube.com/watch?v=gtUPPO8Re98
I ran into this issue as well. My fix was to create a child schema. See below for an example for your models.
---- Person model
const mongoose = require('mongoose');
const SingleFriend = require('./SingleFriend');
const Schema = mongoose.Schema;
const productSchema = new Schema({
friends : [SingleFriend.schema]
});
module.exports = mongoose.model('Person', personSchema);
***Important: SingleFriend.schema -> make sure to use lowercase for schema
--- Child schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const SingleFriendSchema = new Schema({
Name: String
});
module.exports = mongoose.model('SingleFriend', SingleFriendSchema);
I am trying to get a list of todos based on the 'userId' stored against the todo. Using the following code returns no rows.
DB.TodoTable.find({ "userId" : ObjectId("54c12f5f3620c07019e6b144") }, function(err, todos) {
if (err) return console.error(err);
console.dir(todos);
})
the mongoose debug gives:
Mongoose: todos.find({ userId: undefined }) { fields: undefined }
[]
But whenn i try the same in mongo shell, it works. As follows:
> db.todos.find()
{ "_id" : ObjectId("54c12f643620c07019e6b145"), "created_at" : ISODate("2015-01-22T17:12:04.932Z"), "userId" : ObjectId(
"54c12f5f3620c07019e6b144"), "text" : "data", "__v" : 0 }
My database schema is as follows:
var TodoTable = mongoose.model('todos', {
text : {type : String, default: ''},
created_at : Date,
userId : {type:ObjectId, ref:'users'}
});
var UserTable = mongoose.model('users', {
username: String,
password: String,
email: String,
gender: String,
address: String
});
Confusingly, mongoose.Schema.ObjectId is not the ObjectID constructor function, it's only meant to be used in defining schemas.
But you don't need to create an ObjectID from that string anyway because Mongoose will do that for you based on the schema definition, so you can simply use:
DB.TodoTable.find({ "userId": "54c12f5f3620c07019e6b144" }, function(err, todos) {...})
Or if you are in a situation where you need to be explicit, you can access the ObjectID constructor as:
DB.TodoTable.find({ "userId": mongoose.mongo.ObjectID("54c12f5f3620c07019e6b144") },
function(err, todos) {...})
I was trying out mongoose schema in nodejs and ended up in the following error.I have defined two mongoose schema as follows
var markSchema = mongoose.Schema({
examName: String,
scores : mongoose.Schema.Types.Mixed
});
var studentSchema = mongoose.Schema({
studentID: {type : String, unique : true} ,
marks: [{type : mongoose.Types.ObjectId, ref : 'marks'}]
});
mongoose.model('marks',markSchema);
mongoose.model('student',studentSchema);
And i am using it inside my router as
var studentBody = {
"studentID": "ST12",
"marks": []
};
var markz = {
"examName": "Series 1",
"scores": {
"maths": {
"score": 48,
"total": 50,
"teacher": "xxxx"
}
}
};
var marks;
marks = new Marks(markz);
marks.save(function(err,mark){
if(err){
console.log("Some problem occured while writing the values to the database "+err);
}
studentBody.marks.push(mark._id);
var student = new Student(studentBody);
console.log(JSON.stringify(studentBody,null,4)); // This prints as expected with ObjectId
student.save(function(err,student){ // CastError happens here
if(err){
console.log("Problem occured while writing the values to the database"+err);
}
else{
var formatted = "Saved to database :: "+JSON.stringify(student,null,4);
console.log(formatted);
}
});
});
But i am getting the CastError and the error trace is
CastError: Cast to undefined_method failed for value "547e8cddd90f60a210643ddb" at path "marks"
It is printing as expected with the Objectid in the array when i am logging it,But it giving the above castError while trying to save the data to mongoDB.
Could anyone please help me to figure it out ? Thanks
this error is because you write mongoose.Types.ObjectId instead of mongoose.Schema.Types.ObjectId
var studentSchema = mongoose.Schema({
studentID: {type : String, unique : true} ,
marks: [{type : mongoose.Schema.Types.ObjectId, ref : 'marks'}]//change
});
this works fine
I am trying to create an index on two fields of a schema that are to be unique and sparse in MongoDB using Mongoose as follows:
var ArraySchema = new Schema ({
user_id: {type: mongoose.Schema.Types.ObjectId, ref:'User'},
event_id: {type: mongoose.Schema.Types.ObjectId, ref:'Event'}
}, {_id:false});
ListSchema.index({user_id:1, event_id:1}, {sparse:true, unique:true});
Which is then used in an array in the User schema as such:
var User = new Schema({
arrayType1 : {
type: [ArraySchema]
},
arrayType2 : {
type: [ArraySchema]
},
arrayType3 : {
type: [ArraySchema]
}
//More specifications for user schema...
});
However, when trying to save multiple users without the array field, errors are thrown for duplicate fields. The error in Mocha looks similar to this: array.event_id_1 dup key {null, null}. An example of a segment of code that would throw this error is as follows:
var user1, user2;
user1 = new User({
username : 'username1',
password : 'password'
});
user2 = new User({
username : 'username2',
password : 'password'
});
user1.save(function() {
user2.save();
});
Here is my reasoning behind making the the fields of ArraySchema unique and sparse: If the array field is specifed, I do not want the array to contain duplicate objects; however, the array field is not required, so there will be many Users that have null for this field. Obviously I cannot use field-level indices since there are multiple fields that would need an index (arrayType1, arrayType2, arrayType3).
It appears that doing this sort of thing is not supported, at least at this time. The alternative would be to create a compound index on these fields then whenever adding a new element to the field use user.arrayType1.addToSet(). Here is an example of how this would work:
ArraySchema:
var ArraySchema = new Schema ({
user_id: {type: mongoose.Schema.Types.ObjectId, ref:'User'},
event_id: {type: mongoose.Schema.Types.ObjectId, ref:'Event'}
}, {_id:false});
ListSchema.index({user_id:1, event_id:1});
User schema:
var User = new Schema({
arrayType1 : {
type: [ArraySchema]
},
arrayType2 : {
type: [ArraySchema]
},
arrayType3 : {
type: [ArraySchema]
}
//More specifications for user schema...
});
Then I could declare new users as usual (as I did in the question); however, when I want to add a new element to arrayType1, for example, I would use the following line of code to add to new element only if it is not already there:
user.arrayType1.addToSet({user_id : user2._id, event_id : event._id});
user.save(function(err, result) {
//Callback logic.
};
Where user2 and event are defined earlier in the code and saved to the db. Alternatively I could use Mongoose's update function as such:
User.update({_id : user._id}, {
$addToSet : {
arrayType1 : {
user_id : user2._id,
event_id : event._id
}
}
}, function(err) {
//Callback logic.
}
);
//MONGOOSE SCHEMA OBJECT
var userSchema = new Schema( {
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
tags:[ {name : String, color : String } ],
bookmarks:[{link : String, tags:[ {name : String, color : String } ]}]
} );
module.exports = userSchema; //Export the userSchema
var UserModel = mongoose.model('UserModel', userSchema ); //Create Model
module.exports = UserModel; //Export the Model
//I CAN DELETE AN ITEM FROM BOOKMARKS ARRAY NO PROBLEM USING
UserModel.findByIdAndUpdate(userId ,{$push : {bookmarks: {link : req.body.link, tags : req.body.tags}}}, function(err, user_data) {
PROBLEM!!
How do I delete a tag from the tags Array, within the bookmarks array given users _id, bookmarks _id and the tag _id or name?
//I have tried variations of the following without success
var update = {bookmarks:[{ _id : bookmarkId},
$pull: {tags:[_id : tagid ] }] };
UserModel.findByIdAndUpdate(userId ,update, function(err, user_data) {
AND
UserModel.findOne( {_id : userId}).select({ bookmarks:[ { $elemMatch: {_id : req.params.bookmarkId}}] }).exec(function(err, user_data)
Initially I was using different Models and subdocuments.
var bookmarkSchema = new Schema( {
link : String,
tags:[tagSchema]
});
var tagSchema = new Schema( {
name : String,
color : String
});
var userSchema = new Schema( {
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
tags:[ {name : String, color : String } ],
bookmarks: [bookmarkSchema]
} );
However, I was unable to delete items from the subdocuments using the $pull command like I used above. So I reverted to just using one schema/model.
This is a very important task to be able to complete and I would be greatful for help.
Thanks Muhammad but I could not get either of the 2 methods to work:
1) Nothing happens to DB and the values of the callbacks are:
*numberAffected: 0
*raw: {"updatedExisting":false,"n":0,"connectionId":322,"err":null,"ok":1}
UserModel.update({bookmarks:req.params.bookmarkId},
{ $pull: {"bookmarks.tags" :{_id:req.body._id, name :req.body.name ,color:req.body.color}}}, function(err, numberAffected, raw) {
2) I had to use the lean() function to convert Mongoose Document data format to normal JSON. Otherwise bookmarks.push({link:user.bookmarks[i].link,_id:user.bookmarks[i]._id,tags:tags})
would not combine properly with:
bookmarks.push(user.bookmarks[i]);
on bookmarks[]
However using the lean() functions means I would not be able to save the data to the DB with .save
UserModel.findById(userId).lean(true).exec(function(err, user) {
delete from sub-Document of JSON doc,
UserModel.update({bookmarks:bookmarkId},{$pull: {"bookmarks.tags" :{_id:tagsId,name :name ,color:color}}}, function(err, user_data) {
or
UserModel.findOnd(userId,function(er,user){
var bookmarks= [];
var tags = [];
for (var i = 0;i<user.bookmarks.length;i++){
if (user.bookmarks[i]._id==bookmarkId)
{
for (var j = 0;j<user.bookmarks[i].tags.length;j++){
if (user.bookmarks[i].tags[j]._id==tagid )
{
}else {
tags.push(user.bookmarks[i].tags[j])
}
}
bookmarks.push({link:user.bookmarks[i].link,_id:user.bookmarks[i]._id,tags:tags})
}
else {
bookmarks.push(user.bookmarks[i]);
}
}
})