mongoose index don't create - node.js

I tried to create index from two fields in mongoose Schema but it didn't work
this two fields are the id of two other schema and i want to be unique
i get the ids from "mongoose.Schema.ObjectId"
this is my code:
const reviewSchema = new mongoose.Schema(
{
review: {
type: String,
required: [ true, 'Review can not be empty!' ],
},
rating: {
type: Number,
min: 1,
max: 5,
},
createdAt: {
type: Date,
default: Date.now,
},
tour: {
type: mongoose.Schema.ObjectId,
ref: 'Tour',
required: [ true, 'Review must belong to a tour.' ],
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [ true, 'Review must belong to a user' ],
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
},
);
reviewSchema.index({ tour: 1, user: 1 }, { unique: true });

I found it was a bug from atlas ,I don't know why but it couldnt create index with options
I create a local data base and now it works

Related

remove referencing objects on deletion from array in MongoDB

I have 2 schemas User and Restaurant, and user has an array of restaurants, am trying to reach when deleting the restaurant delete its reference from user automatically, am trying to reach it with the model.pre('remove')..but when I delete a restaurant the reference id it still exist in User.
Here is my User Schema:
const userSchema = new Schema(
{
email: {
type: String,
trim: true,
// required: true,
unique: true,
},
password: {
type: String,
// required: true,
min: 5,
},
stripeCustomerId: {
type: String,
// unique: true,
},
linkedAffiliateUser: {
type: String, //mongoose.Schema.Types.ObjectId,
},
restaurants: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Restaurant",
},
],
role: {
roleId: { type: Number, minlength: 1, maxlength: 1, required: true },
roleName: { type: String, trim: true, required: true },
},
// seperated schema
},
{ timestamps: true }
);
export default mongoose.model("User", userSchema);
and here is my Restaurant Schema:
const restaurantSchema = new Schema({
restaurantOwner: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
Name: {
type: String,
trim: true,
},
server: { type: mongoose.Schema.Types.ObjectId, ref: "ServerUser" },
restaurantLinkAccess: {
type: String,
required: true,
trim: true,
unique: true,
},
});
restaurantSchema.pre("remove", function (next) {
this.model("User")
.update(
{ restaurantSchema: this },
{ $pull: { comments: this._id } },
{ multi: true }
)
.exec(next);
});
export default mongoose.model("Restaurant", restaurantSchema);
I also tried to solve it like this:
restaurantSchema.pre("remove", function (next) {
this.model("User").remove({ $pull: { restaurants: this._id } }, next);
});
Please help.

Query MongoDB w/Mongoose on an Array of objects

I am having a rough time querying my collection of data, Im building a simple E-Comm platform and in that platform i want to be able to have categories for the products. the idea is simple, each category should be its own object in the database that has a collection of products, however each product can have multiple categories, this is for products that might fit into one or more category definitions.
here is the model for the categories
import mongoose from "mongoose";
const categorySchema = mongoose.Schema(
{
cat_name: {
type: String,
required: true,
unique: true,
},
cat_items: [
{
product: {
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
},
},
],
// Adds a relationship between a User admin and a product
// this is useful to see which admin, if multiple created a category
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
},
{ timestamps: true }
);
const Category = mongoose.model("Category", categorySchema);
export default Category;
Here is the Product model
const productSchema = mongoose.Schema(
{
// Adds a relationship between a User admin and a product
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
name: {
type: String,
required: true,
},
image: {
type: String,
required: true,
},
brand: {
type: String,
required: true,
},
category: {
type: String,
required: true,
},
categories: [
{
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
},
],
description: {
type: String,
required: true,
},
reviews: [reviewSchema],
rating: {
type: Number,
required: true,
default: 0,
},
numReviews: {
type: Number,
required: true,
default: 0,
},
price: {
type: Number,
required: true,
default: 0,
},
countInStock: {
type: Number,
required: true,
default: 0,
},
},
{
timestamps: true,
}
);
const Product = mongoose.model("Product", productSchema);
export default Product;
I believe the overall problem is the way i have the models setup, because any query i run on the categories object to return the products associated with it in cat_items, does not return any products it simply returns an empty array.
here is the simple function to return data
const products = await category.map((product)=> { return Product.findById(product._id)})
here im pulling out the array of category items, and mapping over them looking for the product in the database by the _id which is what product is example: 620e626203a59f0004e5a8c6 this in theory if you had 3 items, would return a new array of the product objects that i can send to the client, however every attempt only returns a [] and im pretty sure this is all do in part to the way i have the models setup.
for reference this is what returning a category looks like in postman:
{
"category": {
"_id": "62102f5c2b990d0e7c7d9bf8",
"cat_name": "Pendants",
"user": "620af3311fe4c247904b84d9",
"cat_items": [
{
"_id": "620e626203a59f0004e5a8c6"
},
{
"_id": "620e626203a59f0004e5a8c6"
}
],
"createdAt": "2022-02-18T23:44:28.697Z",
"updatedAt": "2022-02-18T23:54:23.103Z",
"__v": 2
},
"products": [
{},
{}
]
}
where im trying to fill the products array with the actual products stashed in the database

Mongoose: query to get nested multiple related data

I'm new to NoSQL, in this case is MongoDB. I'm trying to make an API using ExpressJS and Mongoose. that have some data models
User.js
const UserSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
min: 3,
max: 50,
unique: true,
},
email: {
type: String,
required: true,
max: 50,
unique: true,
},
password: {
type: String,
required: true,
min: 6,
},
profilePicture: {
type: Schema.Types.ObjectId,
ref: "Image",
},
}
Image.js
const ImageSchema = new mongoose.Schema(
{
desc: {
type: String,
},
url: {
type: String,
},
},
{ timestamps: true }
)
Post.js
const PostSchema = new mongoose.Schema(
{
username: {
type: String,
required: true,
},
title: {
type: String,
required: true,
},
desc: {
type: String,
max: 300,
required: true,
},
images: [
{
type: Schema.Types.ObjectId,
ref: "Image",
},
],
comments: [
{
type: Schema.Types.ObjectId,
ref: "Comment",
},
],
}
)
Comment.js
const CommentSchema = new mongoose.Schema(
{
user: {
type: Schema.Types.ObjectId,
ref: "User",
},
text: {
type: String,
required: true,
trim: true,
},
}
Now I want to perform a query that gets all comments based on specific postId. And the response data must include User data and image url that related to User. I tried this to get comment data
const comments = await Comment.find({post: req.params.postId}).populate("user")
It returned Comment with User data, but not include Image. How do I perform that query?
Try this (using the pupulate method with object configuration):
const comments = await Comment.find({post: req.params.postId}).populate({
path: 'user',
populate: {
path: 'profilePicture'
}
})
Here you can also select particular fields of each schema for example.
Let me know if it works for you.
Reference

Mongoose: How to populate across multiple levels and use field selection in the same time?

I have succesfully used population across multiple levels and field selection separately. But I couldn't find a way to do them both at the same time.
Population across multiple levels
Profile.findOne({ user: req.user.id })
.populate({
path: "user",
populate: { path: "following" },
})
Field selection:
Profile.findOne({ user: req.user.id })
.populate("user", ["name", "avatar"])
I want to do something like this:
NOTE: THIS DOESN'T WORK
Profile.findOne({ user: req.user.id })
.populate({
path: ["user", ["name", "avatar"]],
populate: { path: "following" },
This is the Profile model:
const ProfileSchema = new Schema({
//We want to associate the user with the profile
user: {
//This will associate the user by his id
type: Schema.Types.ObjectId,
ref: "users",
},
// We want a user friendly url
handle: {
type: String,
// required: true, // Even though we are doing pre-validatioon
max: 40,
},
bio: {
type: String,
},
school: {
type: String,
},
major: {
type: String,
},
website: {
type: String,
},
location: {
type: String,
},
// Optional (might add it later)
/**
* status: {
//where they are in their career
type: String,
required: true
},
*/
skills: {
// In the form they will put skill1,skill2,.. and we will turn that into an array
type: [String],
// required: true,
},
interests: {
// In the form they will put interest1,interest2,.. and we will turn that into an array
type: [String],
// required: true,
},
help_offers: {
// In the form they will put help_offer1,help_offer2,.. and we will turn that into an array
type: [String],
// required: true,
},
// Might be volunteering experience
experience: [
{
title: { type: String, required: true },
company: {
type: String,
required: true,
},
location: {
type: String,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
// Not required, going to be a checkbox
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
education: [
{
school: { type: String, required: true },
degree: {
type: String,
required: true,
},
fieldofstudy: {
type: String,
required: true,
},
from: {
type: Date,
required: true,
},
to: {
type: Date,
// Not required, going to be a checkbox
},
current: {
type: Boolean,
default: false,
},
description: {
type: String,
},
},
],
social: {
youtube: {
type: String,
},
twitter: {
type: String,
},
facebook: {
type: String,
},
linkedin: {
type: String,
},
instagram: {
type: String,
},
},
date: {
type: Date,
dafault: Date.now,
},
videoURL: {
type: String,
},
});
This is the User model:
const UserSchema = new Schema({
name: {
type: String,
required: true,
},
surname: {
type: String,
required: true,
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
avatar: {
type: String,
},
birthday: {
type: String,
required: true,
},
country: {
type: String,
required: true,
},
date: {
type: Date,
default: Date.now,
},
following: [
{
type: Schema.Types.ObjectId,
//The User has only ObjectId in uses array. ref helps us get full fields of User when we call populate() method.
//https://bezkoder.com/mongodb-many-to-many-mongoose/
ref: "users",
},
],
followers: [
{
type: Schema.Types.ObjectId,
ref: "users",
},
],
});
I am not sure if I understood your aim by this
Profile.findOne({ user: req.user.id })
.populate({
path: ["user", ["name", "avatar"]],
populate: { path: "following" },
But if what i understood is what you want, then we're lucky. here is what i got.
await RequestAproves.find()
.populate('user', 'firstName surname email roleId')
.populate({
path: 'request',
populate: [
{ path: 'budgetItem', select: 'code name' },
{ path: 'user', select: 'firstName surname' },
],
}
)
More details will come later, Sorry, I couldnt use your schema description, Incase it doesnt work out, let me know, ill try to use your schema.

Mongoose opposite populate an array

A list could have many items. I can easily retrieve all the items from a list but I'm having issues doing the opposite i.e retrieving all lists which contain an item
ItemSchema:
const ItemSchema = mongoose.Schema({
name: { type: String, required: true, min: 1 },
created_at: { type: Date, default: Date.now }
},{ toJSON: { virtuals: true }});
ListSchema:
const ListSchema = mongoose.Schema({
title: { type: String, required: true, max: 100 },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
description: { type: String, required: true },
items: [{
type: mongoose.Schema.Types.Mixed, ref: 'Item', quantity: 'String'
}],
completed: { type: Boolean, default: false },
date: { type: Date, default: Date.now },
});
Document:
"items": [
{
"_id": "5c6d74a98a3f532b4c1d2a23",
"quantity": "7"
}
],
How I populate: Item.findById(id).populate('lists'); but it returns empty array.
Any suggestions?
Haven't used MongoDB in a long time, but it doesn't seem you have "lists" key in your Item Schema to populate in the first place. Maybe you should add one or query the lists that have that item.

Resources