Mongoose unique property still allowing me to save to db - node.js

Im under the assumption that adding unique: true to a field would stop from saving to the database using the same value. But im still allowed to do it.
"mongoose": "^5.4.19",
const SessionSchema = new Schema({
jobId: {
type: String,
required: false,
unique: true,
index: true,
},
productId: {
type: String,
required: true,
},
status: {
type: String,
default: "Pending",
},
mode: {
type: String,
default: "authentication",
},
result: {
type: Schema.Types.Mixed,
},
requests: [RequestSchema],
callback_at: {
type: Date,
},
}, {
timestamps: { createdAt: "created_at", updatedAt: "updated_at" },
});
I have already tried deleting and recreating the collection. See the image below i can create new session with the same jobId being 1.
public store = async (req: Request, res: Response): Promise<any> => {
const input = req.body;
let session = new Session({
productId: input.productId,
jobId: input.jobId,
});
try {
session = await session.save();
const response = {
success: true,
status: 201,
data: { session },
message: "SESSION CREATED",
};
return res.status(response.status).json(response);
} catch (err) {
const response = {
success: false,
status: 500,
errors: [],
message: "UNEXPECTED SESSION ERROR",
};
if (err.code === 11000) {
response.errors.push({
code: 11000,
message: "Duplicate key error jobId",
});
}
return res.status(response.status).json(response);
}
db.sessions.getIndex();
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "myDB.sessions"
}
]

You have to understand that unique is an index configuration option in your schema.
For instance, if the users collection doesn't have a unique index on userName, then you need to wait for the index to build before you start relying on it.
const user = new mongoose.Schema({
userName: { type: 'String', unique: true },
});
const User = db.model('User', user);
const doc = new User({
userName: 'Bob'
});
return User.init() // `User.init()` returns a promise that is fulfilled when all indexes are done
.then(() => User.create(doc))
.then(() => User.create({ userName: 'Bob' }));
}

I was not using unique properly: https://mongoosejs.com/docs/validation.html#the-unique-option-is-not-a-validator
Need to wait until the indexes are built before relying on unique to be a validator.
I changed my mongoose connect options to look like the following
options: {
useNewUrlParser: true,
useCreateIndex: true,
autoIndex: true,
},
I;m not sure if its the most appropriate solution, but its the one ive gone with for now.

Related

problem when creating one to many data in mongoose

so i'm creating CRUD with relation of two collections, then i got problem, i can't do push from first collection data to second collection. this is my code.
Schema
const CourseSchema = new Schema(
{
title: {
type: String,
required: true,
},
desc: String,
price: Number,
video: String,
category: String,
status: Number,
lessons: [
{
type: Schema.Types.ObjectId,
ref: "Lessons",
},
],
},
{
timestamps: true,
}
);
const course = mongoose.model("Courses", CourseSchema);
const LessonSchema = new Schema(
{
title: {
type: String,
required: true,
},
desc: String,
video: String,
status: Number,
},
{ timestamps: true }
);
const Lessons = mongoose.model("Lessons", LessonSchema);
code to do data push
Lessons.create(req.body)
.then((data) => {
res.status(200).send({
status: 200,
message: "Successfully Create Lessons",
data: data,
});
Course.findByIdAndUpdate(
courseId,
{ $push: { lessons: data._id } },
{ safe: true, upsert: true, new: true }
);
})
is there any solution for my problem? please help me, i'm just learning about one to many relation in nodejs using mongoose
You need a callback function to make it works.
Lessons.create(req.body)
.then((data) => {
res.status(200).send({
status: 200,
message: "Successfully Create Lessons",
data: data,
});
course.findByIdAndUpdate(
courseId,
{ $push: { lessons: data } },
{ safe: true, upsert: true, new: true },
function (err, newdoc) { // callback function
if (err) {
console.log(err);
} else {
console.log("completed");
}
}
);
})

StrictModeError on using upsert with updateOne in Mongoose

I want to add a list of users in DB if they do not exist in DB and skip for the ones which already exists, for this, I am using writeMany operation with updateOne and upsert set as true. But it gives me StrictModeError.
Here is the function which creates the insertMany
const bulkWriteData = blacklistData.map(value => {
return {
updateOne : {
upsert: true,
filter : {
email : value
},
update : {
$set : {
_id : mongoose.Types.ObjectId(),
status : STATUS.BLOCKED,
createdAt : new Date(),
updatedAt : new Date()
}
}
}
}
})
await EmailUsers.bulkWrite(bulkWriteData)
Here is the EmailUsers Model
const mongoose = require('../config/connection')
const Schema = mongoose.Schema
const validator = require('../utils/validator.util')
const uniqueValidator = require('mongoose-unique-validator')
const EmailUsers = new Schema({
email: {
type: String,
required: true,
validate: validator.mongooseEmailValidator,
unique: true,
},
status: {
type: String,
enum: ['active', 'bounced'],
required: true,
default: 'active',
},
createdAt: {
type: Date,
required: true,
default: () => new Date(),
},
updatedAt: {
type: Date,
required: true,
default: () => new Date(),
},
bounceResponse: [
{
type: Object,
required: false,
},
],
})
EmailUsers.index({ email: 1, status: 1 })
EmailUsers.plugin(uniqueValidator, { type: 'mongoose-unique-validator' })
module.exports = mongoose.model('email_users', EmailUsers)
This is the validator used for email
const mongooseEmailValidator = validate({
validator: 'matches',
arguments: constants.regex.email,
message: 'Email should be a valid email address',
})
Here is the array which was sent to bulkwrite
[{
updateOne : {
upsert : true,
filter : { email: 'test#gmail.com' },
update : {
'$set': {
_id: 607ec7fd009fc824c5c34b5d,
status: 'blocked',
createdAt: 2021-04-20T12:24:29.693Z,
updatedAt: 2021-04-20T12:24:29.693Z
}
}
}
}]
Here is the error
error: Path "email" is not in schema, strict mode is `true`, and upsert is `true`. {"trace":"StrictModeError: Path \"email\" is not in schema, strict mode is `true`, and upsert is `true`.\n at cast
This doesn't make sense as I do have email in the schema
This actually updates the DB with the data and then throws the error.
What could be the possible solution?

Is there a way i could keep track of the Time and the entity that was changed in a model

Basically I'm trying to get the time and the entity changed in a particular model when ever the update method is called.
This is my model I want to keep track of:
const mongoose = require("mongoose");
const modelSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
name: {
type: String,
required: true,
},
note1: String,
note2: String,
note3: String,
images: {
type: Array,
required: true
},
status: {
enum: ['draft', 'pending_quote', 'pendong_payment', 'in_production', 'in_repair', 'pemding_my_review', 'fulfilled'],
type: String,
default: "draft"
},
price: {
type: mongoose.Schema.Types.ObjectId,
ref: "Price",
}
}, {
timestamps: true,
})
module.exports = mongoose.model("Model", modelSchema)
And this is the method I call to update the status:
exports.updateModel = async (req, res) => {
try {
let id = req.params.id;
let response = await Model.findByIdAndUpdate(id, req.body, {
new: true
})
res.status(200).json({
status: "Success",
data: response
})
} catch (err) {
res.status(500).json({
error: err,
msg: "Something Went Wrong"
})
}
}
you can add a new field in your schema like:
logs:[{
entity: String,
timeStamp: Date
}]
Then updating it basing on your current code:
let id = req.params.id;
// I don't know whats in the req.body but assuming that it
// has the correct structure when passed from the front end
let response = await Model.findByIdAndUpdate(id,
{
$set:req.body,
$push:{logs:{entity:'your entity name here',timeStamp:new Date()}}
}, {
new: true
})

Mongoose: How to unselect a field & still use it to calculate a virtual

So I'm learning mongodb & mongoose, and I'm trying to make a projects/tasks app
this is the ProjectModel:
const projectSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
},
grp: {
type: String,
required: true,
trim: true,
lowercase: true,
},
tasks: {
type: [taskSchema],
},
createdAt: { type: Date, default: Date.now },
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true },
}
);
projectSchema.virtual('tasksCount').get(function () {
return this.tasks.length;
});
And when requesting the data, this runs :
getAllProjects = async (req, res) => {
try {
const query = await Project.find().select({tasks: -1});
const projects = query.;
console.log(projects);
res.status(200).send({
status: 'sucess',
data: {
projects,
},
});
} catch (error) {
res.status(400).send({
status: 'fail',
msg: `error info : ${error}`,
});
}
};
The problem is : I get this error: TypeError: Cannot read property 'length' of undefined.
Its coming from the virtual property ('tasksCount'), So it seems when I unselect the ('tasks') property the virtual one can't be calculated, I hope someone have a way to unselect 'tasks' and still be able to send 'tasksCount'.
You can follow this code
const query = await Project.find().select("-tasks");

findOneAndUpdate seems to be doubling my $inc count

I'm trying to post a comment on to my posts for my MERN app but I'm running into an issue where the comment (Posts.findOneAndUpdate) seems to posting the comments twice. I read a few posts on SO that described the issue to be the way mongoose handles queries but I must be missing something.
If anyone could explain what I'm doing wrong I would greatly appreciate it!
Route I'm using:
router.post('/newReply/:id', async function(req, res) {
const body = req.body
if (!body) {
return res.status(400).json({
success: false,
error: 'No text entered!',
})
}
const reply = new Replies(body)
if (!reply) {
return res.status(400).json({ success: false, error: err })
}
await Posts.findOneAndUpdate(
{ _id: req.params.id },
{
"$inc": { "replies": 1 },
"$push": { "comments": reply },
},
{
new: true
},
(err) => {
if (err) {
return res.status(404).json({
success: false,
error: err,
message: 'Post not found!',
})
}
return res.status(200).json({
success: true,
id: reply._id,
message: 'Reply created!',
reply: reply.reply,
points: reply.points,
createdAt: reply.createdAt
})
})
.catch(err => console.log(err))
})
Posts Model
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// Create Schema
const PostsSchema = new Schema({
post: {
type: String,
required: true
},
points: {
type: Number,
default: 0
},
voters: {
type: Array
},
upvotedBy: {
type: Array
},
downvotedBy: {
type: Array
},
createdAt: {
type: Date,
default: Date.now
},
replies: {
type: Number,
default: 0
},
comments: {
type: Array
},
user_id: {
type: 'string'
},
deleted: {
type: Boolean,
default: false
}
});
module.exports = Posts = mongoose.model("posts", PostsSchema);

Resources