How to solve circular dependency in model definition in node? - node.js

I'm following MVC architecture using express and mongoose and I came upon an issue with circular-dependency. The code itself is written in ES6.
I have these two particular models (keep in mind that I obscured these models as much a possible):
Destination model, which contains information about all available rooms:
// destination.model.js
import mongoose, { Schema } from 'mongoose';
import Booking from './booking.model'; // eslint detect dependency cycle here
import Room from './room.model';
const DestinationSchema = new Schema({
id: { type: Number, required: true },
name: { type: String, required: true, max: 100 },
description: { type: String, required: false },
rooms: [Room.schema]
});
DestinationSchema.statics.getAvailableRooms = async function (startDate, endDate) {
const bookings = await Booking.find({ 'room._id': room._id });
// do something with these bookings
};
export default mongoose.model('Destination', DestinationSchema);
and Booking model, which is in relation many to one with Destination.
// booking.model.js
import mongoose, { Schema } from 'mongoose';
import Destination from './destination.model';
import Room from './room.model';
const BookingSchema = new Schema({
id: { type: Number, required: true },
client: { type: String, required: true },
startDate: { type: Date, default: '' },
endDate: { type: Date, default: '' },
room: { type: Room.schema, required: false },
destination: Destination.schema
});
export default mongoose.model('Booking', BookingSchema);
Main issue:
ESLint detects dependency cycle in destination model (and in booking model) - which is present. The reason for that, is that I have a static method in Destination model, which looks over all Bookings (and possibly might call a static method in the future).
My question (or actually looking for an advice) is, how do I handle this issue? I come from Ruby on Rails background, so I'm really used to having both instance and static methods defined in a model, with single file.
I don't want to separate methods into another file and I would like to keep them in single file - is that possible in any way or should I really go for file separation?
Cheers

I think you should model booking schema like:
const BookingSchema = new Schema({
id: { type: Number, required: true },
client: { type: String, required: true },
startDate: { type: Date, default: '' },
endDate: { type: Date, default: '' },
room: { type: Room.schema, required: false },
destination: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Destination',
},
});

Related

Mongoose find() returning empty Array of SchemaType

I have the following Schema type called Orders. I am using Arrays of SchemaTypes in some properties. When I save it to the database, it's saving everything fine. I can open the database and see all the data there.
But the problem happens in one property called "files", whenever I try to use find() or findOne() or findById(), this property always comes empty, even if I have data to show.
This is my Schemas:
const statusChildSchema = new Mongoose.Schema({
...
});
const shippingChildSchema = new Mongoose.Schema({
...
});
const fileChildSchema = new Mongoose.Schema({
path: { type: String, required: true, trim: true },
type: { type: String, required: true },
});
const ordersSchema = new Mongoose.Schema(
{
// Relationships
creator: {
type: Mongoose.Schema.Types.ObjectId,
required: true,
ref: 'Users',
autopopulate: false,
},
template: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Templates',
},
// Common
status: { type: String, trim: true, default: 'new', required: true },
// Child schemas
status_updates: [statusChildSchema],
shipping: [shippingChildSchema],
files: [fileChildSchema],
// Date properties
timings: {
created_at: { type: Date, default: Date.now, required: true },
...
},
},
{ collection: 'Orders', toJSON: { virtuals: true } }
);
Both statusChildSchema and shippingChildSchema is working normally. The problem is only with fileChildSchema. They are very similar, so I don't know what to do. I have researched in Mongoose documents and nothing helpful have been found.
This is the part of my code:
const order = await OrdersModel.findOne({
_id: orderId,
creator: userId,
});
console.log(order.files); // always printing "[]" empty array
I fixed it by installing last version of Mongoose (Version 5.11.8) and restarting everything. Looks like a bug.

How to Auto update Value in mongodb Document

I have a field in my mongoose schema called "active" and I wanted to know if there is any way that every date expired in a particular document, then the "active" field would change to false. how should I do that if so, What is the easiest way to do this? else, what is recommended?
And below is my Schema;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const schema = new Schema({
user_id: {
type: String,
required: true
},
firstName: {
type: String,
required: true
},
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
hash: {
type: String,
required: true
},
active: {
type: Boolean,
},
role: {
type: String,
required: true
},
createdDate: {
type: Date,
default: Date.now
}
});
schema.set('toJSON', { virtuals: true });
module.exports = mongoose.model('User', schema);
You can do this with a feature in mongo called Change Streams that allow you to access real-time data changes. You can subscribe to the changes of a single collection or the whole database and react to them. You can also filter for specific changes or transforms. For your case an example would be something like this.
EDIT: Change streams implementation is only available on replica sets.
const pipeline = [
{ $match: { expire_date: {$lt: Date.now()} } },
{ $set: { active: false } }
];
const collection = db.collection('user');
const changeStream = collection.watch(pipeline);
changeStream.on('change', next => {
// process next document
});

Defining custom Schema methods in mongoose to return a single property?

I have two schemas and from each, I want to return a single property.
Here is the schema for a single document or file.
// #ts-check
import { Schema, model } from "mongoose";
const docSchema = new Schema({
name: { type: String, required: true },
approved: { type: Boolean, default: false },
docType: { type: String, required: true },
size: { type: Number, required: true },
bucket: { type: Schema.Types.ObjectId, ref: "Bucket" }
});
docSchema.methods.getSize = function(cb) {
// Return the size of document
};
const Doc = model("Document", docSchema);
export default Doc;
And this is the schema for a bucket of documents
// #ts-check
import { Mongoose, Schema, model } from "mongoose";
const bucketSchema = new Schema({
name: { type: String, required: true, index: true, unique: true },
projectId: { type: String, required: true, index: true },
contractorId: { type: String, required: true, index: true },
Docs: [{ type: Schema.Types.ObjectId, ref: "Document" }]
});
bucketSchema.methods.getSize = function(cb) {
// return the size of all documents that belong to a single bucket
// How do I traverse over the Docs array and use Doc.getSize()???
};
const Bucket = model("Bucket", bucketSchema);
export default Bucket;
Is there a way to do this? I don't want to have to query the database each time I need to get the size of the bucket and then traverse over the results and add the size of each document. I want to make it simple so that basically I call Bucket.getSize() and it returns me the size of the bucket so I can restrict the users from uploading files when they've exceeded a certain limit.
Any help would be appreciated.

mongoose find and update nested object in an array

Below is my mongoose schema.
An application can have multiple incidents and subscribers. Each incident can have multiple journal updates.
Application->incident(s)->journalupdate(s)
My goal is to allow users to update an incident detail in an application. Later add a journal update to an incident in the application.
const JournalUpdateSchema = mongoose.Schema({
timeStamp: { type: Date, required: true },
owner: { type: String },
updateText: { type: String }
});
// Incident Schema
const IncidentSchema = mongoose.Schema({
incidentNum: { type: String, required: true },
incidentPriority: { type: String, required: true },
description: { type: String, required: true },
jounralUpdates: [JournalUpdateSchema]
});
//Subscriber Schema
const SubscriberSchema = mongoose.Schema({
subscriberName: { type: String },
subscriberMail: { type: String },
subscriberPhone: { type: String }
});
// Application Schema
const ApplicationSchema = mongoose.Schema({
applicationName: { type: String, required: true },
clusterName: { type: String, required: true },
ragStatus: { type: String, required: true },
incidents: [IncidentSchema],
subscribers: [SubscriberSchema]
});
I have already tried Applications.Update(), Applications.findByIdAndUpdate() methods but I cannot reach the incident level to update.
can someone help me with this please? Any help is greatly appreciated.
Thanks.

Get info from 2 separate Mongo documents in one mongoose query

Im using MongoDb, and I have a workspace schema with mongoose (v4.0.1):
var Workspace = new mongoose.Schema({
name: {
type: String,
required: true
},
userId: {
type: String,
required: true
},
createdOn: {
type: Date,
"default": Date.now
}
});
And a user schema:
var User = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
organisation: {
type: String,
required: true
},
location: {
type: String,
required: true
},
verifyString: {
type: String
},
verified: {
type: Boolean,
default: false
},
password: {
type: String,
required: true
},
createdOn: {
type: Date,
"default": Date.now
},
isAdmin: {
type: Boolean,
default: false
}
});
So the Workspace userId is the ObjectID from the User document.
When Im logged in as an adminstrator, I want to get all workspaces, as well as the email of the user that owns the workspace.
What Im doing is getting very messy:
Workspace.find({}).exec.then(function(workspaceObects){
var userPromise = workspaceObects.map(function(workspaceObect){
// get the user model with workspaceObect.userId here
});
// somehow combine workspaceObjects and users
});
The above doesnt work and gets extremely messy. Basically I have to loop through the workspaceObjects and go retrieve the user object from the workspace userId. But because its all promises and it becomes very complex and easy to make a mistake.
Is there a much simpler way to do this? In SQL it would require one simple join. Is my schema wrong? Can I get all workspaces and their user owners email in one Mongoose query?
var Workspace = new mongoose.Schema({
userId: {
type: String,
required: true,
ref: 'User' //add this to your schema
}
});
Workspace.find().populate('userId').exec( (err, res) => {
//you will have res with all user fields
});
http://mongoosejs.com/docs/populate.html
Mongo don't have joins but mongoose provides a very powerfull tool to help you with you have to change the model a little bit and use populate:
Mongoose population
You have to make a few changes to your models and get the info of the user model inside your workspace model.
Hope it helps

Resources