How to create reference in Model Schema in mongoose - node.js

If I have model Products:
var ProductSchema = new Schema({
title: {
type: String,
maxlength: 20,
required: true
},
description: {
type: String,
maxlength: 300
},
price: Number,
active: Boolean,
category: {
}
});
And I must create category, type ObjectID with reference to category model
I create right now this:
var CategorySchema = new Schema({
name: {
type: String,
maxlength: 300
},
description: {
type: String,
maxlength: 300
}
});
Can someone what to do now? Because i do not know how.

The category object in the schema should be as following:
category: {
type: Schema.Types.ObjectId,
ref: 'Category' //category model name
}
For more information about references you should read the following paragraph:
DBRefs are references from one document to another using the value of the first document’s _id field, collection name, and, optionally, its database name. By including these names, DBRefs allow documents located in multiple collections to be more easily linked with documents from a single collection.
To resolve DBRefs, your application must perform additional queries to return the referenced documents like population queries.

Related

Dynamically create a collection field based on the user response with mongoose in mongo dB

I've a database collection where two types of users are there.
1.Customer: They will have all the basic functionalities.
2.Vendor: They will also have the basic functionalities available in addition they can create, delete, update and get/view the vehicles.
suppose a vendor created a vehicle so a vehicle id will get added to the vendor's collection likewise:
{
. /*other fields*/
.
.
listings: [
"uniqueId",
"uniqueId2"
]
}
I did some searching and found out that to add vehicle Id's to listings, the field needs to be created first in mongoose otherwise my data will not get inserted in mongodb through mongoose.
This rises a problem where all the users have listings field in them.
So, here's my user model I have created:
const userSchema = new mongoose.Schema({
user_type: {
type: String,
required: [true, "user type is required!"],
enum: ["customer", "vendor", "Customer", "Vendor"],
default: "customer"
},
listings: {
type: Array,
},//TODO: only create the listings array if the user type is vendor
});
So, my question is can I create this listing field only if the user_type is vendor?
As defined, in the mongoose documentation, Arrays have a default value of [].
So you'll need to override it. Try this:
const userSchema = new mongoose.Schema({
user_type: {
type: String,
required: [true, "user type is required!"],
enum: ["customer", "vendor", "Customer", "Vendor"],
default: "customer"
},
listings: {
type: [String], // can also be ObjectId
default: undefined
},
});

Mongoose reference in Collection for another Collection

I'll start with this that I'm new to backend and I was looking for some solutions for my problem but i don't know which solution will be right for my problem.
So to the point. I'm creating a pizza restaurant project in Next.js with Mongoose and simple api. I have for example collections : Product (here will be my pizzas) and the code for this model is below.
import mongoose from "mongoose";
const ProductSchema = new mongoose.Schema
{
title: {
type: String,
required: true,
maxlength: 60,
},
desc: {
type: String,
required: false,
maxlength: 200,
},
img: {
type: String,
required: false,
},
prices: {
type: [Number],
required: true,
},
extraOptions: {
type: [
{
text: { type: String, required: true },
price: { type: Number, required: true },
},
],
},},); export default mongoose.models.Product || mongoose.model("Product", ProductSchema);
In this Schema i have an array of extraOptions ( for example extra cheese for 1$ and extra onion for 1$) but I want to adopt the principle that all products can have the same additional additives. It is a pity to prescribe the same additives for each products ( each pizza)
So, can I create a new model of extraOptions for pizzas and create some 'reference' (like in SQL but in Mongo) for collections Product? For example my simple model of extraOptions :
import mongoose from "mongoose";
const extraSchema = new mongoose.Schema(
{
title: {
type: String,
required: true,
maxlength: 60,
},
price: {
type: Number,
required: true,
},
},
);
export default mongoose.models.ExtraOptions || mongoose.model("ExtraOptions", extraSchema);
How could I create a maybe reference in the products collection to display there all documents of the additional extra options ? I was reading about CopyTo method, populate method and subdocuments but i don't know which is solution for me and how can i Use it... Thanks for all answers and sorry if i wrote epic here.
Maybe some extraOptions: [{type:mongoose.Schema.Types.ObjectId,ref:'extraSchema'}],or i dont really know. Thanks a lot for help
It is a pity to prescribe the same additives for each products ( each
pizza) So, can I create a new model of extraOptions for pizzas and
create some 'reference' (like in SQL but in Mongo) for collections
Product?
Your assumption is correct - there is no need to store the same data of additives in each one of your products. Indeed, the right approach is to extract the additives data to a different collection, and reference the relevant additives for each product.
This way, your ProductSchema will look like this (for brevity I excluded the 'required: false' field since false is the default value):
import mongoose from "mongoose";
const ProductSchema = mongoose.Schema({
title: {
type: String,
required: true,
maxlength: 60,
},
desc: {
type: String,
maxlength: 200,
},
img: String,
prices: {
type: [Number],
required: true,
},
extraOptions: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'ExtraOptions'
}],
});
export default mongoose.models.Product || mongoose.model("Product", ProductSchema);
In this way, when you get some products documents, the 'extraOptions' property will contain the id's of the extraOptions ducements.
If you want to retrieve the data of the additives itself, instead of just the id, you can use the populate method of mongoose:
Product.find().populate('extraOptions');
For further learning about schema design in mongoDB (it might help you decide in general whether to reference or embed), check this video

How to perform a cascading deleting in MongoDB or Mongoose

I'm working on an e-commerce project in Express and MongoDB. I'm confused with architecture on how to make relationship between two models such that if I delete one element from a table, all of it's associations should be deleted. CASCADE delete, if I'm using the correct term.
I'm not a database designer, just learning Express and MongoDB. So apologies if my schema is not that good.
I have two tables (or documents in MongoDB). One is Order with schema below:
const orderSchema = new mongoose.Schema({
shippingInfo : {
type: mongoose.Types.ObjectId,
ref: 'Address'
},
user : {
type: mongoose.Types.ObjectId,
ref: 'User',
required: true
},
orderItems: [
{
type: mongoose.Types.ObjectId,
ref:'OrderItem'
}
],
totalPrice: {
type: Number,
required: true,
default: 0.0
},
status: {
type: String,
enum: ['processing', 'shipped', 'delivered','cancelled'],
default: 'processing'
},
deliveredAt: {
type: Date,
}
})
and OrderItems
const orderItemSchema = new mongoose.Schema({
product: {
type: mongoose.Types.ObjectId,
ref: 'Product'
},
name: {
type: String,
required: true
},
quantity: {
type: Number,
required: true
},
price: {
type: Number,
required: true
},
image: {
type: String,
required: true
},
})
I want if I delete an Order, all of its OrderItems should be deleted right away (using remove middleware in Order).
I know that Django has something called on_delete=model.CASCADE when we create relationships, but I'm unaware of such thing in Mongoose.
I don't want to explicitly make another API request to search for and delete all OrderItems that are referenced in orderItems array in an Order, once it is deleted. There has to be a better approach for this.
Another post on Stack Overflow suggested that in remove middleware of Order I should do something like
OrderItem.find({ order_id: this._id }).remove().exec();
That would require me to refer order_id in OrderItem right?
And this would create circular dependency since OrderItem would require Order to be created first and vice versa.
What should I do here? Should I change the schema for both tables i.e. remove orderItems entry from Order and instead add order_id in OrderItem? Or is there a Mongoose way to overcome this situation?

MongoDB: How do you create an index on a field which is nested in two arrays?

I have a Mongo schema which essentially has two nested arrays, each of which contain objects. I would like to create an index on a field nested in that second level array. The schema looks as such:
const restaurantsSchema = new Schema({
restaurantName: { type: String, required: true },
dishes: [{
dishName: String,
price: Number,
description: String,
reviews: [{
reviewId: Number, **<<<< INDEX THIS FIELD**
userId: {
type: Schema.Types.ObjectId,
ref: 'users',
required: true,
},
photoUrl: String,
caption: String,
createdAt: Number,
updatedAt: Number,
rating: Number,
reviewText: String,
}],
}],
});
The idea is that I can use this index to find and update a user's reviews. Is this possible with Mongo's multikey indexing or is there another way to do this? Any help would be appreciated.
I believe if you change the schema to
reviewId: {
type: Number,
unique: true
}
mongodb automatically creates an index because each value should be unique. That really the only qualification for creating any index.
write back if it works!

How to populate a document inside an array inside another document?

Sorry if title looks complicated... I couldn't think of a better way to describing it.
My real case situation matches the following Schemes:
Collection1:
const mongoose = require('mongoose');
const itemSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: [true, 'Name is required.'] },
quantity: { type: Number, required: [true, 'Quantity is required.'] },
collection2: { type: mongoose.Schema.Types.ObjectId, ref: 'Collection2' }
}, { _id : false });
const collection1Schema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: [true, 'Name is required.'] },
imagePath: { type: String, required: [true, 'Image is required.'] },
items: [itemSchema]
});
module.exports = mongoose.model('Collection1', collection1Schema);
Note: itemsSchema is inside the collection1 file (and having no declared _id's) because they only exist for the Collection1 model (considering "quantity" and other fields I removed for simplification). This itemsScheme is not needed elsewhere as another collection.
Collection2:
const mongoose = require('mongoose');
const collection2Schema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: [true, 'Name is required.'], unique: true }
});
module.exports = mongoose.model('Collection2', collection2Schema );
Note: Other properties (such as 'imagePath') were removed for simplification.
Now, this is the query I am trying to run:
Collection1.find()
.populate({
path: 'items',
populate: {
path: 'collection2', model: 'Collection2'
}
})
.then(...)
.catch(...);
And this is the error message I am getting when I run it:
Error fetching collection1: Cast to ObjectId failed for value "{
name: 'an item name',
quantity: 750
}" at path "_id" for model "Collection1"
The exact same error happens if I just run:
Collection1.find()
.populate('items')
.then(...)
.catch(...);
Maybe I cannot run .populate('items') because it has no declared model. If this is the case, how can I populate collection2 while querying collection1? Again, I cannot consider storing items in a separated collection.
But if I run:
Collection1.find()
.populate('collection2')
.then(...)
.catch(...);
I get the items, no errors, but it doesn't populate collection2. Well, it makes sense for the items because they're just an array of a block of properties inside collection1. But what about populating collection2?
Collection2 already has a few documents added, all with their _ids and other fields well filled. In the controller, I set _id: new mongoose.Types.ObjectId(), while creating a new document for both cases, Collection1 and Collection2.
In the front-end, I create a new document for Collection1, I add items, each item with a document from Collection2, and I save everything with no errors. I also confirmed everything is been properly saved (collection1 has list of items and each item an _id reference to collection2). The only problem is populating collection2 inside this array.
I have already tried restructuring everything with _ids (including itemScheme) and dropping all collections to test it again but no success.
I have been stuck with this problem for about three days now.
Is there any special property I should be setting for populate to make it work for this specific structure?
Thanks in advance...
populate('items')
This will not work as item is not a model.
What you want is following:
Collection1.find()
.populate('items.collection2')
.then(...)
.catch(...);
This will populate collection2 in all the array elements

Resources