Mongoose display with relationships - node.js

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 .

Related

Error in getting referenced objects in admin-bro Nodejs (There are no resources with given id)

So the error I am getting is There are no resources with given id: "workshop.timelines"\nThis is the list of all registered resources you can use.
This is the resource I am trying to visualize in adminbro
const LearnerSchema = new mongoose.Schema(
{
workshops: [
{
workshop: {
workshop_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'workshop',
},
code: {
type: String,
},
timeline: {
type: mongoose.Schema.Types.ObjectId,
ref: 'workshop.timelines',
},
},
],
{ timestamps: true }
);
This is the workshop model:
const WorkshopSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
description: {
type: String,
},
timelines: [
{
group: {
type: Number,
},
city: {
type: mongoose.Schema.Types.ObjectId,
ref: 'city',
},
description: {
type: String,
},
venue: {
type: mongoose.Schema.Types.ObjectId,
ref: 'venue',
},
month: {
type: String,
},
start: {
type: Date,
},
end: {
type: Date,
},
registration_start: {
type: Date,
},
registration_end: {
type: Date,
},
registrations: {
type: Number,
default: 0,
},
registrations_cancelled: {
type: Number,
default: 0,
},
d_reg: {
type: Number,
default: 0,
},
classLink: {
type: String,
default: '',
},
status: {
type: String,
default: 'E',
},
resources: {
_id: false,
videoSessions: { type: Boolean, default: false },
},
},
],
status: {
type: String,
enum: ['NEW', 'F', 'DISABLED'], //f = FEATURED
default: 'NEW',
},
},
{ timestamps: true }
);
WorkshopSchema.index({ name: 'text', description: 'text' });
module.exports = Workshop = mongoose.model('workshop', WorkshopSchema);
Now, I have added both of these resources among others in my adminbro options, but when adminbro tries to fetch some records from the Collection, it fails with the error:
There are no resources with given id: "workshop.timelines"\nThis is the list of all registered resources you can use.
One more thing that might be affecting this issue is that in MongoDB the value of timelines in the workshop object is a mix of ObjectId and string, however I have tried converting all the object values to ObjectId but it still shows the same error.
Would really appreciate any help here.
had the same issue.
Basically, any ref model that appears in the models that are included in the resources array has to be included as well.
In your case, I will suggest to have a look at the ResourceOptions (https://adminbro.com/ResourceOptions.html) to see if you can included the nested property
comment out you're every (type: mongoose.Schema.Types.ObjectId,) to like this ↓↓ or you can delete it as well.
// type: mongoose.Schema.Types.ObjectId,
and it will work fine.
This is not working because by default, Mongoose adds an _id property to your schemas and you are explicitly defining it so need to explicitly insert it while creating but in AdminBro Dashboard you do not have that kind of option to add _id while creating any new Object. So, for that reason comment every _id generating field.

Remove multiple records from a collection- MongoDB

I have two models with the following schemas:
Map:
var MapSchema = mongoose.Schema({
ownerId:{
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
mapName: {
type: String
},
mapImagePath:{
type: String,
required: true
},
createdAt: { type: Date, default: Date.now },
devices: [{type: Schema.Types.ObjectId, ref: 'Device'}]
});
Device:
var DeviceSchema = mongoose.Schema({
deviceName:{
type: String,
required: true,
index: true,
unique: true
},
roomCapacity: {
type: Number
},
roomImagePath:{
type: String,
},
mapId:{
type: Schema.Types.ObjectId,
ref: 'Map',
required: true
},
coords:{
type: [Number], //[xcoord, ycoord]
required: true
},
status:{
type: String,
required: true,
default: 'Available'
},
user:{
type: String,
},
createdAt: { type: Date, default: Date.now }
});
As you can see, one map has many devices. Now, When I delete a map, I want to delete all of the devices that belong to it. This should be easy because each map has an array of it's device ID's. But I can not seem to find a way to delete multiple records from a collection at once. I delete my map with this function:
module.exports.deleteMap = function(mapId, callback){
Map.findOneAndRemove({_id: mapId}, callback)
};
This returns the map so I can access it's device ID's as map.devices. However, how can I now use map.devices to remove all of these from the device collection? I was thinking something like device.remove(map.devices) ?
You can first find the map object and use the array of device _ids with the $in operator to remove all the devices in the map. Below is some (untested) sample code.
module.exports.deleteMap = function(mapId, callback) {
Map.findOneAndRemove({_id: mapId}, function(dbErr, map) {
if(dbErr) {
return callback(dbErr);
}
Device.remove({_id: {$in: map.devices}}, function(dbErr) {
if(dbErr) {
return callback(dbErr);
}
return callback(undefined, map);
});
});
};

How filter sub sub document in MongoDB

Hello I am working with the full Stack 'MEAN' and i have a data structure in MongoDB as indicated below:
var ExpenseSchema = new Schema({
date: {
type: Date,
default: Date.now,
required: 'Ingrese la fecha del comprobante'
},
supplier: {
type: Schema.ObjectId,
ref: 'Supplier',
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var SupplierSchema = new Schema({
name: {
type: String,
default: '',
required: 'Ingrese la Razon Social del Proveedor',
trim: true
},
category: {
type: Schema.ObjectId,
ref: 'Category',
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var CategorycompraSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Rubrocompra name',
trim: true
},
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
Each 'Expense' has a 'Supplier' and each supplier has a 'Category'
I need to query so that I filter all 'Expenses' in a particular category. Someone could tell me how this can be done with MongoDB or mongoose?
That is an important case in mongodb, aggregations in mongodb is the right approach to solve this. You need to $unwind the supplier array and then category array and then use $group to put it back together:
My solution may differ depending upon your requirement, but this is something you have to do:
db.test.aggregate(
{ $match: {...}}, //match query
{ $unwind: '$supplier'},
{ $unwind: '$supplier.category'},
{ $match: {supplier.category.a: ...}}, //match query after unwinding of supplier and category
{ $group: {_id: '$_id', ...})
It will first unwind the supplier array and then unwind category array
But since you are also using mongoose, you can use plain JavaScript. You can fetch all expenses and then loop through them and
obtain your result
Expense.find().then(function(expenses) {
expenses.forEach(function(suppliers){
suppliers.forEach
...
})
})
Although this javascript way would increase effort in single threaded enviroment(node js), but still it comes in handy for some typical mongodb queries

Mongoose referencing parent in embedded document

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

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 ??

Resources