MongooseJS: Subdoc Schemas in Separate Files - node.js

Let's say that I have a schema called LeagueSchema, which needs to contain some general information about the league (e.g. the name, time created, etc.), as well as some more complicated objects (e.g. memberships). Because these memberships are not needed outside of the league, I don't think it's necessary for them to be their own collections. However, I think for the sake of modularity it would be best for these schemas to live in their own separate files.
It would look something like this:
league.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var LeagueSchema = new Schema({
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
name: {
type: String,
default: '',
trim: true
},
memberships: [MembershipSchema]
});
membership.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var MembershipSchema = new Schema({
startDate: {
type: Date,
default: Date.now
},
endDate: {
type: Date,
default: null
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
Unfortunately, this doesn't work. I get the following error:
ReferenceError: MembershipSchema is not defined
This is obviously happening because LeagueSchema is dependent on MembershipSchema, but I'm not sure what the best way to include it is. Can I define it as a dependency somehow? Or should I just include the file?
Also, is it bad practice to use subdocuments this way? Is there any reason it would be better to let all of these objects live in their own collections?

In your membership.js, export the membership sub-doc schema as a module:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var MembershipSchema = new Schema({
startDate: {
type: Date,
default: Date.now
},
endDate: {
type: Date,
default: null
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
module.exports = MembershipSchema;
You can then require the exported module schema in your LeagueSchema document:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var MembershipSchema = require('./membership');
var LeagueSchema = new Schema({
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
name: {
type: String,
default: '',
trim: true
},
memberships: [MembershipSchema]
});
To answer your second question, as a general rule, if you have schemas that are re-used in various parts of your model, then it might be useful to define individual schemas for the child docs in separate files as so you don't have to duplicate yourself. A good example is when you use subdocuments in more that one model, or have two fields in a model that need to be distinguished, but still have the same subdocument structure.
If your memberships are not used elsewhere then rather treat the schema as an embedded document (document with schema of its own that is part of another document, such as items within an array):
Example definition and initialization:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var MembershipSchema = new Schema({
startDate: {
type: Date,
default: Date.now
},
endDate: {
type: Date,
default: null
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
var LeagueSchema = new Schema({
created: {
type: Date,
default: Date.now
},
updated: {
type: Date,
default: Date.now
},
name: {
type: String,
default: '',
trim: true
},
memberships: [MembershipSchema]
});
mongoose.model('League', LeagueSchema);

Your membership.js file should export the schema and the league.js file should import it. Then your code should should work.
In membership.js towards the bottom add:
module.exports = MembershipSchema;
In league.js, add
var MembershipSchema = require('membership.js');

Related

How to create a dynamic nested mongoose document with the same schema on multiple levels

Before everyone tells me I can't call a const before initializing, I do know that.
But I think this is the simplest way to render the concept I have in mind, (where any subdocument within the replies array also has the same schema as the parent, and documents within the replies array of those subdocuments also having the same schema). I would really appreciate anyone's input.
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
var commentSchema = new mongoose.Schema({
content: String,
createdAt: {
type: Date,
default: Date.now
},
score: {
type: Number,
default: 1
},
username: {
type: String,
lowercase: true
},
parent: {
type: Schema.Types.ObjectId,
ref: 'comment'
},
replyingTo: String,
replies: [commentSchema]
});
module.exports = mongoose.model("comment", commentSchema);
Since a const can't be called before initialization, to fix this issue the parent schema should be called on the children array after initialization the code below:
commentSchema.add({ replies: [commentSchema] })
The final result should look like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const commentSchema = new mongoose.Schema({
content: String,
createdAt: {
type: Date,
default: Date.now
},
score: {
type: Number,
default: 1
},
username: {
type: String,
lowercase: true
},
parent: {
type: Schema.Types.ObjectId,
ref: 'comment'
},
replyingTo: String,
});
commentSchema.add({ replies: [commentSchema] })

How to rename a collection without loosing data of collection in Mongoose

I am using Mongoose in my NodeJs project. I want to rename my collection from OldCollectionName to NewCollectionName.
I tried following:
Mongoose.connection.collection('OldCollectionName').rename('NewCollectionName');
But this is making my collection empty. Can someone suggest how can I change the collection name without loosing my collection data? I need all the data from OldCollectionName in NewCollectionName as well.
I have following schema for my model
const Mongoose = require('mongoose');
const mongooseSequence = require('mongoose-sequence');
const Schema = Mongoose.Schema;
var collectionSchema = new Schema({
collectionIdAutoIncrement: { type: Number,unique: true,index:true,sparse:true},
fieldName: {
type: String,
default: ''
},
otherCollectionId: {
type: Schema.Types.ObjectId,
required: true,
ref: "OtherCollection",
},
created: { type: Date, default: Date.now },
updated: { type: Date},
});
collectionSchema.plugin(mongooseSequence, { inc_field: 'collectionIdAutoIncrement' });
var oldCollection = Mongoose.model('OldCollectionName', collectionSchema);

node mongoose update two collections

I have two schemas, a "projects" schema, and an "applications" schema.
What is the most efficient way of creating a new entry in a collection and updating an existing entry in another collection based on data inside the new entry? Can I avoid making multiple API requests and somehow run a "stored procedure" on the mongoDB end to handle updating the Projects collection when there is a change in the Applications collection?
In this scenario, ideally when an application for a project is created, a new entry is created in the Applications collection and the Project in the Projects collection is updated to reflect the information in the Application.
Can I do this without making multiple api requests?
Project Schema:
// models product.js
const mongoose = require('mongoose');
const { ObjectId } = mongoose.Schema;
const projectSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: true,
maxlength: 32
},
applications: {
type: Number,
default: 0
},
created_by: {
type: ObjectId,
ref: 'User'
},
applicants: {
type: Array,
default: []
}
}, {timestamps: true}
);
module.exports = mongoose.model("Project", projectSchema);
Application Schema:
const mongoose = require('mongoose');
const applicationSchema = new mongoose.Schema({
applicantId: {
type: ObjectId,
ref: 'User'
},
ownerId: {
type: ObjectId,
ref: 'User'
},
projectId: {
type: ObjectId,
ref: 'Project'
}
}, {timestamps: true});
module.exports = mongoose.model("Application", applicationSchema);
Note that these are separate schemas because they each carry around 15 fields, i've trimmed them down to post this question.
I suggest to use hooks for mongoose model, there is a post save hook which you can use on Application model to update Project and increment application count.
EDIT - Added Pseudo Code
Project Model
// models -> project.js
const mongoose = require('mongoose');
const {
ObjectId
} = mongoose.Schema;
const projectSchema = new mongoose.Schema({
name: {
type: String,
trim: true,
required: true,
maxlength: 32
},
applications: {
type: Number,
default: 0
},
created_by: {
type: ObjectId,
ref: 'User'
},
applicants: {
type: Array,
default: []
}
}, {
timestamps: true
});
projectSchema.statics = {
/**
* Find project by _id
*
* #param {ObjectId} _id
* #api private
*/
get: function (_id) {
return this.findOne({
_id
})
.exec();
}
}
module.exports = mongoose.model("Project", projectSchema);
Application Model
const mongoose = require('mongoose');
const projectModel = require("./project")
const applicationSchema = new mongoose.Schema({
applicantId: {
type: ObjectId,
ref: 'User'
},
ownerId: {
type: ObjectId,
ref: 'User'
},
projectId: {
type: ObjectId,
ref: 'Project'
}
}, {
timestamps: true
});
// Async hook
applicationSchema.post('save', function (doc, next) {
const relatedProject = projectModel.get(doc.projectId);
relatedProject.applications++;
relatedProject.save();
next();
});
module.exports = mongoose.model("Application", applicationSchema);

Mongoose- How to reference embed document element?

I have users.js schema with a embeded document array pets. For each user, a user can have multiple pets(usually no more than 3 I would think).
For each pet, there would be a daily chart. So it would be many daily charts for a pet. I have read with embedded documents that each array element is indexed. In daily.js, how can I reference the pet it would belong to for the populate() function?
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
username: { type: String, required: true, unique: true },
location: String,
pets: [{ name: 'string', animalType: 'string'}], //could have more than one pet
created_at: Date,
updated_at: Date
});
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var dailySchema = new Schema({
tite: String,
_pet: { type: Number, ref: 'User.pet' }, // not sure how to reference name in user.pets[#] array
created_at: Date,
updated_at: Date
});
Quoting
Sorry to disappoint but that is an anti-pattern. Populate can't populate from another collection's subdocs - the reason why you're getting that error is that there's no model for boards.
So it may be not good patten to reference to embedded document. It could be better to separate pet from User as one schema
var PetSchema = new Schema ({
name: 'string',
animalType: 'string'
});
And the UserSchema and DailySchema will be
var userSchema = new Schema({
...
pets: [{ type: Schema.Types.ObjectId, ref: 'Pet' }], //could have more than one pet
});
var dailySchema = new Schema({
_pet: { type: Number, ref: 'Pet' }, // not sure how to reference name in user.pets[#] array
});

How to compare _id value of Models made using Mongoose?

I have the following two schemas of User and Critique and I've got the data persisted in MongoDB database:
var userSchema = mongoose.Schema({
critiques: [{ type: Schema.ObjectId, ref: 'Critique' }],
});
var User = mongoose.model("User", userSchema);
var critiqueSchema = mongoose.Schema({
author: {type: String, default: ''},
date: { type: Date, default: Date.now },
comment: { type: String, default: '' },
stars: { type: Number, default: 0 },
_user: { type: Schema.ObjectId, ref: 'User' }
});
var Critique = mongoose.model("Critique", critiqueSchema);
user.critiques[0]._id.equals(critique._id) is giving me undefined is not a function.
How to compare _id value of a User instance with the Critique instance?
The critiques field of your user object directly contains an array of ObjectIds, so it would just be:
user.critiques[0].equals(critique._id)
user.critiques would only contain full Critique objects if you chained a .populate('critiques') call in the find where you obtained user.

Resources