Complex query mongoose - embedded documents array - mongodb

I want to execute a query for getting all "group" documents that have the userId in there array of users.
I've tried several different ways of query, but I always get an empty array.
What am I doing wrong?
group.js
let mongoose = require('mongoose');
const Group = mongoose.Schema({
name: {
type: String,
required: true
},
users: [{
userId: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'users',
required: true
},
userType: {
type: String,
required: true
},
userStatus: {
type: String,
required: true
}
}]
})
module.exports = mongoose.model('group', Group);
groupController.js
exports.getUserGroups = function (req, res) {
Group.find({
"users.userid": "req.user._id"
}, function (err, groups) {
if (err)
res.send(err)
res.json(groups);
});
}

Field names are case-sensitive, so "users.userid" should be "users.userId" instead:
Group.find({
"users.userId": "req.user._id"
}, ...

You can try using $in operator
db.collection.find({
"users.userId": {
$in: [
req.user._id
]
}
})

Related

Mongo How to use select to return multiple selected properties from document?

I am using findOneAndUpdate, where I want
to return updated document
i dont want to return the entire document but only the following:
one object out of an array + a virtual property in the document.
const notifications = {
to:
messages: [
{_id: "23452", title:"hello"}, {_id: "23452", title:"bye"}
]
...
}
so for example I would want to only return the object {_id: "23452", title:"bye"} AND unreadCount virtual field prop.
my code works so far as I am returning updated document and only the message I want, but I dont know how to return also the unreadCount prop.
schema:
const notificationSchema = new mongoose.Schema({
to: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
messages: [{
title: {
type: String,
required: true
},
isRead: {
type: Boolean,
default: false
},
createdAt: {
type: Date,
default: new Date()
}
}, ]
},
{timestamps: true, toObject: {virtuals: true}
});
notificationSchema.virtual('unreadCount').get(function() {
... return count;...
})
updateRead: async (userId, id) => {
const notification = await Notification.findOneAndUpdate({to: userId, 'messages._id': id}, {
$set: { "messages.$.isRead": true} },
{ select: {
messages: {
$elemMatch: {_id: id}
}
}, new: true});
}

How to Find record by ObjectID's property in Mongodb?

Suppose I have 2 models like Orders and Customers.
const orders = mongoose.Schema({
customer_id: {
type: mongoose.Schema.ObjectId
required: true,
ref: 'Customer',
},
OrderNo: {
type: String,
required: true,
})
const customers = mongoose.Schema({
name: {
type: String,
required: true,
})
I want to filter orders based on customer's name as per below.
let query = {
'customer_id.name': { $regex: '.*AMAZON.*' },
}
await Orders.find(query)
but its not working properly. I'm new to mongodb. Can anyone share feasible solution ? Thanks
Try to populate and filter the resulting fields:
await Orders.find({})
.populate({ path: 'customer_id', match: { name: { $regex: '.*AMAZON.*' } } })
.exec();

Mongoose pull ObjectId from array

i'm trying to do a pretty simple operation, pull an item from an array with Mongoose on a Mongo database like so:
User.update({ _id: fromUserId }, { $pull: { linkedUsers: [idToDelete] } });
fromUserId & idToDelete are both Objects Ids.
The schema for Users goes like this:
var UserSchema = new Schema({
groups: [],
linkedUsers: [],
name: { type: String, required: true, index: { unique: true } }
});
linkedUsers is an array that only receives Ids of other users.
I've tried this as well:
User.findOne({ _id: fromUserId }, function(err, user) {
user.linkedUsers.pull(idToDelete);
user.save();
});
But with no luck.
The second option seem to almost work when i console the lenghts of the array at different positions but after calling save and checking, the length is still at 36:
User.findOne({ _id: fromUserId }, function(err, user) {
console.log(user.linkedUsers.length); // returns 36
user.linkedUsers.pull(idToDelete);
console.log(user.linkedUsers.length); // returns 35
user.save();
});
So it looks like i'm close but still, no luck. Both Ids are sent via the frontend side of the app.
I'm running those versions:
"mongodb": "^2.2.29",
"mongoose": "^5.0.7",
Thanks in advance.
You need to explicitly define the types in your schema definition i.e.
groups: [{ type: Schema.Types.ObjectId, ref: 'Group' }],
linkedUsers: [{ type: Schema.Types.ObjectId, ref: 'User' }]
and then use either
User.findOneAndUpdate(
{ _id: fromUserId },
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
or
User.findByIdAndUpdate(fromUserId,
{ $pullAll: { linkedUsers: [idToDelete] } },
{ new: true },
function(err, data) {}
);
I had a similar issue. I wanted to delete an object from an array, using the default _id from mongo, but my query was wrong:
const update = { $pull: { cities: cityId }};
It should be:
const update = { $pull: { cities: {_id: cityId} }};

Mongo find users that are not blocked

I have a db in Mongo with users. I want to retrieve all user that are not in my blocked Array. tried to filter through the results by retrieving All documents but I wonder if that's the most efficient way. Is there a way in Mongo to find all users that are not part of an array?
Here is my User model:
var UserSchema = mongoose.Schema({
username: {
type: String,
index:true,
unique: true
},
password: {
type: String
},
email: {
type: String,
unique: true
},
name: {
type: String
},
latitude:{
type: String
},
longitude:{
type: String
},
blocked:{
type: Array
},
friends:{
type: Array
},
photo:{
type: String
},
identity:{
type: String
},
age:{
type: String
},
herefor:{
type: String
},
conns:{
type: Number
},
status:{
type: String
}
}, { collection: 'users' });
You are probably looking for something like the following, assuming your blocked users array contains usernames and your mongoose model is called UserModel:
var blockedUsernamesArray = [];
UserModel.find({ username: { $nin: blockedUsernamesArray } }, function(err, docs) {
// Handle result
})

How to Check current user's vote before votes are grouped and sumed in same aggregate function

var PostSchema = new mongoose.Schema({
item: {
type: mongoose.Schema.ObjectId,
ref: 'item',
required: true
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
vote: {
type: Number,
default: 0
},
total: {
type: Number,
default: 0
},
awsPostKey: {type: String},
picture: {type: String, required: true}
});
var data = function(){
return Post
.find({})
.then(function(post){
return post;
})
};
var userId = //mongo objectId for current user
//postVote schema:
var PostVoteSchema = new mongoose.Schema({
post: {
type: mongoose.Schema.ObjectId,
ref: 'Post',
required: true
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
vote: {
type: Number,
default: 0
}
});
//pass data from Post query to PostVote sum function:
PostVoteSchema.statics.sum = function (data, userId) {
var postIds = data.map(function (a) {
return a._id;
});
return PostVote
.aggregate(
[
{ $match: { 'post': {$in: postIds}}},
{ $group: { _id:'$post' ,vote:{$sum:'$vote'}}}
])
.execAsync()
.then(function(votes){
return votes;
//desired output to client, _id is for specific post
{_id: 5802ea4bc00cb0beca1972cc, vote: 3, currentUserVote: -1}
});
};
I'm successfully able to get the total sum of all votes with the same postId.
Now, I"m wanting to see if the current user (userId) has placed a vote for the given post as well, then to return how they voted (+1 or -1) along with the sum of all votes for the specific post.
Is it possible to do this, or will I have to do this outside of my aggregate pipeline -- within a second query? It just seems potentially taxing to have to query the collection again.
Yes, that's possible. Within the $group pipeline, you can use the $cond operator as the logic for feeding the $sum accumulator operator. For example:
return PostVote.aggregate([
{ "$match": { "post": { "$in": postIds } } },
{
"$group": {
"_id": "$post",
"votes": { "$sum": "$vote" },
"userVotes": {
"$sum": {
"$cond": [
{ "$eq": ["$user", userId] },
"$vote",
0
]
}
}
}
}
]).execAsync().then(function(votes){
return votes;
});