Mongoose how to update subdocument field? - node.js

This is my schema
var UserSchema = new Schema({
username: String,
email: String,
password: String,
company: String,
contact: Number,
country: String,
isLoggedIn: Boolean,
createdOn: { type: Date, default: Date.now },
ads: [{ type: Schema.Types.ObjectId, ref: 'Ad' }],
notification: {
counter: {type: Number, default: 0},
notidata: [{ itemdate: { type: Date, default: Date.now }, data: {type: String}}]
}
});
var User = module.exports = mongoose.model('User', UserSchema);
I am trying to push data into the notification.notidata.data by the following way and it seems to not be working.
User.findByIdAndUpdate(newuser.id, {
'$set': {
'notification.$.counter': '1',
'notification.$.notidata.data': 'two updated'
}
}, function(err, post) {
if (err) {
console.log(err)
} else {
console.log(post);
}
});
It seems like I am not getting how to access that sub-documented field called data.

Try the $set as:
'$set': {
'notification.counter': '1',
'notification.notidata.0.data': 'two updated'
}

Related

Mongoose query to list all the groups that a user joined

const groupSchema = new Schema({
name: { type: String, default: "", required: true },
participants: [{
type: Schema.Types.ObjectId,
ref: 'User'
}],
});
const userSchema = new Schema({
username: { type: String, default: "", required: true },
name: { type: String, default: "", required: true },
});
I'm trying to fetch all the groups the given user is joined. This is my try
userModel.findOne({ username: data.username }, function(err, user) {
if (user) {
groupModel
.find({"participants":{"$in":[user]}
.populate('participants')
.exec(function(err, result) {
.....
}
}
});
I'm getting an empty list in for the above query
Thank you
Look like you just need to do:
groupModel.find({participants: user._id}).exec(...

E11000 duplicate key error with MongoDB/Mongoose

I have a user model schema, a work model schema, and a critique model schema. The relationship between these schema's is a user can submit many works (like blog posts), and can comment/review (which we call critiques) other people's posts (works).
So when a user submits a critique (think of it like a review), this is my post route. I find the work by the id, then create a new critique model object, and pass that to the .create() mongoose function. All goes seemingly well until I hit the foundWork.critiques.push(createdCritique) line. the console log errors out saying:
BulkWriteError: E11000 duplicate key error collection: zapper.critiques index: username_1 dup key: { : null }
Obviously, it is saying that there are two username keys in the objects and they're conflicting with each other, but I'm not familiar enough with this to find the root of the issue and fix it in the mongoose models. The models are below. If anyone could help, that'd be greatly appreciated.
// post route for getting the review
router.post('/:id', isLoggedIn, function(req, res) {
Work.findById(req.params.id, function(err, foundWork) {
if (err) {
console.log(err);
} else {
// create a new critique
var newCritique = new Critique ({
reviewerName: {
id: req.user._id,
username: req.user.username
},
work: {
id: foundWork._id,
title: foundWork.title
},
critique : req.body.critique,
date: Date.now(),
rating: 0
});
// save new critique to db
Critique.create(newCritique, function(err, createdCritique) {
if (err) {
console.log(err)
} else {
console.log("Created critique is ");
console.log(createdCritique);
// push the new critique into array of critiques of the work
foundWork.critiques.push(createdCritique);
// save to db
foundWork.save();
}
});
}
});
User model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var UserSchema = new mongoose.Schema({
firstname: String,
lastname: String,
username: String,
password: String,
email: String,
zip: String,
bio: {
type: String,
default: ''
},
influences: {
type: String,
default: ''
},
favBooks: {
type: String,
default: ''
},
notWriting: {
type: String,
default: ''
},
favHero: {
type: String,
default: ''
},
favVillain: {
type: String,
default: ''
},
works: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Work'
}
],
critiques: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Critique'
}
],
friends: [
{
friendId: String,
friendName : String,
friendPic: String
}
],
friendRequests: [
{
sendingFriendId: String,
sendingFriendName : String,
sendingFriendPic: String
}
],
createdDate: {
type: Date,
default: Date.now
},
lastLogin: {
type: Date,
default: Date.now
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
Work model:
var mongoose = require('mongoose');
var WorkSchema = new mongoose.Schema({
title: String,
genre: String,
workType: String,
length: Number,
ageRange: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
manuscriptText: String,
critiques: [
{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Critique"
}
}
],
ratingNumber: [Number],
ratingSum: {
type: Number,
default: 0
},
date: {
type: Date,
default: Date.now
},
isPublic: {
type: Boolean,
default: true
}
});
module.exports = mongoose.model("Work", WorkSchema);
Critique model:
var mongoose = require('mongoose');
var passportLocalMongoose = require('passport-local-mongoose');
var CritiqueSchema = new mongoose.Schema({
reviewerName: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
username: String
},
work: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "Work"
},
title: String
},
critique: String,
date: {
type: Date,
default: Date.now
},
rating: [Number]
});
CritiqueSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("Critique", CritiqueSchema);
When you create a unique index in MongoDB, the default behavior is that it will index null values also.
This means if you have a document in your collection with a username of null, you can not add another one with a username of null.
What you need is a sparse index which only indexes actual values (and ignores documents with null for that field).
Check this link It shows how to create a sparse index vs "normal" one in mongoose (index: true, vs spare: true). Most of the time you would want sparse indexes.

How to push objectId to mongodb schema field array of object using findByIdAndUpdate

Everytime I hit api I am getting same error-
I have tried sending value as parameters also but failed. Any help would be appreciated. When i use $set it updates same value everytime the web service is called but it does work but not with $push.
MongoError: The field 'songId' must be an array but is of type objectId in document
{_id: ObjectId('59709590380026118c22dd61')}
My Playlist schema code:-
var PlaylistSchema = new mongoose.Schema({
name: String,
coverphoto: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
songId: [{ type: ObjectId, ref: 'Song' }],
updated_at: { type: Date, default: Date.now },
});
my Song Schema code
var mongoose = require('mongoose');
var Schema = mongoose.Schema,
ObjectId = Schema.ObjectId;
var SongSchema = new mongoose.Schema({
audioName:{type:String,default:''},
title: String,
// coverphoto: { type: String, default: '' },
singer: { type: String, default: '' },
movie: { type: String, default: '' },
album: { type: String, default: '' },
lyricist: { type: String, default: '' },
actors: { type: String, default: '' },
lyrics: { type: String, default: '' },
genre: { type: String, default: 'Random' },
duration: { type: String, default: '' },
size: { type: String, default: '' },
userId: {
type: ObjectId,
ref: 'User'
},
categoryId: {
type: ObjectId,
ref: 'Category'
},
updated_at: { type: Date, default: Date.now },
});
module.exports = mongoose.model('Song', SongSchema);
My Api code
/* post api to add song in playlist */
router.post('/addSongToPlaylist', function (req, res, next) {
Playlist.findOne({ '_id': req.body.playlistId }, function (err, playlist) {
if (err) return next(err);
console.log(playlist)
console.log("req.body.songId", req.body.songId);
if (playlist) {
Playlist.findByIdAndUpdate(
req.body.playlistId,
{ $push: { "songId": req.body.songId } },
{ new: true },
function (err, playlistData) {
if (err) return next(err);
res.json({ message: 'New Song added successfully', playlist: playlistData, success: true });
});
} else if (!Song) {
res.json({ message: 'Failed to add song', success: false });
}
});
});

Populate on basis of condition in mongoose, mongoDB

Here is my code to get one floorplan and populate all flats linked with this
see the code below :
var floorplan = Floorplan.find({
project: req.params.project,
tower: req.params.tower,
isDeleted: false
});
floorplan.populate('flats').exec(function(err, floorplan) {
if (err) { return res.send(err); }
if (!floorplan) { return res.status(401).json(); }
res.status(200).json(floorplan);
});
But I want to populate only those flats where isDeleted : false
How to achive this ??
Schema of floorplan
var FloorplanSchema = new Schema({
project: { type: Schema.ObjectId, ref: "Project" },
flats: [{ type: Schema.ObjectId, ref: "Flat" }],
tower: [{ type: Schema.ObjectId, ref: "Tower" }],
unitType: String,
area: Number,
floorPlan2D: String,
floorPlan3D: String,
livingRoomArea: Number,
kitchenArea: Number,
balconies: Number,
bathRooms: Number,
isDeleted: { type: Boolean, 'default': false },
createdAt: { type: Date, 'default': Date.now }
});
Schema of flat
var FlatSchema = new Schema({
tower: { type: Schema.ObjectId, ref: "Tower" },
floorplan: { type: Schema.ObjectId, ref: "Floorplan" },
project: { type: Schema.ObjectId, ref: "Project" },
status: String,
floor: Number,
size: String,
superbuiltup_area: Number,
directionFacing: String,
furnishingState: String,
flooringType: String,
createdAt: { type: Date, 'default': Date.now },
isDeleted: { type: Boolean, 'default': false },
});
The populate() method has an option which allows for filtering, you can either try this
Floorplan
.find({
project: req.params.project,
tower: req.params.tower,
isDeleted: false
})
.populate({
path: 'flats',
match: { isDeleted: false }
})
.exec(function(err, floorplan) {
if (err) { return res.send(err); }
if (!floorplan) { return res.status(401).json(); }
res.status(200).json(floorplan);
});
or
Floorplan
.find({
project: req.params.project,
tower: req.params.tower,
isDeleted: false
})
.populate('flats', null, { isDeleted: false })
.exec(function(err, floorplan) {
if (err) { return res.send(err); }
if (!floorplan) { return res.status(401).json(); }
res.status(200).json(floorplan);
});

MongooseJS - population path of subdocuments

I'm having real problems with population on the below schemas. It may not be the best design of models (relatively new to MEAN stacks) but I can get it to populate everything except the Spec model.
// Spec Model
var SpecSchema = new Schema({
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: 'true'
},
name: String,
desc: String
});
module.exports = mongoose.model('Spec', SpecSchema);
// Thing model
var specsSchema = new Schema({
time: {
type: Date,
default: Date.now
},
spec: {
type: Schema.Types.ObjectId,
ref: 'Spec'
},
value: String,
});
var ThingSchema = new Schema({
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: true
},
title: String,
specs: [specsSchema]
});
var Thing = mongoose.model('Thing', ThingSchema);
// Set model
var thingsSchema = new Schema({
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: 'true'
},
thing: {
type: Schema.Types.ObjectId,
ref: 'Thing'
}
});
var SetSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
time: {
type: Date,
default: Date.now
},
active: {
type: Boolean,
default: 'true'
},
title: String,
things: [thingsSchema]
});
var Set = mongoose.model('Set', SetSchema);
The standard population is fine but i cant for the life of me get the model.populate to work and from all the examples and solutions I have looked at I'm unclear as to what the path should be.
Set.findById(req.params.id)
.populate('things.thing')
.populate('user', '_id name')
.exec(function (err, set) {
if(err) { return handleError(res, err); }
if(!set) { return res.send(404); }
Thing.populate(set,{
path:'things.thing.specs.spec',
select: 'name',
model: Spec
}, function(err, set){
if ( err ) return res.json(400, err);
});
return res.json(set);
});
any pointers in the right direction would be much appreciated.
path:'things.thing.specs.spec',
select: 'name',
model: Spec
should be
path:'things.thing.specs.spec',
select: 'name',
model: 'Spec'

Resources