E11000 duplicate key error collection: Inserting document in mongodb - javascript

I am seeding my database for testing so I have inserted 15000 instructor data in database now for each instructor I want to insert 100 course. so I ran to for loop
first to get all instructor ids and second to store 100 course for that id of instructor but while inserting courses I get this type of error
E11000 duplicate key error collection: Courser.courses index: ratings.user_1 dup key: { : null }
Here is the code to enter course for each instructor
seedCourse: async (req, res, next) => {
try {
const instructors = await Instructor.find();
//const insrtuctor contains 15000 instructor
for(let oneInst of instructors) {
for(let i=0; i<=100; i++) {
const course = await new Course({
title: faker.lorem.sentence(),
description: faker.lorem.paragraph(),
author: oneInst._id,
prise: Math.floor(Math.random()*6 + 4),
isPublished: 'true',
tags: ["java", "Nodejs", "javascript"]
});
const result = await course.save();
await Instructor.findByIdAndUpdate(oneInst._id, { $push: { courses: result._id } });
console.log(`Instructor Id ${oneInst._id} Course added ${i}`);
}
}
} catch (error) {
next(error)
}
}
My course model definition looks something like this
const mongoose = require('mongoose');
const Course = mongoose.model('courses', new mongoose.Schema({
title: {
type: String,
required: true,
minlength: 3
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: 'instructor'
},
description: {
type: String,
required: true,
minlength: 5
},
ratings: [{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users',
required: true,
unique: true
},
rating: {
type: Number,
required: true,
min: 0,
max: 5
},
description: {
type: String,
required: true,
minlength: 5
}
}],
tags: [String],
rating: {
type: Number,
min: 0,
default: 0
},
ratedBy: {
type: Number,
min: 0,
default: 0
},
prise: {
type: Number,
required: function() { this.isPublished },
min: 0
},
isPublished: {
type: Boolean,
default: false
}
}));
module.exports = Course;

In your Course schema user in ratings array is an unique field. You are not giving any unique value while storing course in DB. First time it tool value as null but next time it is trying to save null value for user. Hence violating the schema.
Either remove unique:true or pass an unique value for user

Related

problem getting data in mongodb, get by category id

I'm trying to filter my pets by category, I have the following model of pets:
const Pet = mongoose.model(
'Pet',
new Schema({
name: {
type: String,
required: true,
},
age: {
type: Number,
required: true,
},
description: {
type: String,
},
weight: {
type: Number,
required: true,
},
color: {
type: String,
required: true,
},
images: {
type: Array,
required: true,
},
available: {
type: Boolean,
},
category: Object,
user: Object,
adopter: Object,
}, { timestamps: true }),
);
module.exports = Pet;
when I try to get the data through postman it returns an empty array as a response.
my code to filter by category:
static async getByCategory(req, res) {
const id = req.params.id;
// check if id is valid
if (!ObjectId.isValid(id)) {
res.status(422).json({ msg: 'Invalid ID' });
return;
}
const pets = await Pet.find({ 'category._id': id }).sort('-createdAt');
if (!pets) {
res.status(404).json({ msg: 'Pets not found!' });
return;
}
res.status(200).json({ pets });
}
it's my first time using mongodb so i'm not sure what's wrong.
id being passed from the client side is string and the one which is saved in the db is ObjectId. Convert the string to Mongoose ObjectId before Pet.find().
const id = mongoose.Types.ObjectId(req.params.id);
const pets = await Pet.find({ 'category._id': id }).sort('-createdAt');
Don't forget to import 'mongoose'.
Could you check that your MongoDB indeed has a field category._id?

why Virtual Populate not working on Node js and mongoose? Scenario : Product and Review by user

I have review and product model.If user give review on specific product(id) then it is stored in review model database but i donot like to store user review in product model database .so, i used virtual populate in product model instead of child referencing.After using virtual properties,if we use product id to see details,we can see review of user in json format but not saved in database.But the problem is my virtual properties (In Product Model) not working as it doesnt show review of user in json format when i send the request in that product id which already have review by user(stored in review model database).what is the problem here?
User Review on Product (id) stored in database
Sending Request of that product id to see review of user in json format using virtual properties(but no review found in json)
In Product Model
const productSchema = new Schema({
name: {
type: String,
required: true,
trim: true,
},
slug: {
type: String,
required: true,
unique: true,
},
price: {
type: String,
required: true,
},
quantity: {
type: Number,
required: true,
},
description: {
type: String,
required: true,
trim: true,
},
offer: {
type: Number,
},
discount: {
type: Number,
},
productPictures: [{
img: {
type: String,
},
}, ],
mainCategory: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
sub1Category: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
sub2Category: {
type: mongoose.Schema.Types.ObjectId,
ref: "category",
required: [true, "It is a required field"],
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "admin",
required: true,
},
vendor: {
type: mongoose.Schema.Types.ObjectId,
ref: "vendor",
},
createdAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
updatedAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
},
{
toJson: { virtuals: true },
toObject: { virtuals: true },
}
);
productSchema.virtual("reviews", {
ref: "review",
foreignField: "product",
localField: "_id",
// justOne: true
});
const Product = mongoose.model("product", productSchema);
module.exports = Product;
In Review Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const moment = require("moment");
const reviewSchema = new Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "user",
required: [true, "Review must belong to user"],
},
product: {
type: mongoose.Schema.Types.ObjectId,
ref: "product",
required: [true, "Review must belong to the product"],
},
review: {
type: String,
required: [true, "Review cannot be empty"],
},
rating: {
type: Number,
min: 1,
max: 5,
},
createdAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
updateddAt: {
type: String,
default: moment().format("DD/MM/YYYY") + ";" + moment().format("hh:mm:ss"),
},
}, {
toJson: { virtuals: true },
toObject: { virtuals: true },
});
// pre middleware and populating user and product(we can also do populate in getAllReview in controller)
reviewSchema.pre(/^find/, function(next) {
// ^find here is we use regex and can able to find,findOne ...etc
this.populate({
path: "product",
select: " _id name",
}).populate({
path: "user",
select: " _id fullName",
});
next()
});
const Review = mongoose.model("review", reviewSchema);
module.exports = Review;
In Review.js
const Review = require("../../models/Review.Models")
exports.createReview = async(req, res) => {
const review = await Review.create(req.body)
return res.status(201).json({
status: true,
review
})
}
exports.getAllReviews = async(req, res) => {
try {
const reviews = await Review.find()
return res.status(200).json({
status: true,
totalReviews: reviews.length,
reviews
})
} catch (error) {
return res.status(400).json({
status: false,
error
})
}}
In Product.js
const Product = require("../../models/Product.Models");
exports.getProductDetailsById = async(req, res) => {
try {
const { productId } = req.params;
// const { productId } = req.body;
if (productId) {
const products = await Product.findOne({ _id: productId })
.populate('reviews')
return res.status(200).json({
status: true,
products,
});
} else {
console.log("error display");
return res.status(400).json({
status: false,
error: "params required...",
});
}
} catch (error) {
return res.status(400).json({
status: false,
error: error,
});
}
try this in Product.js
try {
if (productId) {
const products = await Product.findOne({ _id: productId }).populate(
"reviews"
);
console.log(products);
if (products) {
return res.status(200).json({
status: true,
message: "Products is listed",
products,
reviw: products.reviews,
});
only need to add on response sending
return res.status(200).json({
status: true,
message: "Products is listed",
products,
reviw: products.reviews,
});

How to optimize performance with CREATE, PUT, and DELETE requests on MongoDB?

I have a database named "reviews" with a 9.7GB size. It has a collection name products. I was able to optimize the READ request using indexing technical by running the command db.products.ensureIndex({product_name: 1}); When I run the following command db.products.find({product_name:"nobis"}).explain("executionStats"); in MongoDB terminal, it shows that my execution time reduces from 28334ms to 3301ms.
I have the following 2 questions:
1) How do I use explain("executionStats"); on CREATE, PUT and DELETE requests? For example, I got this following error [thread1] TypeError: db.products.insert(...).explain is not a function when I tried to use the following insert function
db.products.insert({"product_id": 10000002,"product_name": "tissue","review": [{"review_id": 30000001,"user": {"user_id": 30000001,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000001,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}, {"review_photo_id": 60000002,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}, {"review_id": 30000002,"user": {"user_id": 30000002,"firstname": "Peter","lastname": "Chen","gender": "Male","nickname": "Superman","email": "hongkongbboy#gmail.com","password": "123"},"opinion": "It's good","text": "It's bad","rating_overall": 3,"doesRecommended": true,"rating_size": "a size too big","rating_width": "Slightly wide","rating_comfort": "Uncomfortable","rating_quality": "What I expected","isHelpful": 23,"isNotHelpful": 17,"created_at": "2007-10-19T09:03:29.967Z","review_photo_path": [{"review_photo_id": 60000003,"review_photo_url": "https://sdcuserphotos.s3.us-west-1.amazonaws.com/741.jpg"}]}]}).explain("executionStats");
2) Is there any performance Optimization method I can use for the CREATE, PUT and DELETE requests? For example, I am able to use POSTMAN to get the response time of a DELETE request, but the response time takes 38.73seconds.
const deleteReview = (request, response) => {
const id = parseInt(request.params.id);
Model.ProductModel.findOneAndDelete({ "review.review_id": id}, (error, results) => {
if (error) {
response.status(500).send(error);
} else {
response.status(200).send(results);
}
});
};
This is my MongoDB schema:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/reviews', { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true });
const Schema = mongoose.Schema;
const productSchema = new Schema({
product_id: { type: Number, required: true, unique: true },
product_name: { type: String, required: true, unique: true },
review: [{
review_id: { type: Number, required: true, unique: true },
user: {
user_id: { type: Number },
firstname: { type: String },
lastname: { type: String },
gender: { type: String, enum: ['Male', 'Female', 'Other'] },
nickname: { type: String },
email: { type: String, required: true },
password: { type: String, required: true },
},
opinion: { type: String, required: true },
text: { type: String },
rating_overall: { type: Number, min: 1, max: 5, required: true },
doesRecommended: { type: Boolean, required: true },
rating_size: { type: String, enum: ['a size too small', '1/2 a size too small', 'Perfect', '1/2 a size too big', 'a size too big'], required: true },
rating_width: { type: String, enum: ['Too narrow', 'Slightly narrow', 'Perfect', 'Slightly wide', 'Too wide'], required: true },
rating_comfort: { type: String, enum: ['Uncomfortable', 'Slightly uncomfortable', 'Ok', 'Comfortable', 'Perfect'], required: true },
rating_quality: { type: String, enum: ['Poor', 'Below average', 'What I expected', 'Pretty great', 'Perfect'], required: true },
isHelpful: { type: Number, required: true, default: 0 },
isNotHelpful: { type: Number, required: true, default: 0 },
created_at: { type: Date, required: true },
review_photo_path: [{
review_photo_id: { type: Number },
review_photo_url: { type: String }
}]
}]
});
const ProductModel = mongoose.model('product', productSchema);
module.exports = { ProductModel };
If you do not have one, ensure you have an index of review.review_id on your products collection. You're using that to look up what to delete so it should be indexed.
I read your deleteReview function as deleting the product document that contains the review, not just removing the individual review -- is that what you expect?
You should be able to just $pull the review from the reviews array to get rid of it.
You can use explain on an update like so:
db.products.explain().update({...}, {...});
See: https://docs.mongodb.com/manual/reference/method/db.collection.explain/
You can explain:
aggregate()
count()
find()
remove()
update()
distinct()
findAndModify()

Dynamically push, pull, and set on mongoose schema update

I am trying to setup my patch api so that I can create a dynamic query to push, pull, and set data in my mongoose schema. I have plenty of values that I would change using set, but I also have an array of objects which would require me to call push when I need to insert and pull when I need to remove an item. I'm trying to find the best way to combine this into a dynamic structure.
Schema:
const StepSchema = new Schema({
position: {
type: Number,
required: true
},
name: {
type: String,
required: true
},
due_date: {
type: Date
},
status: [{
label: {
type: String,
enum: ['Inactive', 'In Progress', 'Flagged', 'Complete'],
default: 'Inactive'
},
user: {
type: Schema.Types.ObjectId,
ref: 'users',
},
date: {
type: Date
}
}],
comments: [{
user: {
type: Schema.Types.ObjectId,
ref: 'users',
required: true
},
body: {
type: String,
required: true
},
date: {
type: Date,
required: true
},
}],
});
Api:
router.patch('/',
async (req, res) => {
let setQuery = req.body;
let pushQuery = {};
let pullQuery = {};
//remove id from set query
delete setQuery.id;
//if there is a comment
if(req.body.comment){
pushQuery.comments = req.body.comment
}
//if I need to remove a comment
if(req.body.remove_comment){
pullQuery.comments = {_id: req.body.remove_comment.id}
}
//Push new status into array
if(req.body.status) {
pushQuery.status = {
label: req.body.status,
user: req.user._id,
date: new Date()
};
delete setQuery.status;
}
//update step
await Step.findByIdAndUpdate(req.body.id, {$set: setQuery, $push: pushQuery, $pull: pushQuery})
.then(step => {
if(!step){
errors.noflow = "There was a problem updating the step";
return res.status(400).json(errors);
}
res.json(step)
})
.catch(err => {
console.log(err);
res.status(404).json(err);
});
});
I've been getting the following error when trying to push a new status into my document:
operationTime: Timestamp { bsontype: 'Timestamp', low: 1, high_:
1560978288 }, ok: 0, errmsg: 'Updating the path \'status\' would
create a conflict at \'status\'', code: 40, codeName:
'ConflictingUpdateOperators', '$clusterTime': { clusterTime:
Timestamp { bsontype: 'Timestamp', low: 1, high_: 1560978288 },
signature: { hash: [Object], keyId: [Object] } },
Oh, you're doing that $set and $push on a status. Your pushQuery is trying to have status be an array on the document, and your setQuery wants to set it to whatever it was on the actual body (I'm guessing the same object.
A quickfix would be to remove it from the set object:
delete setQuery.status
A reasonable and stable way to do this would be to actually only take the things from req.body which you really want for each of the stages. Example:
const { position, name, dueDate, status, comment, remove_comment } = req.body;
const setQuery = { position, name, dueDate };
const pushQuery = { status, comments: comment };
// ...
That way your queries are not conflicting in any way.

Mongoose $inc operation error on a nested document

I have the following schema:
const postsStatsSchema = new Schema({
postid: {
type: String,
maxlength: [128, "post ID must be at most 128"],
required: true
},
likecounter: {
type: Number,
default: 0,
min: 0,
required: true
}
});
const userStatsSchema = new Schema({
username: {
type: String,
required: true,
maxlength: [50, "Name length must be at most 50"]
},
posts: {
type: [postsStatsSchema],
required: true
}
});
const statisticsSchema = new Schema({
month: {
type: Number,
required: true
},
users: {
type: [userStatsSchema],
required: true
}
}, {
timestamps: true
});
I'm trying to increment (or create, if the document does not exist) the 'likecounter' in the postsStats object. I tried many different ways, but non were successful. This is the last thing I tried:
let update = {};
update['$inc'] = {};
update['$inc'] = {'users.$.username.user.$.posts.somepost.likecounter': 1000};
try {
const res = await stats.findOneAndUpdate({month: 100}, update, {'upsert': true});
console.log(res);
} catch (err) {
console.log(err);
}
The error I'm getting on the code above is:
MongoError: Too many positional (i.e. '$') elements found in path
I tried many variations, with and without the '$' sign, but only managed to increment the month. I believe it is something with the nested document that Im doing wrong but just can't figure out what it is.

Categories