Not able to add default values to the object in array - javascript

I am trying to add the fancyItem to the below model. Whenever I try to add the next fancyITem I get below mongo db error:
MongoError: E11000 duplicate key error collection: natours.fancyitems index: books.name_1 dup key: { : null }\n
To avoid this I am trying to add the default values to the objects so that I don't get this duplicate error.
Kindly suggest better way to handle this issue!
const mongoose = require("mongoose");
const fancyItemSchema = new mongoose.Schema({
firstName: {
type: String,
required: [true, "Please enter fancyItem's first name"]
},
lastName: {
type: String
},
genre: {
type: String,
enum: [
"guidingLights",
"luminaries",
"mavericScientists",
"menOfLetters",
"theGrandPhilosophers",
"architectsOfTheFuture"
],
required: true
},
notableWork: {
type: String,
required: [true, "Please enter notable work"]
},
quotes: [
{
quote: {
type: String,
default: "There is no quote"
}
}
],
books: [
{
bookName: {
type: String,
sparse: true,
default: "There is no quote"
},
bookURL: {
type: String,
sparse: true,
default: "There is no quote"
}
}
],
videos: [
{
videoName: {
type: String,
maxlength: [
50,
"A video description must have less or equal to 50 characters"
],
sparse: true,
default: "There is no quote"
},
videoURL: {
type: String,
sparse: true,
default: "There is no quote"
}
}
],
courses: [
{
courseName: {
type: String,
sparse: true,
default: "There is no quote"
},
courseURL: {
type: String,
sparse: true,
default: "There is no quote"
},
platform: {
type: String,
sparse: true,
default: "There is no quote"
}
}
]
});
const FancyItem = mongoose.model("FancyItem", fancyItemSchema);
module.exports = FancyItem;

Error you have provided says that there's already a record with null as the name. In other words, you already have a book without a name.
The relevant documentation for this:
If a document does not have a value for the indexed field in a unique
index, the index will store a null value for this document. Because of
the unique constraint, MongoDB will only permit one document that
lacks the indexed field. If there is more than one document without a
value for the indexed field or is missing the indexed field, the index
build will fail with a duplicate key error.
You can combine the unique constraint with the sparse index to filter
these null values from the unique index and avoid the error.
Unique Indexes
Sparse indexes only contain entries for documents that have the
indexed field, even if the index field contains a null value.
Sparse Indexes

Related

Mongoose populate returns an empty array | multiple levels of embedded documents

I am trying to populate my ChatRoom model with the User reference. However, it returns a ChatRoom object with only _ids where I expected usernames, as if I never applied populate on it.
Here is an extract of my ChatRoom model :
const ChatRoom = mongoose.model("ChatRoom", {
sender: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
roomname: { type: String, default: "new room" },
messages: [
{
messages: {
type: mongoose.Schema.Types.ObjectId,
ref: "Message",
},
meta: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
},
delivered: Boolean,
read: Boolean,
},
],
},
],
participants: [
{
user: { type: mongoose.Schema.Types.ObjectId, ref: "User" },
},
],
isPrivate: { type: Boolean, default: "false" },
});
My User model :
const User = mongoose.model("User", {
username: { required: true, unique: true, type: String },
avatar: Object,
token: String,
hash: String,
salt: String,
chatroom: {
type: mongoose.Schema.Types.ObjectId,
ref: "ChatRoom",
},
});
As this seems to be a recurrent issue, I tested several StackOverflow answers for my populate code :
Using populate('participants.user') and 'model: user' or just populate('participants.user'), same solution here:
const chatroom = await ChatRoom.findById(req.params.id)
.populate([
{
path: "participants.user",
model: "User",
},
])
.exec((err, user) => {
if (err) {
console.log("error", err);
} else {
console.log("Populated User " + user);
}
});
The console.log returns :
Populated User { _id: new ObjectId("62262b342e28298eb438d9eb"),
sender: new ObjectId("6225d86c9340237fe2a3f067"), roomname:
'Hosmeade', participants: [ { _id: new
ObjectId("6225d86c9340237fe2a3f067") } ], isPrivate: false,
messages: [], __v: 0 }
As if no populate method was ever applied. On the client side, I get an empty string.
Checking my documents aren't empty, this link mentions that Mongoose get problems with detecting referring model across multiple files but the solution doesn't work for me :
_id:6225d86c9340237fe2a3f067 username:"Berlioz" token:"rTCLAiU7jq3Smi3B"
hash:"wvJdqq25jYSaJjfiHAV4YRn/Yub+s1KHXzGrkDpaPus="
salt:"hZdiqIQQXGM1ryYK" avatar:Object
__v:0
If I remove the .exec(...) part, I get the info on the client side, but participants is still filled with only id :
chatroom response : Object { _id: "62262bb14e66d86fb8a041e8",
sender: "6225d86c9340237fe2a3f067", roomname: "Very secret room",
participants: (1) […], isPrivate: false, messages: [], __v: 0 }
I also tried with select: 'username' and get the same result as above :
const chatroom = await ChatRoom.findById(req.params.id).populate({
path: "participants.user",
select: "username",
});
Populating it "as an array"
Changing type of participants.user in my ChatRoom model into an Object (nothing changes)
If needed hereafter are my repos:
Client side and Backend
I run out of ideas on how to debbug my code. Any help would be great !

How to insert ref objectId in nodejs?

Main Model
{
name: {
type: String,
trim: true,
required: true
},
carModels: [{
type: ObjectId,
ref: 'CarModel'
}]
}
Second Model
{
name: {
type: String,
trim: true,
required: true
},
carModels: [
{
type: ObjectId,
ref: 'CarModel'
}
]
},
Third Model
{
name: {
type: String,
trim: true,
required: true
}
},
Here i am trying to insert the data like this
{
"name": "test",
"phoneNumber": "0123456789",
"email": "m#m.com",
"carMakes": [{
"name": "BMW",
"carModels": [{
"_id": "some id"
}]
}]
}
and it giving me error like
carMakes.0: Cast to [ObjectId] failed for value
here is the create function
export const create = async data => {
const result = await Booking(data).save();
return result;
};
Can anyone tell what I am missing here ..i am learning nodejs
i think the problem is with the _id that you're passing to carModel and since you set the type to ObjectId it has to be in a valid format "either 12 byte binary string, or a 24 hex byte string" and "some id" is not the valid one if you're sending that.
you can check if your id is valid with isValidObjectId() function.
or you can easily generate an ObjectId:
var mongoose = require('mongoose');
var id = new mongoose.Types.ObjectId();

mongoose add more key:value inside ref field

organs: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Organ',
healthValue: { // i want to add this field but it is becoming invalid, not generating auto with default val
type: Number,
default: 0,
}
},
],
I have a user schema and also there is organs key in this schema. I'm keeping organs with ref way to get belongs to user organs. I also should keep organ health value but I can not keep it in ref field together as above. How can i do this? Can not I add more key:value to populate (type & ref) fields?
organs: [
{
organId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Organ',
},
healthValue: {
type: Number,
default: 0,
},
isOwned: {
type: Boolean,
default: false,
},
},
],
i think i solve with above code block, i did use wrong syntax

MongooseJS - find() $gt condition case insensitive (Previous title: condition on string not working as expected )

I'm trying to query a collection containing user data, imposing $gt condition on a string field, but when I query it return an empty result, while using an empty $gt condition, as {name: {$gt:''}} it returns the whole dataset, as i would expect.
The model schema:
var patientSchema = mongoose.Schema({
name: {
type: String, required: true
},
surname: {
type: String, required: true
},
birth: {
type: Date, required: true
},
gender: {
type: String, enum: ['M', 'F'], required: true
},
status: {
type: String, required: true
}
});
The query I'm trying to do is:
Patient.
find({name: { $gt: 'bob' }}).
lean().
sort('name surname').
exec()
.then(function (data) {
//send data
})
.catch(next);
Is there something I'm missing?
---------------------EDIT---------------------
Ok, didn't think at all about case sensitivity! ::embarassed::
In fact the query is returning the right result, as all my entries begin with an upper case letter.
Now the question is: should I enforce the database to only have uppercase or lowercase entries? (maybe using a validator hook) or is there a way to do a case insensitive $gt comparison?

duplicate key error when creating a new mongoose sub-document

When creating new document and then try to upsert a new sub-document I get this error:
Object {error: "E11000 duplicate key error index: sales.users.$gro…p key:
{ : ObjectId('537b7788da19c4601d061d04') }"}
error: "E11000 duplicate key error index: sales.users.$groups.groupId_1
dup key: { : ObjectId('537b7788da19c4601d061d04') }"
__proto__: Object
The sub-document I'm trying to insert is defined as sub-schema that has a groupId field with the requirements {unique: true}, {sparse: true}. The mongoose method call I'm using to do the upsert is:
User.findByIdAndUpdate(userId,
{ $push: { 'groups': userUpdate} },
function (err, obj) where userUpdate = { groupId: groupId }.
After dropping the indexes the problem is fixed and this error no longer occurs.
var UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
active: {
type: Boolean,
default: true
},
username: {
type: String,
required: true,
unique: true
},
password: {
salt: {
type: String,
required: true
},
hash: {
type: String,
required: true
}
},
securityQuestion: {
question: String,
salt: String,
hash: String
},
mobile: {
PIN: Number,
Number: Number
},
createDate: {
type: Date,
default: Date.now
},
updateDate: Date,
lastLoginDate: Date,
prevLoginDate: Date,
passChangeDate: Date,
locked: Boolean,
lockDate: Date,
failedCount: Number,
failedDate: Date,
profile: profile,
preference: preference,
courses: [UserCourseSchema],
groups: [UserGroupSchema],
rewards: [UserRewardSchema],
roles: UserRoleSchema,
scores: [UserScoreSchema]
});
var UserGroupSchema = new Schema({
groupId: {
type: Schema.Types.ObjectId,
unique: true,
sparse: true
},
joinDate: {
type: Date,
default: Date.now
},
receiveNotifications: {
type: Boolean,
default: true
},
isAdmin: {
type: Boolean,
default: false
},
isOwner: {
type: Boolean,
default: false
},
isModerator: {
type: Boolean,
default: false
},
updateDate: Date
});
If you are applying upsert on array of object then this will always create new document as it do not compare sub documents of an array and you have unique index on groupId so it is not allowing you to create new record with the same value. For it you should find that record and if exists, then update it else create new record.
Another best way is to use $addToSet.
Hope this helps.
The {unique: true} requirement on the groupId field means that no two documents in the collection may contain the same groupId, rather than what you intended, enforcing uniqueness of the groupIds within the document. You can do what you want by using the MongoDB $addToSet operator instead.
If you are trying to update existing group from groups array, $push is not the solution.
User.findAndUpdate({_id:userId,'groups.groupId': userUpdate.groupId},
{ $set: {'groups.$': userUpdate}},
function (err, obj){})
otherwise as other suggested $addToSet will add the element into set if it exists.
User.findByIdAndUpdate(userId,
{ $addToSet : { 'groups': userUpdate} },
function (err, obj){})

Categories

Resources