Sort collection by common ids and latest created date - node.js

I have mongoose collection "travels" with data like below. In data there is 3 items with same nUid but lastUpdateTime is different for each entry.
[
{
_id: 5ffff2bc9698ce3514ef6d5a,
nUid: 123456,
latitude: '19.154607',
longitude: '72.936027',
name: 'abc1',
lastUpdateTime: 2021-01-14T07:26:51.000Z,
__v: 0
},
{
_id: 5fffeca4794ca00ae85ee940,
nUid: 635241,
latitude: '19.154620',
longitude: '72.936087',
name: 'abc2',
lastUpdateTime: 2021-01-14T03:31:20.000Z,
__v: 0
},
{
_id: 5fffeca4794ca00ae85ee93f,
nUid: 123456,
latitude: '19.154609',
longitude: '72.936047',
name: 'abc1',
lastUpdateTime: 2021-01-14T09:20:23.000Z,
__v: 0
},
{
_id: 5ffff2bc9698ce3514ef6d59,
nUid: 123456,
latitude: '19.154620',
longitude: '72.936087',
name: 'abc1',
lastUpdateTime: 2021-01-14T10:38:20.000Z,
__v: 0
},
{
_id: 5ffff4610fd21902e02ac749,
nUid: 345262,
latitude: '19.154620',
longitude: '72.936087',
name: 'abc3',
lastUpdateTime: 2021-01-14T09:11:30.000Z,
__v: 0
},
{
_id: 5ffff4610fd21902e02ac748,
nUid: 635241,
latitude: '19.154640',
longitude: '72.936017',
name: 'abc2',
lastUpdateTime: 2021-01-14T04:30:10.000Z,
__v: 0
},
]
I want to sort data like common ids with latest lastUpdateTime
[
{
_id: 5ffff2bc9698ce3514ef6d59,
nUid: 123456,
latitude: '19.154620',
longitude: '72.936087',
name: 'abc1',
lastUpdateTime: 2021-01-14T10:38:20.000Z,
__v: 0
},
{
_id: 5ffff4610fd21902e02ac748,
nUid: 635241,
latitude: '19.154640',
longitude: '72.936017',
name: 'abc2',
lastUpdateTime: 2021-01-14T04:30:10.000Z,
__v: 0
},
{
_id: 5ffff4610fd21902e02ac749,
nUid: 345262,
latitude: '19.154620',
longitude: '72.936087',
name: 'abc3',
lastUpdateTime: 2021-01-14T09:11:30.000Z,
__v: 0
},
]
Tried solution
const travelsQuery = Travels.aggregate([
{ $group: { _id: "$nUid", result: { "$first": "$$ROOT" } } },
{ $sort: { lastUpdateTime: 1 } }]);
const travelsData= await travelsQuery;
let travelsResult= travelsData.map((travel) => {
return travel.result;
});
But not getting proper result, I got data with same ids but not sorted by lastUpdateTime, I tried lastUpdateTime: -1` also.
Thanks in advance :)

You copied the document into result so try:
const travelsQuery = Travels.aggregate([
{ $group: { _id: "$nUid", result: { "$first": "$$ROOT" } } },
{ $sort: { "result.lastUpdateTime": 1 } }]);
const travelsData= await travelsQuery;
let travelsResult= travelsData.map((travel) => {
return travel.result;
});

After updating $first to $last It's working as required :)
const travelsQuery = Travels.aggregate([
{ $group: { _id: "$nUid", result: { "$last": "$$ROOT" } } },
{ $sort: { "result.lastUpdateTime": 1 } }]);
const travelsData= await travelsQuery;
let travelsResult= travelsData.map((travel) => {
return travel.result;
});

Related

I am trying to get retrive data from mongodb but not getting expected output

DB Data -
[{
title: "Vivo X50",
category: "mobile",
amount: 35000
},
{
title: "Samsung M32",
category: "mobile",
amount: 18000
},
{
title: "Lenovo 15E253",
category: "laptop",
amount: 85000
},
{
title: "Dell XPS 15R",
category: "laptop",
amount: 115000
}]
Expected Output:
[{
category: "mobile",
qty: 2,
totalAmount: 53000
},
{
category: "laptop",
qty: 2,
totalAmount: 200000
}]
Code I am running (Using mongoose)
let products = await Product.aggregate([
{
$project: { _id: 0, category: 1, amount: 1 },
},
{
$group: {
_id: "$category",
qty: { $sum: 1 },
totalAmount: { $sum: "$amount" },
},
},
]);
Result I am Getting.
[
{
"_id": "laptop",
"count": 2,
"totalSum": 200000
},
{
"_id": "mobile",
"count": 2,
"totalSum": 53000
}
]
As you can clearly see that I am able to get correct data but I want correct name also category instead of _id. Please help me with that. Thanks in advance
You need $project as the last stage to decorate the output document.
{
$project: {
_id: 0,
category: "$_id",
qty: "$qty",
totalAmount: "$totalAmount"
}
}
Meanwhile, the first stage $project doesn't need.
db.collection.aggregate([
{
$group: {
_id: "$category",
qty: {
$sum: 1
},
totalAmount: {
$sum: "$amount"
}
}
},
{
$project: {
_id: 0,
category: "$_id",
qty: "$qty",
totalAmount: "$totalAmount"
}
}
])
Sample Mongo Playground
You can use the following query to get your expected output. cheers~
await Product.aggregate([
{
$group: {
_id: "$category",
qty: {
$sum: 1
},
totalAmount: {
$sum: "$amount"
},
},
},
{
$addFields: {
category: "$_id"
}
},
{
$project: {
_id: 0
},
}
])

Pull in mongoose doesn't wanna work. Any suggestions?

Here's my object:
{
posts: [
{
_id: 603dd892548e9b30201cb254,
title: '222222222',
content: '222222222',
__v: 0
},
{
_id: 603df0a5996e422c6cbd05d6,
title: 'ANdrew',
content: 'testestetsetste',
__v: 0
},
{
_id: 603df53790787539a8c92472,
title: 'vvvvvvvvvvvvvvvvvvv',
content: 'vvvvvvvvvvvvvvvvvvvvvvvvvv',
__v: 0
},
{
_id: 603e2abfa384381c28529c76,
title: 'qqqqqqqqqqqqqq',
content: 'qqqqqqqqqqqqqqqqqq',
__v: 0
},
{
_id: 603e2b36550cd6289caa880b,
title: '333',
content: '333',
__v: 0
}
],
_id: 603dd326b8c54d1ac0afea0f,
username: 'andrew',
mail: 'mail#gmail.com',
__v: 5
}
From array "posts" I wanna delete any item I want, so I did:
User.findByIdAndUpdate(req.user.id, {$pull: {posts: { _id: postID}}}, function(err, obj) {
if(!err){
res.redirect("/")
} });
Page refreshes, but every note is still there. What's going on? i tried updateOne too, and object.notes.pull({_id: postID}). All without errors as well as without deletions.

Use group by on a group by result in mongoose

I am working on a NodeJS and a Mongoose Project and I have the following two schemas.
UserSchema.js
const UserSchema = mongoose.Schema({
name: {
type: String,
required: true,
trim: true,
},
incharge: {
type: String,
enum: ['Adhihariharan', 'Anuja', 'Dhivya', 'Govind', 'Joann'],
required: true
},
)}
ContactSchema.js
const ContactSchema = new mongoose.Schema(
{
name: {
type: String,
trim: true,
required: [true, 'Please add a name'],
},
status: {
type: String,
enum: [
'Not Called',
'Wrong Number',
'Called/Declined',
'Called/Not Reachable',
'Called/Postponed',
'Called/Accepted',
'Emailed/Awaiting Response',
'Emailed/Declined',
'Emailed/Confirmed',
],
default: 'Not Called',
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true,
},
I am looking for a query which would give me a result which looks as the following:
[
{
_id: "5d7a514b5d2c12c7449be048",
name: "Benita",
incharge: "Joann",
statuses: [
{ status: "Not Called", count: 1 },
{ status: "Called/Accepted", count: 1 },
{ status: "Called/Declined", count: 1 },
{ status: "Called/Not Reachable", count: 1 },
]
},
{
_id: "5d7a514b5d2c12c7449be048",
name: "Febia",
incharge: "Dhivya",
statuses: [
{ "Not Called": 2 },
{ "Called/Postponed": 2 },
{ "Called/Declined": 3 },
{ "Called/Not Reachable": 1 },
]
},
... and so on
]
Here, the integer, is the number of times that status appears for a particular user and in charge is the manager in charge of the user. The _id mentioned is the ID of the user.
The _id, user, in charge belong to the UserSchema and the status belongs to the ContactSchema
I have tried the following query:
teams = await Contact.aggregate([
{
$group: {
_id: { user: '$user', status: '$status' },
count: { $sum: '$count' },
},
},
{
$lookup: {
from: 'members',
localField: '_id.user',
foreignField: '_id',
as: 'user',
},
},
{
$unwind: { path: '$user' },
},
{
$project: {
'user.name': 1,
'user.incharge': 1,
count: 1,
},
},
{
$sort: { 'user.incharge': 1, 'user.name': 1 },
},
]);
And the following was the output:
{
_id: { user: 5ff52b10fa237b001c93ef18, status: 'Not Called' },
count: 1,
user: { name: 'Benita', incharge: 'Joann' }
},
{
_id: { user: 5ff4ca05fa237b001c93ef15, status: 'Not Called' },
count: 2,
user: { name: 'Febia', incharge: 'Dhivya' }
},
{
_id: { user: 5ff4ca05fa237b001c93ef15, status: 'Called/Accepted' },
count: 4,
user: { name: 'Febia', incharge: 'Dhivya' }
}
Can someone please help me get the desired result?
Thanks in advance.
EDIT:
I did try #turivishal's approach but this is what I got:-
{
_id: 5ff52b10fa237b001c93ef18,
name: 'Sadana',
incharge: 'Joann',
statuses: [ [Object] ]
},
{
_id: 5ff4ca05fa237b001c93ef15,
name: 'Sudarshan B',
incharge: 'Joann',
statuses: [ [Object], [Object] ]
}
Can you please tell me how I can access the [Object] inside the status array in mongoose so that I can get a result as below...
{
_id: "5ff4ca05fa237b001c93ef15",
name: "Sudarshan B",
incharge: "Joann",
statuses: [
{ "Not Called": 2 },
{ "Called/Postponed": 2 },
]
},
You can try lookup with aggregation pipeline,
$lookup with contact pass _id in let,
$match user id condition
$group by status and get total count
$project to change name of the key and value
$addFields to convert statuses array to object using $arrayToObject
teams = await User.aggregate([
{
$lookup: {
from: "contact",
let: { user: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$user", "$user"] } } },
{
$group: {
_id: "$status",
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
k: "$_id",
v: "$count"
}
}
],
as: "statuses"
}
},
{ $addFields: { statuses: { $arrayToObject: "$statuses" } } }
])
Playground

Mongoose: I need to calculate totalizer for some fields in aggregate

I have a query (aggregate) and I need to calculate a totalizer for some fields (value and comissionValue) and count how many registers this query have.
My query(aggregate)
let findTerm = {
store: req.body.store,
status: {
$in: resultStatusServices
}
}
if (req.body.start) {
findTerm.scheduleStart = {
$lte: req.body.start
};
}
if (req.body.period) {
findTerm.scheduleEnd = {
$gte: req.body.period
};
}
Schedule.aggregate([{
$match: findTerm
},
{
$project: {
"employee.name": 1,
"customer.name": 1,
"service.name": 1,
"value": 1,
"scheduleDate": 1,
"scheduleStart": 1,
"scheduleEnd": 1,
"comissionValue": 1,
"status": 1,
"paymentMethod": 1
}
},
{
$group:{
_id: {
"employee.name" : "$employee.name",
"customer.name" : "$customer.name",
"service.name": "$service.name",
"value": "$value",
"scheduleDate": "$scheduleDate",
"scheduleStart" :"$scheduleStart",
"scheduleEnd": "$scheduleEnd",
"comissionValue" : "$comissionValue",
"status" : "$value",
"paymentMethod" : "$paymentMethod"
},
}
},
{
$match: findTerm
},
{
$group: {
_id: {
id: "$store"
},
totalValue: {
$sum: "$value"
},
totalServices: {
"$sum": 1
},
totalComission: {
$sum: "$comissionValue"
},
count: {
$sum: 1
}
}
},
{
$sort: sort
},
{
$skip: req.body.limit * req.body.page
},
{
$limit: req.body.limit
}
Schedule (model)
store: {
type: String,
required: true
},
customer: {
id: {
type: String
},
name: {
type: String
},
avatar: String,
phone: {
type: String
},
email: { type: String },
doc: {
type: String
},
},
employee: {
id: {
type: String,
required: true
},
name: {
type: String,
required: true
},
avatar: String,
},
service: {
id: {
type: String
},
name: {
type: String,
required: true
},
filters: [String]
},
info: {
channel: {
type: String,
required: true,
default: 'app'
},
id: String,
name: String
},
scheduleDate: {
type: String,
required: true
},
scheduleStart: {
type: String,
required: true
},
scheduleEnd: {
type: String,
required: true
},
value: {
type: Number
},
comissionType: {
type: String,
default: '$'
},
comissionValue: {
type: Number,
default: 0
},
status: {
type: Number,
required: true
},
observation: String,
paymentMethod: {
type: Number,
default: 0
},
What I'am trying to do as a result of this query:
[
0:{
comissionValue: 14
customer: {name: "Marcelo"}
employee: {name: "Andy"}
paymentMethod: 0
scheduleDate: "2019-01-01"
scheduleEnd: "2019-01-01 09:30"
scheduleStart: "2019-01-01 09:00"
service: {name: "Barber"}
status: 2
value: 20
_id: "5c26275ffe046d25a07cb466"}
1: {...}
2: {...}
...
],[totalizers: { count: 2, totalServices: 50, totalComission:65}]
How can i do this, how can i make this totalizers?
You can use $facet to accomplish this type of query since it allows you to run various aggregations on the same set of input documents, without needing to retrieve the input documents multiple times. The first facet can have the query pipeline with the sort and limit and the other facet will yield the aggregate sums (totalizers).
For instance the following aggregate operation will give you the desired result:
Schedule.aggregate([
{ '$match': findTerm },
{ '$facet': {
'data': [
{ '$project': {
'employee.name': 1,
'customer.name': 1,
'service.name': 1,
'value': 1,
'scheduleDate': 1,
'scheduleStart': 1,
'scheduleEnd': 1,
'comissionValue': 1,
'status': 1,
'paymentMethod': 1
} },
{ '$sort': sort },
{ '$skip': req.body.limit * req.body.page },
{ '$limit': req.body.limit }
],
'totalizer': [
{ '$group': {
'_id': '$store',
'count': { '$sum': 1 },
'totalValue': { '$sum': '$value' },
'totalComission': { '$sum': '$comissionValue' }
} },
{ '$group': {
'_id': null,
'storesCount': {
'$push': {
'store': '$_id',
'count': '$count'
}
},
'totalValue': { '$sum': '$totalValue' },
'totalServices': { '$sum': '$count' },
'totalComission': { '$sum': '$totalComission' }
} }
]
} }
]).exec((err, results) => {
if (err) handleError(err);
console.log(results[0]);
})

Create document arrays with least and highest count on a grouping key

I have below collection
[
{
_id: {
giftId: "coin",
userId: "5b839dfeaaafc94ff323da35"
},
count: 1
},
{
_id: {
giftId: "coin",
userId: "5b8390cc4bf35e13fe67d545"
},
count: 2
},
{
_id: {
giftId: "Gold",
userId: "5b8390cc4bf35e13fe67d545"
},
count: 3
},
{
_id: {
giftId: "Gold",
userId: "5b8390cc4be35e13fe67d545"
},
count: 1
},
{
_id: {
giftId: "Silver",
userId: "5b8390cc4bf35e13fe67d545"
},
count: 4
},
{
_id: {
giftId: "Silver",
userId: "5b8390cc4bf35e13ff67d545"
},
count: 2
}
]
I need below output
[{
array1: [{
_id: {
giftId: "coin",
userId: "5b8390cc4bf35e13fe67d545"
},
count: 2
},
{
_id: {
giftId: "Gold",
userId: "5b8390cc4bf35e13fe67d545"
},
count: 3
},
{
_id: {
giftId: "Silver",
userId: "5b8390cc4bf35e13fe67d545"
},
count: 4
}],
array2: [{
_id: {
giftId: "coin",
userId: "5b839dfeaaafc94ff323da35"
},
count: 1
}, {
_id: {
giftId: "Silver",
userId: "5b8390cc4bf35e13ff67d545"
},
count: 2
},{
_id: {
giftId: "Gold",
userId: "5b8390cc4be35e13fe67d545"
},
count: 1
}]
}]
I need count for each giftId in an array of objects.
Suppose in above document for giftId coin we have 2 count and for giftId Gold we have 3 count
and for giftId Silver we have 4 count. So all the values with hight count should be in same array.
Any help would be highly appriciated!!!
I am using latest version of mongodb 4.0
You can use the below aggregation query.
$sort to sort the data by count.
Initial $group to get the max and min doc element for each gift_id followed by $group to push the max and min value documents into array.
db.colname.aggregate([
{"$sort":{"count":-1}},
{"$group":{"_id":"$_id.giftId","maxdoc":{"$first":"$$ROOT"}, "mindoc":{"$last":"$$ROOT"}}},
{"$group":{"_id":null,"array1":{"$push":{"_id":"$maxdoc._id", "count":"$maxdoc.count"}}, "array2":{"$push":{"_id":"$mindoc._id", "count":"$mindoc.count"}}}},
{"$project":{"_id":0}}
])

Resources