Querying sub document of sub document in mongoose - javascript

I wanted to save the data in "messageSchema" which is sub document of chatSchema by checking the "receiver" of chatSchema and "username" of userSchema.
like pseudoCode:-
if(userSchema.username == "Rahul" && userSchema.chatSchema.receiver){
then save the data in chatSchema.message;
}
Here is my Schema:-
var messageSchema = mongoose.Schema({
messageId: {type: String, unique: true, required: true},
created: {type: Date, default: Date.now},
messageContent: String
});
var chatSchema = mongoose.Schema({
message: [messageSchema],
receiver: {type: String, required: true}
});
var userSchema = mongoose.Schema({
username: { type: String, unique: true, required: true },
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
token: { type: String, required: false },
conversations: [chatSchema]
});
please suggest what should be code to save the message data.
tried below one that didn't work.
User.findOneAndUpdate({username: "rahul", "conversations.receiver": data.receiver },{$push: {"conversations.message": message}});

I think you need to use $elemMatch instead of the dot notation for matching properties within an array. Try this:
User.findOneAndUpdate(
{
username: "rahul",
conversations: {
$elemMatch: { receiver: data.receiver }
}
},
// or whatever your update is
{$push: {"conversations.message": message}
})

Related

What is the difference between surrounding the type with square brackets and surrounding the whole object

What is the difference between this code:
const userSchema = new Schema(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
phoneNumber: { type: Number, required: false, unique: true },
address: [{ type: mongoose.Types.ObjectID, required: true, ref: "Address" }],
},
{
timestamps: true,
}
);
And this code:
const userSchema = new Schema(
{
name: { type: String, required: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
phoneNumber: { type: Number, required: false, unique: true },
address: { type: [mongoose.Types.ObjectID], required: true, ref: "Address" },
},
{
timestamps: true,
}
);
NOTICE:
In the first code, I surrounded the whole address object with square brackets.
In the second code, I only surrounded the type property of the address with square brackets.
What I want to know is how that will impact the app's behavior.
Is there any difference?
Thanks.
They both declare an array-of-references, but there are some subtle differences.
Let's take this simple schema:
const userSchema = mongoose.Schema({
address: { type: [ String ], required: true, default: undefined }
});
const User = mongoose.model('User', userSchema);
This will apply the options (required and default) to the address array-of-strings as a whole:
// This will fail validation:
// ValidatorError: Path `address` is required
const doc = new User({});
// This will pass validation:
const doc = new User({ address : [] });
Now change the position of the brackets in the schema:
const userSchema = mongoose.Schema({
address: [{ type: String, required: true, default: undefined }]
});
This will apply the options to the elements of the address array:
// This will pass validation, `address` itself isn't required:
const user = new User({});
// This will also pass validation, for the same reason:
const user = new User({ address : [] });
// This will fail validation, because elements of the array are required to have a proper value
// ValidatorError: Path `address.0` is required
const user = new User({ address : [ '' ] });
EDIT: if you want to enforce that the address field is always defined and has at least one element, you have to use a custom validator. For your schema, it would look like this:
address: {
type: [ mongoose.Schema.Types.ObjectID ],
ref: 'Address',
required: true,
default: undefined,
validate: a => Array.isArray(a) && a.length > 0
}

Mongoose find.where in a schema with an attribute of type ObjectId

I have this schemas:
UserLike
const UserLikeSchema = Schema({
user: {
type: Schema.Types.ObjectId,
ref: "User",
required: [true, "User is required"],
},
game: {
type: Schema.Types.ObjectId,
ref: "Game",
required: [true, "Game is required"],
}
});
Game
const GameSchema = Schema({
title: {
type: String,
required: [true, "Title is required"],
},
status: {
type: Boolean,
default: true,
},
});
I need to find all UserLikes where populated Game.status are true
I'm trying with a code like this
const games = await UserLike.find()
.populate("game")
.where("game.status")
.equals(true);
But I can't find a solution :(
You should try using the match option as directed by mongoose docs.
https://mongoosejs.com/docs/populate.html#query-conditions
The query might look something like this:
UserLike.
find().
populate({
path: 'game',
match: { status: true },
}).
exec();

Mongoose not saving changes to a document

I'm sorry if this might be a duplicate question but I'm quite having a hard time understanding Mongoose. I am working on a Node.js project that implements Mongoose and MongoDB. What I want to accomplish is to modify and save some users' data through a call from a specific endpoint.
Mongoose Schema looks like this
var UserSchema = new Schema({
isAdmin: {type: Boolean, default: false},
name: String,
surname: String,
nickname: { type: String },
email: { type: String, lowercase: true, required: true, trim: true, unique: true, dropDubs: true },
password: { type: String, required: true },
salt: { type: String },
verified: { type: Boolean, default: false },
bio: {
type: { type: String, enum: [0,1] }, // 0='Appassionato', 1='Giocatore'
birthday: String,
height: Number,
number: Number,
role: { type: String, enum: [0,1,2,3] }, // 0='Playmaker', 1='Ala', 2='Guardia', 3='Centro'
team: String,
city: String,
aboutMe: String,
},
newsletter: {type: Boolean, default: false},
lastCheckin: {type: mongoose.Schema.Types.ObjectId, ref: 'Checkin'},
follows: [{type: mongoose.Schema.Types.ObjectId, ref: 'Structure'}],
score: { type: Number, default: 0 },
profilePicture: String,
lastLogin: {type: Date},
facebook: {
id: String,
accessToken: String,
profileImage : String
}
}, {
collection: 'users',
retainKeyOrder: true,
timestamps: true,
}).plugin(mongoosePaginate);
Following is the code for when the endpoint gets interrogated
exports.updateUser = (req,res) => {
var userId = req.params.userId;
var updates = req.body;
User.findOneAndUpdate({_id: userId}, {$set: updates}, (err, saved) => {
if (!err) {
console.log("Ritorno questo: " + saved);
return res.status(202).json(saved);
} else {
return res.status(500).json(saved);
}
});
};
As far as I understood, the method findOneAndUpdate exposed by Mongoose should find the document I'm looking for and then modify it and save it. This doesn't happen though.
Through PostMan I'm sending this JSON
{"bio.aboutMe":"Hello this is just a brief description about me"}
But PostMan is responding with the non-modified object. What am I missing here?
What you need to do is to add {new:true}, it give you back the updated document.
In the documentation :
If we do need the document returned in our application there is
another, often better, option:
> Tank.findByIdAndUpdate(id, { $set: { size: 'large' }}, { new: true },
> function (err, tank) { if (err) return handleError(err);
> res.send(tank); });
This is something I don't really like as there is another option if we don't want to have the document → update
So what you need to do is :
User.findOneAndUpdate({_id: userId}, {$set: updates}, {new:true}.....

Mongoose (mongodb) $push data as subdocument, validate unique?

I have a User document which has a Notes subdocument.
I'm using the following code to push new notes for the user with the given email address.
UserSchema.statics.addNotesToUser = function (email, notes, callback) {
return this.updateOne(
{'email': email},
{$push: {notes}},
callback
)
};
This is working fine, however it's ignoring my unique constraint on the NoteSchema. these are my schemas
const NoteSchema = new Schema({
_id: false,
id: {type: String, required: true, trim: true, unique: true},
content: {type: String, required: true, trim: true, lowercase: true},
added: {type: Date, default: Date.now},
used: {type: Date, default: Date.now},
book: {
name: {type: String, required: true}
}
});
const UserSchema = new Schema({
email: {type: String, required: true, trim: true, lowercase: true, unique: true},
notes: [NoteSchema]
});
I'm wondering how I can make sure that when pushing new notes to my user, I can validate if the ID of the notes is unique.
Thank you.
To achieve uniqueness constraint like functionality in subdocuments, hope that's OK.
let notesId = [];
notes.forEach(function(val,index){
notesId.push(val.id)
})
db.yourCollection.update(
{ 'email': email, 'NoteSchema.id': { '$ne': { $each: notesId } }},
{$push: {notes} },
callback)

passing nested JSON data from mongoose model is not working

I am pushing nested JSON data to database. This is how my schema looks like,
const mongoose = require('mongoose');
// original Schema
const dataSourceSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true, unique: true },
type: { type: String, required: true },
projectId: { type: mongoose.Schema.Types.ObjectId, ref: 'Project', required: true },
config:{type: String, required: true}
});
module.exports = mongoose.model('DataSource', dataSourceSchema);
I would like to pass the following json data to my dataSourceSchema,
{
“name”:”JdbcSourceConnector”,
"type" :"string",
“config”: {
“connector.class”:” io.confluent.connect.jdbc.JdbcSourceConnector”,
“tasks.max”:1,
“connection.url”:”<connection to connect to database along with username and password>”,
“mode”:”incrementing”,
“incrementing.column.name”:”<incrementing column name in table>”,
“topic.prefix”:”test-mysql-jdbc-”
}
}
But its not taking, gives casting error or ',' expected.
So i tried this,
const dataSourceSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true, unique: true },
type: { type: String, required: true },
projectId: { type: mongoose.Schema.Types.ObjectId, ref: 'Project', required: true },
config:{
connector.class:{ type: String, required: true },
tasks.max:{ type: String, required: true },
connection.url:{ type: String, required: true },
mode:{ type: String, required: true },
incrementing.column.name:{ type: String, required: true },
topic.prefix:{ type: String, required: true }
}
});
this Schema is also giving me errors, ',' expected.
If i pass just a string as i have mentioned in my original schema, the data gets stored in db.
but i want to pass the nested json data, please guide me in right direction.
I also tried stringify the data , its not working.
As I see it , the error lies in defining you schema with your second schema you came close to the answer. Change you schema as follows:
const dataSourceSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: { type: String, required: true, unique: true },
type: { type: String, required: true },
projectId: { type: mongoose.Schema.Types.ObjectId, ref: 'Project', required: true },
config:{
connectorClass:{ type: String, required: true },
tasksMax:{ type: String, required: true },
connectionUrl:{ type: String, required: true },
mode:{ type: String, required: true },
incrementingColumnName:{ type: String, required: true },
topicPrefix:{ type: String, required: true }
}
});
I have suggested the change as mongoose doesn't understand the dot notation in the key column of it's schema, hence you were receiving the error you have mentioned.
If you want to use the dot notation for some reason encapsulate the key in " and not in the special character as appearing in your code snippet.
P.s. - Don't forget to change the key names in your json

Categories