Reverse models relationship in mongoose? - node.js

I have two schemas (Portfolio and Projects that relate to portfolios):
portfolioSchema = new Schema({
name: {type: String, required: true, unique: true},
isActive: {type: Boolean, default: true}
});
projectSchema = new Schema({
name: {type: String, required: true, unique: true},
isActive: {type: Boolean, required: true, default: true},
portfolio: {type: mongoose.Schema.Types.ObjectId, ref: 'portfolioSchema', required: true},
isCodeBaseFullyOwned: {type: Boolean, required: true, default: false}
});
Is there a way to select all portfolios and 'populate' their relevant projects? I can group projects by portfolio but then if there are no projects in a portfolio I would have to figure a way to additionally pull this information, too.

There are 2 ways you could do this.
One would be adding an array of projectIds on the portfolio which you could then populate but could become messy if you are not careful with cleaning up/managing references on updates.
This can be done by updating the portfolioSchema to
portfolioSchema = new Schema({
name: {type: String, required: true, unique: true},
isActive: {type: Boolean, default: true},
projects: [{type: mongoose.Schema.Types.ObjectId, ref: 'projectSchema', required: true}]
});
The other option would require grouping projects by portfolio as you have said and then collecting all portfolio ids that return, and running a second query to get portfolios with
{_id: {$nin: <array of already returned portfolio ids>}}
Both options have pros and cons but currently these are the only two options that I can think of.

Related

What is the best mongoose practices to PATCH a large document: global or specific PATCH?

Let say I have a large Model like this:
const largeSchema = mongoose.Schema({
var1: {type: Number, required: true, unique: true},
var2: {type: String, required: true},
var3: {type: String, required: true},
var4: {type: String, required: true},
varsA: {type: [String], required: true, default: []},
varsB: {type: [SchemaB], ref: 'SchemaB'},
var5: {type: mongoose.Schema.Types.ObjectId, ref: 'Schema5'},
varsC: {type: [SchemaC], required: true, default: []},
varsD: {type: [SchemaD], required: true, default: []},
varsE: {type: [SchemaE], required: true, default: []},
varsF: {type: [SchemaF], required: true, default: []},
var6 {type: Schema6},
varsG: {type: [SchemaG], required: true, default: []},
varsH: {type: [SchemaH], required: true, default: []},
},{timestamps: true});
As you can see, model like this one contains each referenced and embedded models, some in array some not. Furthermore some of these array can be large (no more than 100 items per arrays).
My question is about these embedded schemas in array.
Let's say that every fields can be updated. If I want to update one item in varsE. Should I use a Global PATCH that will patch my whole list; or build one route for each array of this model?
Architecture: Node.JS - Express - Mongoose
I would suggest you update only the specific element in the array instead of a global patch in order to avoid unnecessary contamination of other elements.
Following is an example of how you could update a specific element in the array.
await LargeSchemaModel.update({'items.id': 1}, {'$set': {
'items.$.name': 'updated name',
'items.$.value': 'updated value'
}});

How to create a mongoose sub model without creating a collection for it

const walletTransactionSchema = new mongoose.Schema({
a: {type: Boolean, required: true},
},
{timestamps: {createdAt: 'created_at', updatedAt: 'updated_at'}});
const walletSchema = new Schema({
b: {type: Boolean, required: true},
transactions: [{type: walletTransactionSchema}],
});
walletSchema.index({'transactions': 1}, {sparse: true});
module.exports.Wallet = mongoose.model('Wallet', walletSchema, 'wallets');
module.exports.WalletTransaction = mongoose.model('WalletTransaction', walletTransactionSchema);
I'm trying to create a model for a subdocument (WalletTransaction) without creating a collection for it. Unfortunately mongoose is automatically creating that collection. How can I prevent that behavior and just define a sub-model without creating a collection. I prefer to organize my schemas by refactoring them instead of just embedding them.
I used to do this without trouble with above definitions. I guess after updating to mongoose 6.0.8 (from 5.13) this is happend.
if you wanna create another model inside a model
You should go with the following approach
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
email: {
type: String,
required: true},
about:{type:String},
password: {type: String, required: true},
friends: [new mongoose.Schema({
user: {type: String}
}, {strict: false})],
profileImage: {type: String, default: "default.jpg"},
userName: {type: String, required: true},
matches: {type: Number, default: 0},
wins: {type: Number, default: 0},
losses: {type: Number, default: 0},
backgroundImage: {type: String, default: "default.jpg"},
resetPasswordToken: {type: String, required: false},
resetPasswordExpires: {type: Date, required: false},
isDeleted: {type: Boolean, default: false},
deletedAt: {type: Date, default: null},
}, {timestamps: true}, {strict: false});
module.exports = mongoose.model('User', userSchema);
Like this I create another model in my User model with name Friends in which each entry has one specific id

Create like and dislike with nodejs and mongoose

For my training project, I have to create the controllers for the 'like' and 'dislikes' with the verb post.
Could someone suggest me how to proceed for this?
It's my schema and model:
const mongoose = require('mongoose');
const sauceSchema = mongoose.Schema({
userId: {type: String, required: true},
name: {type: String, required: true},
manufacturer: {type: String, required: true},
description: {type: String, required: true},
mainPepper: {type: String, required: true},
imageUrl: {type: String, required: true},
heat: {type: Number, required: true},
likes: {type: Number, required: true, default:0},
dislikes: {type: Number, required: true, default:0},
usersLiked: {type: [String]},
usersDisliked: {type: [String]},
});
module.exports = mongoose.model('Sauce', sauceSchema);

Mongoose validation value from another table

This is my Product table schema
let schema = new mongoose.Schema({
title: {type: String, required: true},
price: {type: Number, required: true},
description: {type: String, required: true},
sizes: {type: Object, required: true},
offer: {type: Number, default: 0},
images: {type: Array, required: true},
deal: {type: Boolean, default: false},
category: {
_id: {type: Schema.Types.ObjectId, required: true},
name: {type: String, required: true}
},
company_name: {type: String, required: true}
});
What I am trying to do
I am trying to validate if category.name value equal exist in my another table called Category.
You could use an async validator and query the categories collection. Something like this (using promise sytax for validator):
let schema = new mongoose.Schema({
title: {type: String, required: true},
price: {type: Number, required: true},
description: {type: String, required: true},
sizes: {type: Object, required: true},
offer: {type: Number, default: 0},
images: {type: Array, required: true},
deal: {type: Boolean, default: false},
category: {
_id: {type: Schema.Types.ObjectId, required: true},
name: {
type: String,
required: true,
validate: function(nameVal) {
return new Promise(function(resolve, reject) {
let Category = mongoose.model('Category'); //you would have to be sure that Category model is loaded before this one. One reason not to do this
Category.findOne({name: nameVal}, (err, cat) => resolve(cat ? true : false)); //any non null value means the category was in the categories collection
});
}
}
},
company_name: {type: String, required: true}
});
Some thoughts about this:
This is documented at http://mongoosejs.com/docs/validation.html#async-custom-validators.
As it states there, the validator is not run by default on updates. There are a lot of caveats to read through there.
In my experience with NoSQL DBs, the code creating a new Product would take care to make sure the category being assigned was valid. The code probably found the category from the DB at some point prior. So having a validator lookup would be redundant. But you may have a situation where you have a lot of code that creates Products and want validation in one place.
When I see that you are storing a document of {_id: ..., name: ...} as the category field in your Product schema, I think you might want this instead:
...
category: {Schema.Types.ObjectId, ref: 'Category'},
This allows you to store a reference to a category you have retrieved from the categories collection. Mongoose will load up the category document inline for you when you retrieve the products, if you use the populate method on your query. See http://mongoosejs.com/docs/populate.html. There are a lot of options with the populate functionality you might find useful. It does not do validation that the category is valid on save, as far as I know. But if you take this approach, you would have already looked up the category previously in the code before your save (see the link to get a better idea what I mean). Essentially this gives you join like behavior with MongoDB, with the storage savings and other benefits one expects from "normalization".

Unique index on nested fields in Mongoose schema

Are these schemas valid?
var StudentSchema = new mongoose.Schema({
first_name: {type: String, required:true},
last_name: {type: String, required: true},
mother_name : {type: String, required: true},
siblings:[{
name:{type: String, required: true},
age:{type:Number, required: true},
school:{type:String, required:true}
}]
});
And this
var WinSchema = new mongoose.Schema({
event:{type: mongoose.Schema.Types.ObjectId, ref:'Event'},
winners : [{
student: {type: mongoose.Schema.Types.ObjectId, ref: 'Student'},
position: {type:String, enum:["First","Second","Third","Consolation"]}
}]
});
WinSchema.index({event:1,winners.student:1},{unique:true});
mongoose.model('Win',WinSchema);
Can we nest it as shown in StudentSchema?
Can we create a unique index on nested documents?
Yes you can nest it, see schema example here and of course you can create a index on nested documents here is the example.

Resources