Mongoose delete nested subdocuments and documents - mongodb

I have:
let userSchema = mongoose.Schema({
email: {type: String, required: true, unique: true},
passwordHash: {type: String, required: true},
fullName: {type: String, required: true},
salt: {type: String, required: true},
ads: [{type: ObjectId, ref: 'Ad'}],
roles: [{type: String}]
}
let adSchema = mongoose.Schema({
author: {type: ObjectId, ref: 'User'},
title: {type: String, required: true},
category: {type: ObjectId, ref: 'Category', required: true},
town: {type: ObjectId, ref: 'Town', required: true},
}
);
let categorySchema = mongoose.Schema({
name: {type: String, required: true, unique: true},
ads: [{type: ObjectId, ref: 'Ad'}]
}
);
let townSchema = mongoose.Schema({
name: {type: String, required: true, unique: true},
ads: [{type: ObjectId, ref: 'Ad'}]
}
);
I want to find for example town by id and remove all ads in it(and ofcourse to remove the ads from their categories and authors).How can i do that?

I would suggest bulk getting the array of object Ids and using it like this:
Ad.remove({_id: {$in: Ad_ids_array}}, function(){...}); // and so on
You can add a pre-remove hook in the ad schema definition like this:
adSchema.pre('remove', function(next) {
let lethis = this;
// Pull ad out of all the Category docs that reference the removed ad.
this.model('Category').update({}, { $pull: {ads: lethis._id}}, { safe: true }, next);
// Pull ad out of all the User docs that reference the removed ad.
this.model('User').update({}, { $pull: {ads: lethis._id}}, { safe: true }, next);
});
This will remove the ad from the categories and users that have it in their ads array.

Related

Mongoose Populate ignored for this basic setup

I have a User Schema, which has multiple notes, and the Note which belongs to a userId
const UserSchema = new Schema({
_id: Schema.Types.ObjectId,
email: {type: String, required: true, trim: true, lowercase: true, unique: true},
notes: [{type: Schema.Types.ObjectId, ref: 'Note'}]
});
const NoteSchema = new Schema({
userId: {type: mongoose.Types.ObjectId, ref: 'User'},
content: {type: String, required: true, trim: true, lowercase: true},
});
I'm trying to populate my User with the notes using the following syntax (from the docs)
const user = await User.findById(mongoose.Types.ObjectId("5bd2a8c4963ac00f57a18074"))
.populate('notes')
.exec(function (err, result) {
console.log(result);
});
But it's returning the User without the Notes data. Any idea what I might be doing wrong?
NoteSchema here is the problem:
userId: {type: mongoose.Types.ObjectId, ref: 'User'}
Use below,
userId: {type: mongoose.Schema.Types.ObjectId, ref: 'User'}
// OR
userId: {type: Schema.Types.ObjectId, ref: 'User'}
// OR
userId: {type: Schema.ObjectId, ref: 'User'} // For backword compatibility
Note:- The schema should always use mongoose.Schema.Types. And mongoose.Types.ObjectId can be used withing mongoose implementation.
I am able to get document properly (Below code):
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
const NoteSchema = new Schema({
userId: {type: Schema.Types.ObjectId, ref: 'UserTest'},
content: {type: String, required: true, trim: true, lowercase: true},
});
const UserSchema = new Schema({
_id: Schema.Types.ObjectId,
email: {type: String, required: true, trim: true, lowercase: true, unique: true},
notes: [{type: Schema.Types.ObjectId, ref: 'NoteTest'}]
});
var Note = mongoose.model('NoteTest', NoteSchema);
var User = mongoose.model('UserTest', UserSchema);
User.find({_id : mongoose.Types.ObjectId("5bd2c84dd79cc5d8b1c62964")})
.populate('notes')
.exec(function (err, result) {
console.log("result.....", JSON.stringify(result));
});
Output:
[
{
"_id": "5bd2c84dd79cc5d8b1c62964",
"email": "hardik#com.com",
"notes": [
{
"_id": "5bd2c869d79cc5d8b1c62965",
"content": "ABC",
"userId": "5bd2c84dd79cc5d8b1c62964"
},
{
"_id": "5bd2c88ad79cc5d8b1c62966",
"content": "DEF",
"userId": "5bd2c84dd79cc5d8b1c62964"
}
]
}
]

MongoDB: How to find the relationships between collections in database

I have a collection of user which has id, firstName, lastName. id field of user collection has been used in another collection.
Is there any way to find all collection which used user id?
user schema:
let userSchema = new mongoose.Schema({
firstName: {
type: String,
trim: true,
required: true
},
lastName: {
type: String,
trim: true,
required: true
}
},
{
timestamps: true,
usePushEach: true
});
training schema:
var trainingSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
name: {type: String, required: true},
institution: {
instituteName: {type: String, required: true},
type: {type: String, required: true},
address: {
country: String,
state: String,
city: String
}
},
startDate: Date,
endDate: Date,
achievements: String,
createdAt: Date,
updatedAt: Date,
endorsers: [{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
profilePic: {
container: {type: String,default: null},
name: { type: String, default: null }
},
currentPosition: {type: String,default: ""},
currentWorkingCompany: {type: String,default: ""}
}],
});
In above schema userId come from user collection
Note: similar to this which is available in MYSQL:
SELECT
ke.referenced_table_name 'parent table',
ke.referenced_column_name 'parent column',
ke.table_name 'child table',
ke.column_name 'child column',
ke.constraint_name
FROM
information_schema.KEY_COLUMN_USAGE ke
WHERE
ke.referenced_table_name IS NOT NULL
AND table_schema = 'your_db_name'
ORDER BY ke.referenced_table_name;
Source: here
You probably need MySQL function named join. In MongoDB it is named $lookup and it is part of aggregate function.

Update field in another schema with post hook mongoose

I have that schemas:
var schema = new Schema({
number: {type: Number},
subtotal: {type: Float, required: true},
quantity: {type: Number, required: true},
prodprov: {type: Schema.Types.ObjectId, ref: 'ProdProv'},
order: {type: Schema.Types.ObjectId, ref: 'Order'}
});
module.exports = mongoose.model('OrderDetail', schema);
and
var schema = new Schema({
name: {type: String, required: true},
price: {type: Float, required: true},
idProvider: {type: Schema.Types.ObjectId, ref: 'Provider'},
idProduct: {type: Schema.Types.ObjectId, ref: 'Product'},
description: {type: String},
quantitySold: {type: Number}
});
module.exports = mongoose.model('ProdProv', schema);
I need to update field "quantitySold" when a post.save happens in order-detail, i've tried this:
schema.post('save', function(next){
//But this point I can't find the prodprov to update field
})
How I can do this?
Thank you.
Thank you #mjim, but that´s code not works. I've modify the code and works! this is my solution:
schema.post('save', function(doc){
var prod = ProdProv.findById(this.prodprov).exec().then((pp) => {
pp.quantitySold += this.quantity;
pp.save();
});
});
I would suggest:
var ProdProv = require('./ProdProv');
var schema = new Schema({
number: {type: Number},
subtotal: {type: Float, required: true},
quantity: {type: Number, required: true},
prodprov: {type: Schema.Types.ObjectId, ref: 'ProdProv'},
order: {type: Schema.Types.ObjectId, ref: 'Order'}
});
schema.post('save', async function(doc){
var prod = await ProdProv.findById(doc.prodprov);
prod.quantitySold = doc.quantity;
prod.save()
});
module.exports = mongoose.model('OrderDetail', schema);

How findOne in mongoose

How find comment inside commentUnders?
comments: [{
nickName: {type: String, required: true},
comment: {type: String, required: true},
commentUnders: [{
nickName: {type: String, required: true},
comment: {type: String, required: true},
}]
}]
I tried this but it did not work.
Post.findOne({'comments._id' : req.body._idComment},
{'comments._id': $elemMatch: req.body._idComment,
'commentUnders._id': $elemMatch: req.body._idCommentUnders}, function(err, data) {
console.log(data);
});

Modeling with MongoDB and Mongoose

I have a simple app which lists tour guides and reviews left by users about each guide. Any user can give be a guide and can give reviews. My scheme for a user is:
var User = new mongoose.Schema({
firstName: { type: String, required: true, unique: true },
lastName: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true}
isGuide: Boolean,
rating: {type: Number, "default": 0, min: 0, max: 5},
reviews: [reviewSchema]
});
var Review = new mongoose.Schema({
userId: {type: Schema.ObjectId, ref: 'User'},
author: {type: String, required: true},//contatenation of firstName and lastName of user that wrote review
rating: {type: Number, required: true, min: 0, max: 5},
reviewText: {type: String, required: true},
createdOn: {type: Date, "default": Date.now}
});
mongoose.model('User', User);
I want to be able to:
1/ show a guide and all that guide's reviews
2/ show all a user's reviews
3/ show all guides a user has reviewed
I think my schema above is fine for 1. But is it suitable for 2 and 3? Am I understanding the MonboDB philosophy correctly?