Querying objects for an array of attributes in Mongoose - node.js

I am trying to fetch chats for an array of users.
Chats schemas are defined like this:
const ChatSchema = new Schema<IChatSchema>(
{
messages: [
{
type: Schema.Types.ObjectId,
ref: "MessageSchema",
},
],
participants: [
{
type:Schema.Types.ObjectId,
ref: "UserSchema",
}
]
},
{
timestamps: true,
}
);
I have two usersnames 'A' and 'B' and I want to query common chats of those two users. Any idea how to do it?
User schema
const UserSchema = new Schema<IUserSchema>(
{
username: {
type: String,
required: true,
unique:true,
},
},
{
timestamps: true,
}
);
I tried this approach but did not work.
let chat = await Chats.find({
participants: { $elemMatch: { username: usernames } },
})
I also tried this
let chat = await Chats.find({
"participants.username": { $all: usernames },
})

I can think of a few ways:
Option A: Use aggregation to lookup the participants, then match the usernames
Option B: Use find to retrieve the user records from Users, then query Chats for matching ObjectID values
Option C: modify the schema so the chats also contain the usernames, so you can query them directly

Related

How To Remove Particular Objects From A Nested Array In Mongoose?

I need a function to remove particular objects from a nested array ,please check the code as follow ,I have already tried a lot times ,but fail ..Could you please help me ?Thank you so much in advance!
UserSchema :
userName: {
type: String,
},
specialList: [
{
type: mongoose.Types.ObjectId,
ref: "Friend",
},
],
FriendSchema:
userName:{
type:string
}
Now ,I need a function to delete some of the friends in a user's specialList by their user's id,
For instance ,
//this is not working like I wish ...I have no idea what is going on ...
const needToRmoveList = ["123","456"];
await UserInfo.findOneAndUpdate(
{ _id: "345" },
{ $pull: { specialList: { id: { $in: [needToRmoveList] } } } },
{ new: true }
);

find object by id which is in array mongoose

My chat object has got an array with two elements - users id. I have first id from url params, but second id I have in array of users. Now I want to get all chats where first id is this one from url, and second is in the array. I think that example how I tried to do it will be the best explanation of this problem :
Chat.find({
users: { $all: [req.params.id, { $in: usersIdArray }] }
})
where usersIdArray is for example:
[ 5f8777a01d8c122e74cb6f08, 5f8777721d8c122e74cb6f02 ]
they are numbers, not strings. I don't know if it is important...
The error I get now :
(node:12168) UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value "{ '$in': [ 5f8777a01d8c122e74cb6f08, 5f8777721d8c122e74cb6f02 ] }" at path "users" for model "Chat"
And my chat schema:
// Create schema
const ChatSchema = new Schema({
users: {
type: [{
type: Schema.Types.ObjectId,
ref: 'Users',
}, {
type: Schema.Types.ObjectId,
ref: 'Users',
}],
required: [true, 'Podaj uczestników czatu.'],
},
lastMessage: {
type: Schema.Types.ObjectId,
ref: 'Message'
}
}, { timestamps: opts });
Since the length of your array is fixed (2), you can just query based on array position:
Chat.find({
"users.0": req.params.id,
"users.1": {$in: usersIdArray}
});
If that doesn't work then probably because usersIdArray are actually not ObjectIds, in which case you'd need to map them:
usersIdArray.map(x => ObjectId(x))
#Christian Fritz, I had to add $or to your solution and everything is fine now:
Chat.find({
$or: [
{
"users.1": req.params.id,
"users.0": { $in: usersIdArray }
}, {
"users.0": req.params.id,
"users.1": { $in: usersIdArray }
}]
})

Mongodb update array for specific fields only

I have a json array reorderList for a topic:
const reorderList = [
{ _id: '5e6b419c76a16d5c44d87132', order: 0 },
{ _id: '5e6b41a276a16d5c44d87139', order: 1 },
{ _id: '5e6b41a776a16d5c44d87140', order: 2 }
]
And my TopicSchema is like this:
var TopicSchema = new Schema({
topicTitle: String,
topicQuestion: [
{
questionTitle: String,
answer: String,
order: Number
}
]
}
Now I want to update my topic questions order based on the reorderList's _id.
But the below statement will replace all the things from topicQuestion (e.g. questionTitle and answer will be removed)
Topic.findOneAndUpdate(
{ '_id': topicId },
{ $set: { 'topicQuestion': reorderList } }, //replaces here
{ upsert: true },
function (err, response) {
...
});
How to update it based on reorderList and also keep the original data inside topicQuestion?
The schema that you're using is badly designed. What you can do here is create another schema, TopicQuestionSchema and put a ref to the topic it belongs to.
var TopicQuestionSchema = new Schema({
questionTitle: String,
answer: String,
order: Number,
topic: {type: ObjectId, ref: 'Topic'} // the name of your model
}
This way you can still keep track of the topic the questions belong to, and still be able to update the order easily.

Mongoose: Modeling and updating many to many

I'm very new to mongodb and mongoose and I'd like to know how to handle many to many relations. I know mongodb is a non-relational database, but how would I model something like Users and Groups.
A User can join multiple groups while a group can have multiple users.
My Schemas look like this:
User
const UserSchema: Schema = new Schema({
username: {
type: String,
required: true,
unique: true,
},
groups: [
{
type: Schema.Types.ObjectId,
ref: 'Group',
},
],
});
Group
const GroupSchema: Schema = new Schema({
name: {
type: String,
required: true
},
users: [
{
type: Schema.Types.ObjectId,
ref: 'User',
},
],
});
So, with an endpoint like 'users/:id/assigngroup/:groupid I would like to add an User_id to User.groups and the groupid to Group.users.
For my endpoint I do something like this:
User.findOneAndUpdate({ _id }, { $addToSet: { groups: groupId } }, { new: true }).populate('groups').exec((err, user) => {
Group.findOneAndUpdate({ _id: groupId }, { $addToSet: { users: _id } }, { new: true }).exec((err, group) => {
res.send(user);
});
})
This works tho, but the problems I encountered are:
I could still add a groupId to User.users even if the Group with this id doesn't exist. I could check this with an additional Group.findOne() before the User.findOneAndUpate but this doesn't feel like it's the best/quickest way in mongoose.
How should I handle a rollback if, for example one of the findOneAndUpdate, fails?
What I need is a clean way to check if the Group with the groupid exists, then check if the user with the _id exists and after that I can add the groupid to User.groups and the _id to Group.users (and not being able to add the same id multiple times of course). What would be the best way to do this? Using findOne/findOneUpdate mutiple times for each check doesn't feel like the right way.
Please let me know if my approach to model this "relation" is completely wrong.
Thanks! :)
Edit: I don't think it's the smart way to save a reference in both models. I removed groups from the User model. So to assign a User I just use
Group.findOneAndUpdate({ _id: groupId }, { $addToSet: { users: _id } }, { new: true }).populate('users', '_id username').exec().then(group => { });
And the easy way to get all Groups of a User would be
Group.find({
"users": new Types.ObjectId(id)
}).exec(callback);
So I don't need to update a reference twice if I assign/remove a User.

It is possible to pull elements from a referred objects' array using mongoose?

I have 2 mongo schemas related one with the other using ObjectId:
var User = new Schema({
username: {
type:String,
unique: true
},
password: {
type:String
},
verified: {
type: Boolean,
default: false
},
lastActivity:{
type:Date,
default:Date.now
}
});
And a watitingRoom schema with lists all the users:
var WaitingRoom = new Schema({
lastActivity:{
type:Date,
default:Date.now
},
clients: [{
type : mongoose.Schema.ObjectId,
ref: 'User'
}],
videocalls: [{
type: mongoose.Schema.ObjectId,
ref:'VideoCall'
}]
});
So, I want to 'refresh' my clients array pulling all the clients which a lastActivity less than the current time. I tried it by using the $pull tool present in mongoose. After googling and mixing different examples I tried things like:
WaitingRoom.findOneAndUpdate({}, { lastActivity: new Date(),
$pull : {clients : {"clients.lastActivity": { $lt: new Date() }}}
}, options)
.populate("clients")
.exec( function(error, waitingRoom) {
if (err) { return res.status(500).send({ msg: err.message }); }
})
Which finds the unique waiting room, updates the lastActivity field and tries to pull all the clients that has a clients.lastActivity less than the current date.
(Obviously this snipped code doesn't work)
The problem is that I didn't find any documentation or example that explains if it is possible to pull elements from a referred ObjectId schema using a nested condition clients.lastActivity
You need to first find the ids from User database and then need to $pull them from the WaitingRoom database
User.find({ lastActivity: new Date() }).then((users) => {
const ids = []
users.map((user) => {
ids.push(user._id)
})
WaitingRoom.update({}, { $pull: { clients: ids }}, { multi: true }).then(() => {
console.log('removed')
})
})

Resources