Mongoose Nested Schema - required fields inside array field - mongodb

I have following mongoose schema -
const UserSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
email: {type: String, required: true, trim: true},
password: {type: String, required: true, trim: true},
addresses: [{
type: {type: String, required: true, trim: true},
pinCode: {type: String, required: true, trim: true},
city: {type: String, required: true, trim: true},
state: {type: String, required: true, trim: true},
landmark: {type: String, required: false, trim: true},
}]
})
only name, email and password are required for registration. User can have more than one address , and each address has some required fields like in flipkart.com/account/addresses user's account page.
Each address will be stored in addresses array field in mongodb database.
I want to keep addresses array [ ] when user register. address are only provided through user's account page web page.
But I am getting ValidatorError because schema of doc inside addresses array having required fields.
when user register -
{
name: 'rahul',
email: 'rahul#example.com',
password: 'pass123',
addresses: []
}
user is already registered, and then add address
{
name: 'rahul',
email: 'rahul#example.com',
password: 'pass123',
addresses: [
{
type: 'home',
pinCode: '111222',
city: 'city1',
state: 'state1'
},
{
type: 'work',
pinCode: '333444',
city: 'city2',
state: 'state2',
landmark: 'landmark2'
}
]
}
How to achieve above ??
Any help would be appreciated.

You just have to remove required:true field from address array not even required:false.
Just type and trim will the job and required is false by default so you dont have to include it.

I was doing this -
const userDoc = new UserModel({
name: nm,
email: em,
password: pass,
addresses: []
})
Not passing addresses: [], prevent error ValidatorError
const userDoc = new UserModel({
name: nm,
email: em,
password: pass
})

Try the below code. Hope this will help you.
const ToySchema = new mongoose.Schema({ name: String });
const ToyBoxSchema = new mongoose.Schema({
toys: [ToySchema],
bucketName: String,
numToys: Number
});
const ToyBox = mongoose.model('ToyBox', ToyBoxSchema);
var toyBoxData = {
toys : [
{ name : "Dummy Toy 1"},
{ name : "Dummy Toy 2"},
{ name : "Dummy Toy 3"},
],
bucketName: "My Toy Bucket",
numToys: 3
}
var toyBucket = new ToyBox(toyBoxData);
toyBucket.save( (err: any)=> {
console.log(err);
})
And when you will run the above code, you will be able to see a collection and document like this in your DB, as in the below screenshot.
Hope this will help you. For more information please visit this mongoose documentation link.

Related

How to create an array of objects with default values?

how to create an array of objects with default values?
const schema = new mongoose.Schema({
login: {type: String, unique: true, required: true},
password: {type: String, required: true},
chatlist: [
{
channelname: {type: String, default: "frontend"},
channelid: {type: String, default: "619a6bfe5b0931f1e5dbaf2c"}
},
{
channelname: {type: String, default: "backend"},
channelid: {type: String, default: "619a71002954ba23a951bb8f"}
},
{
channelname: {type: String, default: "devops"},
channelid: {type: String, default: "619d69c190a85a40893b6522"}
},
]
})
this code above does not work
when a new user register i want to add a default chats to his profile
next thing is the user must be able to add/remove chats from the database
how should i do this? do the chats need to be an objects or documents?
const schema = new mongoose.Schema({
chatlist: {
type: Array,
defualt: []
}
})
so to have a structure in the array I personally would make the request correct using error handling.
for more information visit this site
didnt find any good solution so i just did this
const schema = new mongoose.Schema({
login: {type: String, unique: true, required: true},
password: {type: String, required: true},
chatlist: [{
channelname: String,
channelid: String
}]
})
const user = await MONGO_user.create({
login: login,
password: hashPassword,
})
user.chatlist.push(
{
channelname: "frontend",
channelid: "619a6bfe5b0931f1e5dbaf2c"
}, {
channelname: "backend",
channelid: "619a71002954ba23a951bb8f"
}, {
channelname: "devops",
channelid: "619d69c190a85a40893b6522"
})
await user.save()

How to Populate CartItem schema product details into Order Schema using mongoose

I want to populate the details of products in my order. Currently it is only adding product id inside the products array. I tried a couple of methods but none seems to work.
import mongoose from 'mongoose'
const CartItemSchema = new mongoose.Schema({
product: {type: mongoose.Schema.ObjectId, ref: 'Product'},
quantity: Number,
shop: {type: mongoose.Schema.ObjectId, ref: 'Shop'},
status: {type: String,
default: 'Not processed',
enum: ['Not processed' , 'Processing', 'Shipped', 'Delivered', 'Cancelled']}
})
const CartItem = mongoose.model('CartItem', CartItemSchema)
const OrderSchema = new mongoose.Schema({
products: [CartItemSchema],
customer_name: {
type: String,
trim: true,
required: 'Name is required'
},
customer_email: {
type: String,
trim: true,
match: [/.+\#.+\..+/, 'Please fill a valid email address'],
required: 'Email is required'
},
delivery_address: {
street: {type: String, required: 'Street is required'},
city: {type: String, required: 'City is required'},
state: {type: String},
zipcode: {type: String, required: 'Zip Code is required'},
country: {type: String, required: 'Country is required'}
},
payment_id: {},
updated: Date,
created: {
type: Date,
default: Date.now
},
user: {type: mongoose.Schema.ObjectId, ref: 'User'}
})
const Order = mongoose.model('Order', OrderSchema)
export {Order, CartItem}
I tried doing this:
const create = async (req, res) => {
try {
req.body.order.user = req.profile;
console.log(req);
let order = new Order(req.body.order);
let neworder = await Order.findById(order._id)
.populate("products.product", "name price")
.populate("products.shop", "name")
.exec();
// console.log(order);
let result = await order.save();
sendMail(order);
res.status(200).json(result);
} catch (err) {
return res.status(400).json({
error: errorHandler.getErrorMessage(err),
});
}
};
Also tried using this for poulating the product details but doesnt seem to work!
Order.findById(order._id).populate({ path: "products.product", select: "_id name price" })

In MongoDb, are sub documents references or extensions

Using mongoose you can create a sub document as such:
export const UserSchema = new Schema({
email: {
type: String,
unique: true,
required: "email is required.",
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
},
firstName: {
type: String,
required: "firstName is required."
},
lastName: {
type: String,
required: "lastName is required."
},
dateOfBirth: {
type: Date,
required: "dateOfBirth is required."
},
roles: [{
role: String,
required: "role is required",
validate: isValidUserRole
}],
address: AddressSchema,
});
Address Schema
export const AddressSchema = new Schema({
streetAddress: {
type: String
},
addressLine2: {
type: String
},
city: {
type: String
},
state: {
type: String
},
zipCode: {
type: String
},
country: {
type: String
}
});
Whenever I save a user, will mongoose create a new user document with nested address data or a user and address and then reference that address document within the user instance?
How would I approach the case of 2 users sharing the same address?
Thanks!
You need not to provide the AddressSchema in the UserSchema schema.
You can just provide reference of AddressSchema like the following.
export const UserSchema = new Schema({
email: {
type: String,
unique: true,
required: "email is required.",
match: [/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{2,3})+$/, 'Please fill a valid email address']
},
firstName: {
type: String,
required: "firstName is required."
},
lastName: {
type: String,
required: "lastName is required."
},
dateOfBirth: {
type: Date,
required: "dateOfBirth is required."
},
roles: [{
role: String,
required: "role is required",
validate: isValidUserRole
}],
address: {type:mongoose.Schema.Types.ObjectId,ref:'AddressSchema',required:true}
});
But remember that in order to make this work, 1st you need to create an Address data and provide their _id in UserSchema.
What happens by the above method is,
AddressSchema's ObjectId(_id) works as the REFERENCE in UserSchema. Basically, it acts as a REFERENCE DOCUMENT in UserSchema
You can find this out by querying your data through mongo shell.
When a document contains an embedded document (this is indistinguishable from nested hashes in the shell), the embedded document wholly belongs to the top level document and cannot be included-by-reference into another document.

MongoDB: How to find the relationships between collections in database

I have a collection of user which has id, firstName, lastName. id field of user collection has been used in another collection.
Is there any way to find all collection which used user id?
user schema:
let userSchema = new mongoose.Schema({
firstName: {
type: String,
trim: true,
required: true
},
lastName: {
type: String,
trim: true,
required: true
}
},
{
timestamps: true,
usePushEach: true
});
training schema:
var trainingSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
name: {type: String, required: true},
institution: {
instituteName: {type: String, required: true},
type: {type: String, required: true},
address: {
country: String,
state: String,
city: String
}
},
startDate: Date,
endDate: Date,
achievements: String,
createdAt: Date,
updatedAt: Date,
endorsers: [{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
profilePic: {
container: {type: String,default: null},
name: { type: String, default: null }
},
currentPosition: {type: String,default: ""},
currentWorkingCompany: {type: String,default: ""}
}],
});
In above schema userId come from user collection
Note: similar to this which is available in MYSQL:
SELECT
ke.referenced_table_name 'parent table',
ke.referenced_column_name 'parent column',
ke.table_name 'child table',
ke.column_name 'child column',
ke.constraint_name
FROM
information_schema.KEY_COLUMN_USAGE ke
WHERE
ke.referenced_table_name IS NOT NULL
AND table_schema = 'your_db_name'
ORDER BY ke.referenced_table_name;
Source: here
You probably need MySQL function named join. In MongoDB it is named $lookup and it is part of aggregate function.

Mongoose delete nested subdocuments and documents

I have:
let userSchema = mongoose.Schema({
email: {type: String, required: true, unique: true},
passwordHash: {type: String, required: true},
fullName: {type: String, required: true},
salt: {type: String, required: true},
ads: [{type: ObjectId, ref: 'Ad'}],
roles: [{type: String}]
}
let adSchema = mongoose.Schema({
author: {type: ObjectId, ref: 'User'},
title: {type: String, required: true},
category: {type: ObjectId, ref: 'Category', required: true},
town: {type: ObjectId, ref: 'Town', required: true},
}
);
let categorySchema = mongoose.Schema({
name: {type: String, required: true, unique: true},
ads: [{type: ObjectId, ref: 'Ad'}]
}
);
let townSchema = mongoose.Schema({
name: {type: String, required: true, unique: true},
ads: [{type: ObjectId, ref: 'Ad'}]
}
);
I want to find for example town by id and remove all ads in it(and ofcourse to remove the ads from their categories and authors).How can i do that?
I would suggest bulk getting the array of object Ids and using it like this:
Ad.remove({_id: {$in: Ad_ids_array}}, function(){...}); // and so on
You can add a pre-remove hook in the ad schema definition like this:
adSchema.pre('remove', function(next) {
let lethis = this;
// Pull ad out of all the Category docs that reference the removed ad.
this.model('Category').update({}, { $pull: {ads: lethis._id}}, { safe: true }, next);
// Pull ad out of all the User docs that reference the removed ad.
this.model('User').update({}, { $pull: {ads: lethis._id}}, { safe: true }, next);
});
This will remove the ad from the categories and users that have it in their ads array.