Modified model in node.js not reflecting on MongoDB - node.js

I just modified my model Order for my API because I need a value to pass a condition. But unfortunately, my new set of properties (orderStatus) on my collection did not reflect on my MongoDB database.
I don't need to input a value for the property (orderStatus) since I set a default value ("Pending"). I tried to delete the entire collection orders to see any changes but there were none. Is there something else I need to do on my MongoDB or any command on the git?
Here is my code for my model
const mongoose = require("mongoose");
const orderSchema = new mongoose.Schema({
userId: {
type: String,
required: [true, "User ID is required!"]
},
products: [
{
productId: {
type: String,
required: [true, "Product ID is required!"]
},
quantity: {
type: Number,
required: [true, "Quantity is required!"],
min: 1,
default: 1
},
subtotal: {
type: Number,
required: [true, "Subtotal is required"],
default: 0
}
}
],
totalAmount: {
type: Number,
required: [true, "Total Amount is required!"],
default: 0
},
orderStatus: {
type: String,
required: [true, "Order Status is required!"],
default:"Pending"
},
purchasedOn: {
type: Date,
default: new Date
}
});
module.exports = mongoose.model("Order", orderSchema);
and a screenshot of my database
I will really appreciate your help! Thank you so much!

Your documents which were already created will not reflect your new schema field. Since those fields only will get reflected on the update only. So, add orderStatus as pending as the default. You must migrate those documents by the update method. This is going to be a one-time process.
So, in the terminal, you can write the following code and run
db.collection.update({}, {$set: {orderStatus: "Pending"}}, {upsert: true, muti: true})
Or you are comfortable with the code itself you can write your update code in your project itself like this
await orderModel.updateMany({}, {$set: {orderStatus: "Pending"}}, {upsert: true})
and make an API and call this code.
In this process, you will see that in your documents that new key called orderStatus with pending value has been created.

Related

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, mongoose, Schema structure. get a collection into a field of other collection

I have a schema "Questions" it has like a dozen of questions in it, I can add and delete those questions, I need this collection reflected in a field of other collection - "User" with one additional field (nested in options).
Question Schema:
var QuestionScema = new mongoose.Schema({
key: { type: String, required: true },
label: { type: String, required: true },
name: { type: String, required: true },
page: { type: String, required: true },
type: { type: String, required: true },
options: [{
key: {type: String, required: true},
value: {type: String, required: true}
}],
});
User Schema:
var UserSchema = new mongoose.Schema({
Name: { type: String, required: true },
Email: { type: String, required: true, unique: true },
Password: { type: String, required: true },
//this is where I need to reflect a Questions collection on each user,
//so that it will look something like this//
Questions: [{
key: {type: String, required: true},
//here can be all other fields from Questions collection, that is not a problem
options: [{
key: {type: String, reuired: true},
value: {type: String, reuired: true},
counter: {type: Number, default: 0} //this is the additional field
}]
}],
//
Notifications: [{
Title: { type: String },
Data: { type: String },
Created: { type: Date, default: Date.now }
}]
});
I can't figure out how to do that.
I have another collection of users, say User2 that will answer those questions from Questions collections and I need to keep track on Users schema (not User2, there I just save questions and answers) of how many times an option for that question is chosen.
A Questiuons entry can look like this:
{
key: Haveyouseenthismovie,
label: Have you seen this movie?,
name: Have you seen this movie?,
page: 1,
type: dropdown,
options: [{
key: yes,
value: yes
}, {
key: no,
value: no
}]
}
I want it to work like that (reflect a collection in field of each User) so I don't have to check if that question is in User collection if not add and if it is, is there an option that I need if it is than increment, if not than add that option (that user selected from options in that question in Questions schema) and increment. That looks like a bummer. So I figured that it will be better if that field will reflect a collection and I will just increment the option that I need on a question that I need.
Please help me figure that out, I don't have enough practise in mongo so I struggle with it sometimes :)
I don't think there is a way to reflect a collection in another document as the way you seem to wish it.
As I understand, the following options are available for you:
Embed the entire question document inside the User documents in User Collection.
Just maintain the '_id' of the question document in the User document in User Collection.
Please read on Data Modelling concepts & maintaining relationship between documents from Mongo DB Page https://docs.mongodb.com/manual/applications/data-models-relationships/

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

MongoDB database design for products and bundles

I am trying to build an e-commerce website based on Node.js with a mongoDB database and I am encountering problems about some database design or some logic I am missing
To sum up, I have Product that contain price, name, description etc... and Bundle that contains an array of products (by reference). The main problem come when I have to order, I can't get Product AND Bundle together ...
So I have already a Product schema :
const productSchema = new mongoose.Schema({
file: {
type: String,
required: true,
},
name: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
preparation: String,
allergics: {
type: Array,
required: true,
},
price: {
type: Number,
required: true,
},
// More fields
});
module.exports = mongoose.model('Product', productSchema);
And a Bundle schema that contains ref to Product (A bundle contains multiple products) :
const bundleSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
},
itemsId: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Product',
required: true,
}],
description: String,
reduction: {
type: Number,
min: 0,
default: 0,
max: 100,
},
});
module.exports = mongoose.model('Bundle', bundleSchema);
So when a user orders a bundle OR a single product, I use this schema :
const orderSchema = new mongoose.Schema({
orderedBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
articlesId: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Product',
},
],
itemsNumber: {
type: Array,
required: true,
},
amount: Number,
orderedAt: Date,
placeToShip: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Place',
},
});
module.exports = mongoose.model('Order', orderSchema);
As you can see, I only reference to Product , but I want to reference to Product AND Bundle , I don't know if this is possible, or if this is the wrong way to design the database like that.
Sorry if the post is a bit long, but I am trying to be as clear as possible! Thanks a lot.
if you want to reference product or bundle(depending on user buys bundle or single product) in articleId, you can do it like this:
Dont give ref in the articleId field of your orderSchema, just specify its type as ObjectId.
const orderSchema = new mongoose.Schema({
...
articlesId: [
{
type: mongoose.Schema.Types.ObjectId
},
],
...
});
And, while populating tell it which model to populate from.
//In case user bought a product
Order.find({findQuery})
.populate({path : '',model : 'Product'})
.exec(function(err,result){...});
//In case user bought a Bundle
Order.find({findQuery})
.populate({path : '',model : 'Bundle'})
.exec(function(err,result){...});
But, you must have a way to find out user bought a single product or a bundle.
Hope that helps you!

MongoDB reference using Mongoose

I'm trying to reference one model to other with Mongoose doing this:
//product model
var productSchema = new mongoose.Schema({
id: {type: Number, unique: true, required: 'product_id'},
sku: {type: String, unique: true, required: 'product_sku'},
name: {type: String, required: 'product_name'},
short_description: {type: String},
details: [{
type: String
}],
categories: [{
type: String
}, {min: 1, max: 5}],
});
module.exports = mongoose.model('products', productSchema);
//order model
var orderSchema = new mongoose.Schema({
id: {type: String, unique: true},
date: {type: Date, default: Date.now},
products_sold: {type: mongoose.Schema.Types.ObjectId, ref: 'products'},
});
module.exports = mongoose.model('orders', orderSchema);
I've already created a product with id: 1:
"_id": ObjectId('55a1ce54b058d142051ca61d'),
"id": 1,
"sku": "p0001",
"name": "test1",
"short_description": "test",
//etc
If I do:
var newOrder = new Order({
id: 'order001',
products_sold: 1,
//etc
});
newOrder.save(function(err, data) {
if(err){
console.log(err);
}else{
console.log(data);
}
});
It fails with error:
message: 'orders validation failed',
name: 'ValidationError',
errors:
{ products_sold:
{ [CastError: Cast to ObjectID failed for value "1" at path "products_sold"]
How can I have products_sold in my order filled with product data that has id 1?
Thanks in advance
You are specifying that the products_sold field is of type ObjectId, but you are trying to set a Number as its value. In order to store references to documents in other collections, you must store the _id of the referenced document, not an arbitrary id field that you have in the Schema. You will either need to change the type for _id in your products database to use integers (and then manually specify what that _id field's value is when creating a new product), or you need to store the product's _id value in the order document's product_sold field.
Response to comment:
Here is updated code for specifying your own unique _id field:
Product Model
var productSchema = new mongoose.Schema({
_id: {type: Number, unique: true, required: 'product_id'},
sku: {type: String, unique: true, required: 'product_sku'},
name: {type: String, required: 'product_name'},
short_description: {type: String},
details: [String],
categories: [{
type: String
min: 1,
max: 5
}],
}, {_id: false});
module.exports = mongoose.model('products', productSchema);
In the productSchema, you will see that there is an _id specified, and it is set to required, and has no default value. This should make it so that Mongo will use whatever Number you specify as it's _id.
You need to pass objectId of the product in product_sold , rather than number.

Resources