Mongoose referencing parent in embedded document - node.js

I am fairly new to mongoose so bear with me a little. I have the following two simple schemas defined.
var ShipmentSchema = new Schema({
shipmentId: {
type: String,
default: '',
required: 'Please fill Shipment Id',
trim: true
},
dateInvoiced: {
type: Date,
default: Date.now
},
vendorInvoices:
[
{
referenceNo: {type: Schema.ObjectId,ref: 'VenderInvoice'}
}
],
});
mongoose.model('Shipment', ShipmentSchema);
var VendorInvoiceSchema = new Schema({
type: {
type: String,
default: '',
trim: true
},
referenceNo: {
type: String,
default: '',
trim: true
},
cases: {
type: Number,
required: 'Please fill in number of cases',
},
dateShipped: {
type: Date,
default: Date.now
},
invoicedProducts:
[
{
productId: {type: Schema.ObjectId,ref: 'Shoe'},
}
]
});
mongoose.model('VenderInvoice', VendorInvoiceSchema);
If I want to get a list of VendorInvoices and want to include the shipment that they belong to is there any way to do so? I know how to get a shipment and its nested list of invoices, but if I wanted to go the other direction is there a way? Or do I need to normalize in this case?
Thank you

Related

Model whose fields are the result of a calculation on mongoose

I have a practice problem on mongoose.
I have two models, let's call them product and support.
I need to create a third model which is the result of a mathematical calculation between some fields of the first two models (product and support).
However, I don't want this third model to be saved in the database, unless the user wants it.
I would like to have some advice on the best way to do it.
Thanks in advance
as you can see I have two models (product and support) and a third model (calculation) which is the result of a calculation between the first two models.
const productSchema = new mongoose.Schema({
diametre : {
type: Number,
required: true
},
type: {
type: String,
enum : ['metal', 'wood']
},
weight : {
type: Number,
required: true
},
length: {
type: Number,
required: true
},
createdAt: {
type: Date,
default: Date.now()
}
})
const supportSchema = new mongoose.Schema({
reference: {
type: String,
required: true
},
type: {
type: String,
required: true
},
material: {
type: String,
enum : ['wood', 'metal']
},
internalDiameter : {
type: Number,
required: true
},
externalDiameter : {
type: Number,
required: true
},
internalWidth : {
type: Number,
required: true
},
externalWidth : {
type: Number,
required: true
},
createdAt: {
type: Date,
default: Date.now()
}
})
const calculSchema = new mongoose.Schema({
product: {
type: mongoose.Schema.ObjectId,
ref: 'Product',
required: true
},
support: {
type: mongoose.Schema.ObjectId,
ref: 'Support',
required: true
},
lengthOnSupport:{
type: Number,
required: true
},
numberOfSupport: {
type: Number,
required: true
},
createdAt: {
type : Date,
default : Date.now()
}
})

How to keep track of videos watched with Node.js and Mongodb

I'm building a MEAN stack video app (I'm pretty new to Node and Mongodb) and I need a way to keep track of videos watched. How do I do this?
I was thinking I could have an array of Ids in the user collection that references videos but I'd like to be able to return videos with a watched: true key value pair that's dependent on the user making the request. If this is a good way to do it, how do I return a key value pair that's dependent on another document in another collection?
User model:
let UserSchema = new mongoose.Schema({
email: {
type: String,
required: true,
minlength: 1,
trim: true,
unique: true,
validate: {
validator: VALUE => validator.isEmail(VALUE),
message: '{VALUE} is not a valid email'
}
},
password: {
type: String,
required: true,
minlength: 6
},
admin: {
type: Boolean,
default: false
},
vid_inprogress: {
type: mongoose.Schema.Types.ObjectId
},
vid_completed: [{ type : mongoose.Schema.Types.ObjectId, ref: 'Attachment' }],
tokens: [{
access: {
type: String,
required: true
},
token: {
type: String,
required: true
}
}]
});
Video Model:
var Video = mongoose.model('Video', {
url: {
type: String,
required: true,
minlength: 1,
trim: true
},
title: {
type: String,
required: true,
default: '',
trim: true
},
description: {
type: String,
default: '',
trim: true
},
img: {
type: String,
default: '',
trim: true
},
attachments: [{ type : mongoose.Schema.Types.ObjectId, ref: 'Attachment' }]
});
vid_completed on the User model is where I'd like to keep track of the video ids that have been watched. And the Video model is what would be returned with a key: value pair based on whether the video id is found in the user vid_completed array. Let me know if that makes sense. Thanks in advance!
I ended up just using an array in the User model like so:
vid_inprogress: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Video'
},
vid_completed: [{ type : mongoose.Schema.Types.ObjectId, ref: 'Video' }]
I'll just have to add watched: true on the front end. Let me know if you have a better way. I'd be interested to hear.

Mongoose display with relationships

I have three tables 'cases', 'partners' and 'casepartners' with the following structure:
cases: id,subject,description
partners: id,name,address
casepartners:case,partner,createdAt
I would like to list all cases by also showing for each case, casepartners records where the case id is the same.
I am using this code:
Case.find().sort('-created').exec(function (err, cases) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(cases);
}
});
It shows all cases fine, but I would like to also show for each case object a list of casepartners for that case id...
Let's say a few partners got subscribed to the same case and I would like to list all of those partners or just count how many partners got subscribed to that case.
I am using Angularjs to list all cases using the ng-repeat but I am kinda confused if I have to make a separate call to show casepartners records for each case within ng-repeat or attach this in the same function by using some kind of .populate() or something else with the entity relationships.
These are the models defined:
var CaseSchema = new Schema({
subject: {
type: String,
default: '',
trim: true,
required: 'Subject cannot be blank'
},
description: {
type: String,
default: '',
trim: true
},
created: {
type: Date,
default: Date.now
},
customer: {
type: Schema.ObjectId,
ref: 'Customer'
},
category: {
type: Schema.ObjectId,
ref: 'Category'
}
});
var CasePartnerSchema = new Schema({
case: {
type: Schema.ObjectId,
ref: 'Case'
},
partner: {
type: Schema.ObjectId,
ref: 'Partner'
},
created: {
type: Date,
default: Date.now
}
});
var PartnerSchema = new Schema({
name: {
type: String,
trim: true,
default: ''
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
Can anyone help with this?
If possible I would recommend redefining your collections (tables) since having a CasePartner collection is a little redundant.
Instead of having a case and casePartner collection, I would recommend you only have a case collection and then have an array of partners inside of that collection.
Your schema would then look like this:
var CaseSchema = new Schema({
partners: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Partner'
}],
subject: {
type: String,
default: '',
trim: true,
required: 'Subject cannot be blank'
},
description: {
type: String,
default: '',
trim: true
},
created: {
type: Date,
default: Date.now
},
customer: {
type: Schema.ObjectId,
ref: 'Customer'
},
category: {
type: Schema.ObjectId,
ref: 'Category'
}
});
Your find would then look like this:
Case
.find({})
.sort('-created')
//.populate() populates all info from the partnersSchema for each partner
.populate('partners')
.exec(function(err, cases) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(cases);
}
});
Check out this for more on MongoDB schema design.
try mongoose-reverse-populate. There is a way called populate in mongoose but that helps you when you are first searching caseparters and populate case types . however what you wanted is kind of reverse of that .

Populating field values for referred documents in aggregate call in Mongoose

I have two base schemas User and Course
//User Model
var UserSchema = new Schema({
userId: {
type: String,
default: '',
required: 'Please provide UserId.',
index : true
},
Name :{
type: String,
default: '',
trim : true
}
});
//Course Schema
var CourseSchema = new Schema({
title: {
type: String,
default: '',
required: 'Please fill course title',
trim: true,
index: true
},
description: {
type: String,
default: '',
trim: true
},
category: {
type: String,
ref: 'Category',
index: true
}
});
And a UserCourseProgress schema for storing each user's course progress.
var UserCourseProgress= new Schema({
userId: {
type: String,
ref:'User',
required: 'Please provide the User Id',
index:true
},
courseid: {
type: Schema.Types.ObjectId,
ref: 'Course',
required: 'Please provide the Course Id',
index: true
},
timespent: {
type: Number, //seconds
default: 0
},
score: {
type: Number
}
});
Now I have to aggregate results such over UserCourseProgress schema on Course level such that i show my result like this :
- {Course Name}( {No_Of_Users_taken_Course} ) [e.g. Course A (15) ]
History.aggregate([{
$group: {"_id": {"primary": "$courseid"},
"popularityCount": {$sum: 1}}},
{$sort:{"popularityCount":-1}},
{$project:{"_id":0,
"courseid":"$_id.primary",
"count":1}}
])
Now want to populate Course title from Course Schema through its reference, but after going through the list of aggregation operators(here).I am not able to figure out a way to do so.
Can you suggest any way to populate data from other collections ??

Mongoose - reference an embedded id in the parent document

I have this simple schema so far:
var room = new Schema(
{
name: { type: String, default: null, trim: true }
});
var event = new Schema({
name: { type: String, default: null, trim: true },
startDate: Date,
endDate: Date,
logo: { type: Boolean, default: false },
public: { type: Boolean, default: false },
rooms: [room]
sessions: [
{
title: { type: String, default: null, trim: true },
description: { type: String, default: null, trim: true },
date: Date,
start: Number,
end: Number,
room: { type: Schema.Types.ObjectId, ref: 'room' }
}
]
});
I know how to reference another collection but how do I reference an embedded id in the parent document?
I know this schema is not right because even If I delete a room, the room reference is not removed from a session where it is referenced.
Thanks in advance!
You could create a reference to the event in room schema.Then use the schema.pre middleware to remove the sessions.room value whenever you do a remove on the main parent room.(make sure you remove the eventid from main room schema as well).Also refer Removing many to many reference in Mongoose
var room = new Schema({
name: {
type: String,
default: null,
trim: true
},
eventid: {
type: Schema.Types.ObjectId, //Set reference to event here.
ref: 'event'
}
});
room.pre('remove', function(next) {//event is the schema name.
this.model('event').update({; //this will have all the models,select the event
sessions.room: this._id//will have the room id thats being deleted
}, {
$pull: {
sessions.room: this._id//removes that array from the event.sessions.room
}
}, {
multi: true
},
next();//continues and completes the room.remove.
);
});

Resources