Mongoose update push, delete in array - mongodb

I have a mongoose model:
var schema = new Schema({
loginName: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
},
rooms: [{ _id: Schema.Types.ObjectId, loginName: [{ type: String }] }]
});
Example result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
What I need:
search on field : rooms._id in every user document,
push new loginName in array loginName in every found user document
delete selected loginName in array
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy", "John"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
How can I do this?

You could push John into loginName array through $push
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$push: {'rooms.$.loginName': 'John'}}, function(...));
delete John from loginName array through $pull
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$pull: {'rooms.$.loginName': 'John'}}, function(...));

Related

Get extra field in model summing all records from lookup document

Having this model:
const matchSchema = mongoose.Schema({
location: {type: mongoose.Types.ObjectId, ref: 'Location'},
datetime: Date,
teamAName: String,
teamBName: String,
teamA: [{type: mongoose.Types.ObjectId, ref: 'Player'}],
teamB: [{type: mongoose.Types.ObjectId, ref: 'Player'}],
teamAScore: {type: Number, default: 0},
teamBScore: {type: Number, default: 0},
pichichi: [{type: mongoose.Types.ObjectId, ref: 'Player'}],
mvp: {type: mongoose.Types.ObjectId, ref: 'Player', default:null},
});
"teamA" and "teamB" are lists containing the "_id" of every player. When retrieving a player, I want to retrieve the number of matches that he/she have played. How can I do that? Below my query just retrieving fields from model "Player"
class PlayerController {
getAll(req, res) {
Player.find()
.sort('firstname')
.exec(function(err, players) {
res.send(players);
});
}
}
So, instead of just having this list:
[
{
_id: new ObjectId("6232395d08663294b412d6a1"),
firstname: 'Sam',
lastname: 'Credington',
__v: 0
},
{
_id: new ObjectId("622479f39be8118a52af70e5"),
firstname: 'Santi',
lastname: 'Futsal',
__v: 0
},
{
_id: new ObjectId("6232399608663294b412d6b9"),
firstname: 'Tom',
lastname: 'Hendry',
__v: 0
}
]
I would like to have the amount of matches that every player played:
[
{
_id: new ObjectId("6232395d08663294b412d6a1"),
firstname: 'Sam',
lastname: 'Credington',
matches:6,
__v: 0
},
{
_id: new ObjectId("622479f39be8118a52af70e5"),
firstname: 'Santi',
lastname: 'Futsal',
matches:8,
__v: 0
},
{
_id: new ObjectId("6232399608663294b412d6b9"),
firstname: 'Tom',
lastname: 'Hendry',
matches: 2,
__v: 0
}
]
Here's one way you could do it.
db.players.aggregate([
{
"$lookup": {
"from": "matches",
"let": { "myId": "$_id" },
"pipeline": [
{
"$match": {
"$expr": {
"$in": [ "$$myId", { "$setUnion": [ "$teamA", "$teamB" ] } ]
}
}
},
{ "$count": "numMatches" }
],
"as": "matchCount"
}
},
{
"$set": {
"matches": {
"$ifNull": [ { "$first": "$matchCount.numMatches" }, 0 ]
}
}
},
{ "$unset": "matchCount" }
])
Try it on mongoplayground.net.

Mongoose/Mongodb Aggregate - group and average multiple fields

I have a Post model with 2 fields : date and rating. How would I go about getting an average aggregate rating for each date? So group by date first and then average the rating across all posts for that date. I need to do this within mongoose but their docs are so difficult to understand.
const PostSchema = new Schema({
date: {
type: String,
default: getToday() //this is just a new Date() formatted
},
rating: {
type: Number,
required: true
}
},
)
This gives me the average across all dates but I can't figure out how to filter it by date:
Post.aggregate([
{ $group: { _id: null, avgRating: { $avg: '$rating' }}}
])
.then(function (res) {
console.log(res[0]["avgRating"]);
})
This worked for me:
Post.aggregate([
{ $group: { _id: "$date", avgRating: { $avg: '$rating' }}}
]).
then(function (res) {
console.log(res);
})
Output:
[
{ _id: 'Aug 18, 2021', avgRating: 3.0212234706616727 },
{ _id: 'Aug 19, 2021', avgRating: 2.9680319680319682 },
{ _id: 'Aug 20, 2021', avgRating: 3.023976023976024 },
{ _id: 'Aug 17, 2021', avgRating: 2.9600665557404326 },
{ _id: 'Aug 21, 2021', avgRating: 3.072661217075386 }
]
BUT it would be great if I could somehow filter this based on other factors. For example, each post has an author (reference to User model). How would I go about filtering based on the author's country.name or gender?
User model:
const userSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
birthday: {
type: Date,
required: true,
},
gender:{
type: String,
required: true
},
country:{
name: {
type: String,
required: true
},
flag: {
type: String,
// default: "/images/flags/US.png"
}
},
avatar: AvatarSchema,
displayName: String,
bio: String,
coverColor: {
type: String,
default: "#343a40"
},
posts: [
{
type: Schema.Types.ObjectId,
ref: "Post"
}
],
comments: [
{
type: Schema.Types.ObjectId,
ref: "Comment"
}
],
postedToday: {
type: Boolean,
default: false
},
todaysPost: {
type: String
}
})
Something like this
Post.aggregate([
{$match: {"date": today}},
{$group: {_id: {"country": "$author.country.name"}, avgRating: {$avg: "$rating"}}}
]).then(function(res) {
console.log(res)
})

Query embedded document in mongoDB

I'm trying to query a Group document that has Users sub documents.
But I only want the subdocuments that belong to the user,
but I'm also getting data of other users.
Look at the query result, UserStatus field, only one of them belongs to the user.
The query result -
[
{
"_id": "5b1bcc12d5cdbf2cf78f2133",
"name": "First group ever",
"users": [
{
userId: 1,
"userStatus": "I"
},
{
userId: 2,
"userStatus": "I"
}
]
}
]
Group.js -
const GroupSchema = mongoose.Schema({
name: {
type: String,
required: true
},
users: [{
userId: {
type: mongoose.SchemaTypes.ObjectId,
ref: 'users',
required: true
},
userStatus: {
type: String,
required: true
}
}]
})
Query -
Group.find({
"users": {
$elemMatch: {
userId: req.params.id,
$or: [{
userStatus: "I"
}, {
userStatus: "A"
}]
}
}
}, "_id name users.userStatus",
function (err, groups) {
if (err)
res.send(err)
res.json(groups);
});

Check follow status in aggregate mongo / mongoose

I have this schema for users where followers/followed is array and the reference the same schema
var userSchema = new Schema({
username: { type: String, unique: true, trim: true, required: true },
password: { type: String, required: true },
followers: [{ type: Schema.Types.ObjectId, ref: "users" }],
followed: [{ type: Schema.Types.ObjectId, ref: "users" }],
registered: { type: Date, default: Date.now },
admin: { type: Number, default: 0 }
});
What I am looking for to return the follow status, if the _id is contains in followed array give me for example follow_status: 1
[
{
$match: { username: new RegExp(username, "i") }
},
{
$unwind: "$followers"
},
{
$lookup: {
from: "users",
localField: "followers",
foreignField: "_id",
as: "info"
}
},
{
$unwind: "$info"
},
{
$project: {
info: {
_id: 1,
username: 1,
avatar: { $ifNull: ["$avatar", ""] },
fullname: { $ifNull: ["$fullname", ""] }
}
}
},
{
$replaceRoot: { newRoot: "$info" }
},
{
$limit: 1000
}
]
Current pipeliens result
[
{
"_id": "5a906653f52e66c9c7a23cb6",
"username": "User1"
},
{
"_id": "5a908eb564a726cf8ec7e0a3",
"username": "User2"
}
]

mongo db aggregate keep fields after group

I have this aggregate:
[
{
$match: {_id: new ObjectId('xxx')}
},
{
$unwind: "$users"
},
{
$group: {
_id: '$users.status',
count: {$sum: 1}
}
},
{
$project: {
name: 1,
field1: 1,
field2: 1,
count: '$count'
}
}
]
The schema:
{
name: String,
field1: String,
field2: Schema.Types.Mixed,
users: [{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
status: String
}]
}
The expected result:
{
name: 'NY',
field1: 'example',
field2: 'example2',
statuses: [
{
_id: 'ONLINE',
count: 20
},
{
_id: 'OFFLINE',
count: 120
},
{
_id: 'OTHER',
count: 230
}
]
}
This way I get group result just for the users statuses, and not for the other fields of the original object.