Need to get the average of complex data on mongodb - javascript

I'm not being able to find the average of a complex search in mongodb, using mongoose (nodejs) (and i'm not sure if its possible).
So, i have a collection of Scouts.
A Scout is basically the vote of a user for a player, in a match (fixture).
Scout items looks like this:
{
"_id": "6036dbbd148ccf17e82e6f14",
"player": "60243968d7ec0721344514ab",
"user": "601e9c826a339228e8f54305",
"fixture": "602435741f2e4e263492de66",
"score": 7,
"text": "Lorem ipsum",
"likes": 92,
"createdDate": "2021-02-24T23:05:33.176Z",
"__v": 0
},
{
"_id": "6036dbbd148ccf17e82e6f16",
"player": "60243968d7ec0721344514ab",
"user": "601e9c826a339228e8f54306",
"fixture": "602435741f2e4e263492de66",
"score": 1,
"text": "Lorem ipsum",
"likes": 76,
"createdDate": "2021-02-24T23:05:33.181Z",
"__v": 0
},
What i need is: the average score of a player in a fixture (among all users).
And this is what i'm trying:
Scout.aggregate([
{ $match: {
$and: [
{player: mongoose.Types.ObjectId(req.params.playerId)},
{fixture: mongoose.Types.ObjectId(req.params.fixtureId)},
]}
},
{ $group: { _id: "$_id", "avgScore": { "$avg": "$score" }}}
])
But all i get as result is this (which is not the expected result - should be 4 as the avg):
{
"_id": "6036dbbd148ccf17e82e6f16",
"avgScore": 1
},
{
"_id": "6036dbbd148ccf17e82e6f14",
"avgScore": 7
}
]

Your aggregation is okay but you are regrouping on document by _id which will always be different, so do this,
{
$group: {
_id: null,
"avgScore": {
"$avg": "$score"
}
}
}
Test it here: https://mongoplayground.net/p/6Qt8E69Z2d6

Related

update nested array data in mongodb and node js

I want to update status in corder data. i try many time but i cannot update that data. if you know how update that data ??
{
"_id": "63064232cf92b07e37090e0a",
"sname": "Bombay collection ",
"name": "Hussain Khan",
"costomer": [
{
"cname": "Basan",
"cphone": 9863521480,
"_id": "632eeff2a9b88eb59d0210f0",
"corder": [
{
"clothType": "Shirt",
"date": "2022-10-21",
"status": "false",
"_id": "635283363edde6a0e9e92dc0"
},
]
}
]
}
You can use $[<identifier>]. For example:
db.collection.update(
{},
{$set: {"costomer.$[elemA].corder.$[elemB].status": newStatus}},
{arrayFilters: [
{"elemA._id": costomerId},
{"elemB._id": corderId}
]}
)
See how it works on the playground example

How to update a object value when matched from an array in one mongodb document?

I have created a complex MongoDB document like this :
{
"_id": {
"$oid": "6354129e0f5b15991649fd10"
},
"orderId": "NEK-2209-06215614-79553",
"user": {
"$oid": "634d11565f254092fd666fd1"
},
"shippingAddress": {
"$oid": "6353aaf0fa6a1b0124c22532"
},
"billingAddress": {
"$oid": "6353aaf0fa6a1b0124c22532"
},
"productInfo": [
{
"seller": {
"$oid": "634d784c723ee32fc178aa7a"
},
"products": [
{
"productId": {
"$oid": "6353951e001ff50ea1a92602"
},
"quantity": 2,
"variation": "M",
"tax": 111
}
],
"price": 850,
"status": "Pending"
},
{
"seller": {
"$oid": "6354112f0f5b15991649fcfc"
},
"products": [
{
"productId": {
"$oid": "635411940f5b15991649fd02"
},
"quantity": 2,
"variation": "M",
"tax": 111
}
],
"price": 850,
"status": "Pending"
}
],
"total": 1671,
"shippingFees": 60,
"couponDiscount": 10,
"subtotal": 1721,
"paymentInfo": {
"paymentType": "Cash on Delivery"
},
"paymentMethod": "Home Delivery",
"createdAt": {
"$date": {
"$numberLong": "1666454174641"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1666454174641"
}
},
"__v": 0
}
Here you can see that ProductInfo is an array. My Document structure is
id: "id"
productInfo: [
{seller: "id", ....},
{seller: "id", ....},
]
Now I have two things- id and seller
Actually here I want to do this- first, find by id, then filter productInfo by seller and update status to this particular seller. How can I do that ?
mydocument.findByIdAndUpdate(id,
//Here I have to write an update value to a particular seller from productInfo array.
, {new: true})
Please help me to do this. Can anyone help me?
**Note: Here I want to update only status value from a particular seller when matched find by document id.
You can do it with positional operator - $:
db.collection.update({
_id: ObjectId("6354129e0f5b15991649fd10"),
"productInfo.seller": ObjectId("6354112f0f5b15991649fcfc"),
},
{
"$set": {
"productInfo.$.status": "New status"
}
})
Working example

How to remove deeply nested object (Node.js, mongoose)

I'm making a kanban task management app and I'm trying to remove a task with the _id: req.params.id which has the value of 62fa5ae05778ec97bc6ee23a. I tried the following:
const task = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{ $pull: { "columns.$.tasks.$._id": req.params.id } },
{ new: true }
);
But I get the error Too many positional (i.e. '$') elements found in path'columns.$.tasks.$._id'
I searched for a while and came across arrayFilters from the docs but I'm struggling a lot to understand how to implement it for this particular need.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23b"
},
{
"name": "do homework",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23c"
}
],
"_id": "62fa5ae05778ec97bc6ee23a"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v": 0
}
You need to use $[] positional operator in order to pull from the nested array. Try running this query:
db.Board.updateOne({
"_id" : "62fa5aa25778ec97bc6ee231",
}, {
$pull: { 'columns.$[].tasks': { '_id': '62fa5ae05778ec97bc6ee23a' } }
});

How can I get count of the Documents and filter them in efficient way? (mongoose)

I'm implementing search function that is simply find document in mongoDB. I want to .skip(x) and .limit(x) on result to simulate paging result, but can I get total count of document (before skip and limit) and get filtered result at once?
Code that produce Expected Output :
db.Datas.find({ type: "Unknown" })
.then((result) => {
let count = result.length;
db.Datas.find({ type: "Unknown" })
.sort({ createdAt: -1 })
.skip((req.query.page - 1) * 10)
.limit(10)
.then((res) => {
res.json({ count: count, result: res });
});
})
.catch((err) => {});
But querying twice it somehow annoying, and it might be slow at large database.
I tried something like find({}).then(x => { ... }).sort(...) ... but isn't working because it only returns Promise.
How can I do this things in efficient way?
or is just getting whole documents and skip, limit with JS-way (using .splice, or etc..) will be faster and efficient?
You can use $facet aggregation to achieve this.
db.Datas.aggregate([
{
$match: {
"type": "Unknown"
}
},
{
$sort: {
createdAt: -1
}
},
{
$facet: {
totalRecords: [
{
$count: "total"
}
],
data: [
{
$skip: 0
},
{
$limit: 5
}
]
}
}
])
Playground
Let's say you have these documents:
db={
"Datas": [
{
"_id": "5e390fc33285e463a0799689",
"type": "Known",
"createdAt": "2020-02-04T06:31:31.311Z",
"__v": 0
},
{
"_id": "5e390fd03285e463a079968a",
"type": "Known",
"createdAt": "2020-02-04T06:31:44.190Z",
"__v": 0
},
{
"_id": "5e390fda3285e463a079968b",
"type": "Unknown",
"createdAt": "2020-02-04T06:31:54.248Z",
"__v": 0
},
{
"_id": "5e390fdf3285e463a079968c",
"type": "Unknown",
"createdAt": "2020-02-04T06:31:59.993Z",
"__v": 0
},
{
"_id": "5e390fec3285e463a079968d",
"type": "Unknown",
"createdAt": "2020-02-04T06:32:12.336Z",
"__v": 0
},
{
"_id": "5e390ffd3285e463a079968e",
"type": "Unknown",
"createdAt": "2020-02-04T06:32:29.670Z",
"__v": 0
},
{
"_id": "5e3910163285e463a079968f",
"type": "Unknown",
"createdAt": "2020-02-04T06:32:54.131Z",
"__v": 0
},
{
"_id": "5e3910213285e463a0799690",
"type": "Unknown",
"createdAt": "2020-02-04T06:33:05.166Z",
"__v": 0
}
]
}
Response will be like this:
[
{
"data": [
{
"__v": 0,
"_id": "5e3910213285e463a0799690",
"createdAt": "2020-02-04T06:33:05.166Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e3910163285e463a079968f",
"createdAt": "2020-02-04T06:32:54.131Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e390ffd3285e463a079968e",
"createdAt": "2020-02-04T06:32:29.670Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e390fec3285e463a079968d",
"createdAt": "2020-02-04T06:32:12.336Z",
"type": "Unknown"
},
{
"__v": 0,
"_id": "5e390fdf3285e463a079968c",
"createdAt": "2020-02-04T06:31:59.993Z",
"type": "Unknown"
}
],
"totalRecords": [
{
"total": 6
}
]
}
]
As you see, we got the total records with the filtered, sorted, skipped and limited data.

sequelize include returns only one result

I have a Users table and a Groups table, when I retrieve a single user, I want to get a list of all the groups that user belongs to. I created a joins table named GroupUsers and it has userId and groupId on it, this is how I know that a user belongs to a group. Now, when I try to get the list of groups a user belongs to using the sequelize include on find, it only returns the first match, if the user belongs to various groups. How do I solve this?
Users controller
return models.Users
.find({
include: [{
model: models.Groups,
as: 'groups',
limit: null,
required: false
}],
where: { username }
})
Returns this:
{
"id": 1,
"username": "John",
"phone": "xxxxxxxx",
"email": "john#email.com",
"createdAt": "2017-07-16T20:14:04.744Z",
"updatedAt": "2017-07-16T20:14:04.744Z",
"groups": [
{
"id": 1,
"name": "Test Group",
"type": "Public",
"createdAt": "2017-07-16T20:13:18.392Z",
"updatedAt": "2017-07-16T20:13:18.392Z",
"GroupUsers": {
"userId": 1,
"groupId": 1,
"last_seen": null,
"createdAt": "2017-07-16T20:14:27.903Z",
"updatedAt": "2017-07-16T20:14:27.903Z"
}
}
]
}
Instead of this:
{
"id": 1,
"username": "John",
"phone": "xxxxxxxx",
"email": "john#email.com",
"createdAt": "2017-07-16T20:14:04.744Z",
"updatedAt": "2017-07-16T20:14:04.744Z",
"groups": [
{
"id": 1,
"name": "Test Group",
"type": "Public",
"createdAt": "2017-07-16T20:13:18.392Z",
"updatedAt": "2017-07-16T20:13:18.392Z",
"GroupUsers": {
"userId": 1,
"groupId": 1,
"last_seen": null,
"createdAt": "2017-07-16T20:14:27.903Z",
"updatedAt": "2017-07-16T20:14:27.903Z"
}
},
{
"id": 2,
"name": "Test Group 2",
"type": "Public",
"createdAt": "2017-07-16T20:13:18.392Z",
"updatedAt": "2017-07-16T20:13:18.392Z",
"GroupUsers": {
"userId": 1,
"groupId": 2,
"last_seen": null,
"createdAt": "2017-07-16T20:14:27.903Z",
"updatedAt": "2017-07-16T20:14:27.903Z"
}
}
]
}
I'm sure I'm doing something wrong somewhere I just don't know where, that same thing may also be the cause of sequelize including the joins table in the result: GroupUsers
It appears that in my associations, I did:
Users.belongsToMany(models.Groups, {
through: 'GroupUsers',
as: 'groups',
foreignKey: 'groupId'
});
Instead of :
Users.belongsToMany(models.Groups, {
through: 'GroupUsers',
as: 'groups',
foreignKey: 'userId'
});
Note the foreignKey attribute
And as for the GroupUsers object that is also returned, I removed that by doing:
include: [{
model: models.Groups,
as: 'groups',
attributes: ['id', 'name'],
through: { attributes: [] }
}]
Note the through key which has attributes set to an empty array.

Categories

Resources