aggregation with group by, inner join and nested conditions in MongoDB - mongodb

First of all I am sorry if this is a basic question, I am new to queries in MongoDB. Well, what I need is to find the latest registers for a worker in my WorkerLocationContext document and the latest register for each sensor in my HeatMeasureContext document, then join it by their location and then apply some filters. Here are my Schemas:
HeatMeasureContext:
const heatMeasureContextSchema = new mongoose.Schema({
sensor: { type: Schema.Types.ObjectId, ref: 'MeasureSensor', required: true },
humid: { type: Schema.Types.Number, required: true },
globe: { type: Schema.Types.Number, required: true },
mercury: { type: Schema.Types.Number, required: true },
internal: { type: Schema.Types.Number, required: true },
external: { type: Schema.Types.Number, required: true }
}, { timestamps: true })
MeasureSensor:
const measureSensorSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: false },
type: { type: String, required: false, uppercase: true,
enumValues: ['MEASURE'], default: 'MEASURE' },
location: { type: Schema.Types.ObjectId, ref: 'Location' },
sensorType: { type: String, required: false, uppercase: true,
enumValues: ['WORKER_ATTACHED', 'ENVIRONMENT'], default: 'ENVIRONMENT' },
measurerType: { type: String, required: false, uppercase: true,
enumValues: ['HEAT', 'RUID'] },
placementType: { type: String, required: false, uppercase: true,
enumValues: ['INTERNAL', 'EXTERNAL'], default: 'INTERNAL' }
})
WorkerLocationContext:
const workerLocationContextSchema = new mongoose.Schema({
sensor: { type: Schema.Types.ObjectId, ref: 'LocationSensor', required: true },
worker: { type: Schema.Types.ObjectId, ref: 'Worker', required: true }
}, { timestamps: true })
Location
const locationSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: false },
type: { type: String, required: false, uppercase: true,
enumValues: ['REST', 'ROOM', 'COURTYARD'], default: 'ROOM' }
})
Worker
const workerSchema = new mongoose.Schema({
name: { type: String, required: true },
workGroup: { type: Schema.Types.ObjectId, ref: 'WorkGroup', required: false }
})
I have built my query like this:
WorkerLocationContext.aggregate([
{
"$lookup": {
"from": "HeatMeasureContext",
"localField": "sensor.location._id",
"foreignField": "sensor.location._id",
"as": "HMContext"
}
},
{
"$match": {
"$and": [
{ "$or": [
{ "$and": [
{
"HMContext.sensor.placementType": { "$eq": "INTERNAL" }},
{"HMContext.internal": { "$gte": limit}
},
{
"HMContext.sensor.placementType": { "$eq": "EXTERNAL" }},
{"HMContext.external": { "$gte": limit}
},
]},
]},
{ "WorkerLocationContext.worker.location.type": { "$ne": "REST" } }
]
}
},
{
"$group": {
"_id": "null",
"workers": {
"$count": {}
},
"hmDatetime": {
"$max": "$HMContext.createdAt"
},
"wlDatetime": {
"$max": "$WorkerLocationContext.createdAt"
}
}
}
]);
Basically, my goal with it is to count how many workers fit in that condition according to their current location, thus the latest registers in the context tables. I have tried some simulations in the mongoplayground, but nothing succeeded. Is it possible to be done in MongoDB? Can you help me?
Thanks in advance!
Edit 1
Sample Data
- Worker
[
{ "_id": "6181de993fca98374cf901f6", "name": "Worker 1", "workGroup": "6181de3e3fca98374cf901f4", "__v": 0 },
{ "_id": "6181dec33fca98374cf901f7", "name": "Worker 2", "workGroup": "6181de4a3fca98374cf901f5", "__v": 0 },
{ "_id": "6181decc3fca98374cf901f8", "name": "Worker 3", "workGroup": "6181de4a3fca98374cf901f5", "__v": 0 },
{ "_id": "6181ded13fca98374cf901f9", "name": "Worker 4", "workGroup": "6181de4a3fca98374cf901f5", "__v": 0 }
]
- Location
[
{ "_id": "6181df293fca98374cf901fa", "name": "Location 1", "description": "Rest place", "__v": 0, "type": "ROOM" },
{ "_id": "6181df3b3fca98374cf901fb", "name": "Location 2", "description": "Room 1", "__v": 0, "type": "ROOM" }
]
- MeasureSensor
[
{ "_id": "6181e5ae3fca98374cf901fc", "name": "Sensor 1", "description": "Heat Sensor 1", "location": "6181df3b3fca98374cf901fb", "measurerType": "HEAT", "__v": 0, "placementType": "INTERNAL", "sensorType": "ENVIRONMENT", "type": "MEASURE" }
]
- LocationSensor
[
{ "_id": "6181e5f83fca98374cf901fd", "name": "Location Sensor 1", "description": "Location sensor for Location 2", "location": "6181df3b3fca98374cf901fb", "trackerType": "RFID", "__v": 0, "sensorType": "ENVIRONMENT", "type": "LOCATION" }
]
- WorkerLocationContext
[
{ "_id": "615676c885ccad55a493503b", "updatedAt": "2021-10-01T02:47:36.207Z", "createdAt": "2021-10-01T02:47:36.207Z", "sensor": "615657572079a55f7814947b", "worker": "6153dcfb58ad722c747eb42d", "__v": 0 },
{ "_id": "618311b56b77f445ecf73277", "updatedAt": "2021-11-03T22:48:21.887Z", "createdAt": "2021-11-03T22:48:21.887Z", "sensor": "6181e5f83fca98374cf901fd", "worker": "6181de993fca98374cf901f6", "__v": 0 },
{ "_id": "618311c86b77f445ecf73278", "updatedAt": "2021-11-03T22:48:40.507Z", "createdAt": "2021-11-03T22:48:40.507Z", "sensor": "6181e5f83fca98374cf901fd", "worker": "6181decc3fca98374cf901f8", "__v": 0 }
]
- HeatMeasureContext
[
{ "_id": "61831b796b77f445ecf7327b", "updatedAt": "2021-11-03T23:30:01.640Z", "createdAt": "2021-11-03T23:30:01.640Z", "sensor": "6181e5ae3fca98374cf901fc", "mercury": 25.8, "humid": 23.5, "globe": 25.5, "external": 24.13, "internal": 24.1, "__v": 0 },
{ "_id": "61831bc96b77f445ecf7327c", "updatedAt": "2021-11-03T23:31:21.080Z", "createdAt": "2021-11-03T23:31:21.080Z", "sensor": "6181e5ae3fca98374cf901fc", "mercury": 28.6, "humid": 27.8, "globe": 27, "external": 27.72, "internal": 27.56, "__v": 0 }
]
Edit 2
I had to simplify a bit my query because some expressions like heatMeasureContex.sensor.location wouldn't work in there (as far as I know), but here is a simple trial that is not working, and isn't even the half of what I need: mongopplaygroung.net

You can start an aggregation pipeline from the HeatMeasureContext collection:
$match on the internal or external field
$lookup the WorkerLocationContext collection using an sub-pipeline. In the sub-pipeline, $sum the worker count and get the $max wlDatetime
$unwind the result for further processing
$group again on HeatMeasureContext.location, use $first to get the result in sub-pipeline and $max to get the hmDatetime
db.HeatMeasureContext.aggregate([
{
$match: {
$expr: {
$or: [
{
$gte: [
"$internal",
27
]
},
{
$gte: [
"$external",
27
]
}
]
}
}
},
{
"$lookup": {
"from": "WorkerLocationContext",
let: {
loc: "$location"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$loc",
"$location"
]
}
}
},
{
$group: {
_id: "$location",
"workers": {
"$sum": 1
},
"wlDatetime": {
"$max": "$createdAt"
}
}
}
],
"as": "workerAggResult"
}
},
{
$unwind: "$workerAggResult"
},
{
$group: {
_id: "$location",
"hmDatetime": {
$max: "$createdAt"
},
"wlDatetime": {
$first: "$workerAggResult.wlDatetime"
},
"workers": {
$first: "$workerAggResult.workers"
}
}
}
])
Here is the Mongo playground for your reference.

Well, after some studying and going through the MongoDB University Aggregation Framework course (which I found out to be very good, I highly recommend), I was able to figure how to do what I needed:
db.heatmeasurecontexts.aggregate([
{
'$group': {
'_id': '$sensor',
'createdAt': {
'$last': '$createdAt'
},
'external': {
'$last': '$external'
},
'internal': {
'$last': '$internal'
}
}
}, {
'$lookup': {
'from': 'measuresensors',
'localField': '_id',
'foreignField': '_id',
'as': 'sensor'
}
}, {
'$unwind': {
'path': '$sensor'
}
}, {
'$match': {
'$or': [
{
'$and': [
{
'sensor.placementType': {
'$eq': 'INTERNAL'
}
}, {
'internal': {
'$gte': 27
}
}
]
}, {
'$and': [
{
'sensor.placementType': {
'$eq': 'EXTERNAL'
}
}, {
'external': {
'$gte': 25
}
}
]
}
]
}
}, {
'$project': {
'_id': 0,
'measure_sensor_location': '$sensor.location'
}
}, {
'$lookup': {
'from': 'locationsensors',
'localField': 'measure_sensor_location',
'foreignField': 'location',
'as': 'location_sensor'
}
}, {
'$unwind': {
'path': '$location_sensor'
}
}, {
'$lookup': {
'from': 'workerlocationcontexts',
'localField': 'location_sensor._id',
'foreignField': 'sensor',
'as': 'worker_location_context'
}
}, {
'$unwind': {
'path': '$worker_location_context'
}
}, {
'$project': {
'worker_location_context': 1,
'location_sensor': 1
}
}, {
'$lookup': {
'from': 'workers',
'localField': 'worker_location_context.worker',
'foreignField': '_id',
'as': 'worker_location_context.worker'
}
}, {
'$unwind': {
'path': '$worker_location_context.worker'
}
}, {
'$match': {
'$expr': {
'$eq': [
'$location_sensor.location', '$worker_location_context.worker.currentLocation'
]
}
}
}, {
'$group': {
'_id': {
'sensor': '$worker_location_context.sensor',
'sensor_location': '$location_sensor.location',
'worker': '$worker_location_context.worker._id',
'worker_location': '$worker_location_context.worker.currentLocation'
},
'createdAt': {
'$last': '$worker_location_context.createdAt'
}
}
}, {
'$count': 'workers_at_risk'
}
])
I hope it may be useful eventually, despite the fact that this is a very specific scenario.

Related

MongoDB aggregate using $match with $expr with array

MongoDB 5.0.9
I am trying to get
value of application within course and their specification
value of paid application ( status : paid) based on course and their specification
courses collection having multiple courses with specification which might be there maybe not
[
{
"_id": {
"$oid": "62aab6669b3740313d881a30"
},
"course_name": "Master",
"fees": "Rs.1000.0/-",
"course_specialization": [
{
"spec_name": "Social Work",
"is_activated": true
}
],
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"is_pg": true
},
{
"_id": {
"$oid": "62aab6669b3740313d881a38"
},
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"course_name": "BBA",
"fees": "Rs.1000.0/-",
"is_pg": false,
"course_specialization": null
},
{
"_id": {
"$oid": "628f3967cb69fc0789e69181"
},
"course_name": "BTech",
"fees": "Rs.1000.0/-",
"course_specialization": [
{
"spec_name": "Computer Science and Engineering",
"is_activated": true
},
{
"spec_name": "Mutiple Specs",
"is_activated": true
}
],
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"is_pg": false
},
{
"_id": {
"$oid": "628f35a1cb69fc0789e6917e"
},
"course_name": "Bachelor",
"fees": "Rs.1000.0/-",
"course_specialization": [
{
"spec_name": "Social Work",
"is_activated": true
}
],
"college_id": {
"$oid": "628dfd41ef796e8f757a5c13"
},
"is_pg": false
}
],
Student Application forms collection where we are storing student application forms details
[
{
"_id": {
"$oid": "62cd476adbc878a0490e20ee"
},
"spec_name1": "Social Work",
"spec_name2": "",
"spec_name3": "",
"student_id": {
"$oid": "62cd1374dbc878a0490e20a5"
},
"course_id": {
"$oid": "62aab6669b3740313d881a30"
},
"current_stage": 2.5,
"declaration": true,
"payment_info": {
"payment_id": "123458",
"status": "paid"
},
"enquiry_date": {
"$date": {
"$numberLong": "1657620330432"
}
},
"last_updated_time": {
"$date": {
"$numberLong": "1657621796062"
}
}
},
{
"_id": {
"$oid": "62cd476adbc878a0490e20ef"
},
"spec_name1": "",
"spec_name2": "",
"spec_name3": "",
"student_id": {
"$oid": "62cd1374dbc878a0490e20a5"
},
"course_id": {
"$oid": "62aab6669b3740313d881a38"
},
"current_stage": 2.5,
"declaration": true,
"payment_info": {
"payment_id": "123458",
"status": "paid"
},
"enquiry_date": {
"$date": {
"$numberLong": "1657620330432"
}
},
"last_updated_time": {
"$date": {
"$numberLong": "1657621796062"
}
}
},
{
"_id": {
"$oid": "62cdc12000b820f5ea58cc60"
},
"spec_name1": "Social Work",
"spec_name2": "",
"spec_name3": "",
"student_id": {
"$oid": "62cdad90a9b64d58b15e6976"
},
"course_id": {
"$oid": "628f35a1cb69fc0789e6917e"
},
"current_stage": 6.25,
"declaration": false,
"payment_info": {
"payment_id": "",
"status": ""
},
"enquiry_date": {
"$date": {
"$numberLong": "1657651488511"
}
},
"last_updated_time": {
"$date": {
"$numberLong": "1657651987155"
}
}
}
]
Desired output with every specification within the course
[
"_id": {
"coursename": "Master",
"spec": "Social Work",
"Application_Count": 1,
"Paid_Application_Count:0
},
{
"_id": {
"coursename": "Bachelor"
"spec":"" ,
"Application_Count": 1,
"Paid_Application_Count:0
},
{
"_id": {
"coursename": "BBA"
"spec":"" ,
"Application_Count": 1,
"Paid_Application_Count:1
},
]
Aggregation Query
[{
$match: {
college_id: ObjectId('628dfd41ef796e8f757a5c13')
}
}, {
$project: {
_id: 1,
course_name: 1,
course_specialization: 1
}
}, {
$unwind: {
path: '$course_name',
includeArrayIndex: 'course_index',
preserveNullAndEmptyArrays: true
}
}, {
$unwind: {
path: '$course_specialization',
includeArrayIndex: 'course_specs_index',
preserveNullAndEmptyArrays: true
}
}, {
$lookup: {
from: 'studentApplicationForms',
'let': {
id: '$_id',
spec: '$course_specialization.spec_name'
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
'$course_id',
'$$id'
]
},
{
$eq: [
'$spec_name1',
'$$spec'
]
}
]
}
}
},
{
$project: {
student_id: 1,
payment_info: 1,
spec_name1: 1,
spec_name2: 1,
spec_name3: 1
}
}
],
as: 'student_application'
}
}, {
$unwind: {
path: '$student_application',
includeArrayIndex: 'application',
preserveNullAndEmptyArrays: true
}
}, {
$facet: {
course: [
{
$group: {
_id: {
course_name: '$course_name',
spec: '$course_specialization'
},
count: {
$count: {}
}
}
}
],
declatration: [
{
$group: {
_id: {
course_name: '$course_name',
spec: '$course_specialization'
},
count_dec: {
$sum: {
$cond: [
'$student_application.declaration',
1,
0
]
}
}
}
}
],
payment: [
{
$group: {
_id: {
course_name: '$course_name',
spec: '$course_specialization'
},
payment: {
$sum: {
$eq: [
'$student_application.payment_info.status',
'paid'
]
}
}
}
}
]
}
}]
Problem :
I am able to get application count but it is not getting unique value if 2 specs are same then duplicate value is coming as you can see on sample application collection Social Work is in two different course . So my aggregations is not grouping them based in course name.specs
Not able to find correct Paid_Application_Count and Application_Count
Update :
Updated JSON Data Matching use cases with different type of data
MongoDB Playground
You can do it in several different ways, I took the liberty to simplify the pipeline a little bit.
I will just mention that the structure does not fully make sense to me, and there are some additional contradictions between the sample input you provided and the "text" description/pipeline description.
Just a tiny example is payment_info_status being paid in the sample and capture in the pipeline.
These things will not change the pipeline structure, will just need to be fixed by you based on the actual needs.
db.courses.aggregate([
{
$project: {
_id: 1,
course_name: 1,
course_specialization: 1
}
},
{
$unwind: {
path: "$course_specialization",
preserveNullAndEmptyArrays: true
}
},
{
$lookup: {
from: "studentApplicationForms",
"let": {
courseId: "$_id",
spec: {
$ifNull: [
"$course_specialization.spec_name",
""
]
}
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$spec_name1",
"$$spec"
]
},
{
$eq: [
"$$courseId",
"$course_id"
]
}
]
}
}
},
{
$project: {
student_id: 1,
payment_info: 1,
spec_name1: 1,
spec_name2: 1,
spec_name3: 1,
declaration: 1,
}
},
{
$group: {
_id: null,
count: {
$sum: 1
},
declatration: {
$sum: {
$cond: [
"$declaration",
1,
0
]
}
},
paid: {
$sum: {
$cond: [
{
$eq: [
"$payment_info.status",
"paid"
]
},
1,
0
]
}
},
}
}
],
as: "student_application"
}
},
{
$project: {
_id: {
coursename: "$course_name",
spec: "$course_specialization.spec_name",
Application_count: {
$ifNull: [
{
$first: "$student_application.count"
},
0
]
},
Declaration_count: {
$ifNull: [
{
$first: "$student_application.declatration"
},
0
]
},
Paid_Application_Count: {
$ifNull: [
{
$first: "$student_application.paid"
},
0
]
},
}
}
}
])
Mongo Playground

MongoDB Aggregation - Select only same value from array inside lookup

I have aggregation like this:
Produk.aggregate([
{
$lookup: {
from: "kis_m_kategoriproduks",
localField: "idSubKategori",
foreignField: "subKategori._id",
as: "kategori",
},
},
{ $unwind: "$kategori" },
{ $sort: { produk: 1 } },
{
$project: {
_id: 0,
id: "$id",
dataKategori: {
idKategori: "$kategori._id",
kategori: "$kategori.kategori",
idSubKategori: "$idSubKategori",
subKategori: "$kategori.subKategori",
},
},
},
])
current result is :
{
"status": "success",
"data": [
{
"dataKategori": {
"idKategori": "6195bbec8ee419e6a9b8329d",
"kategori": "Kuliner",
"idSubKategori": "6195bc0f8ee419e6a9b832a2",
"subKategori": [
{
"nama": "Food",
"_id": "6195bc0f8ee419e6a9b832a2"
},
{
"nama": "Drink",
"_id": "6195bc258ee419e6a9b832a8"
}
]
}
}
]
}
I only want to display data in subKategori that the _id match with idSubKategori. this what I expected:
{
"status": "success",
"data": [
{
"dataKategori": {
"idKategori": "6195bbec8ee419e6a9b8329d",
"kategori": "Kuliner",
"idSubKategori": "6195bc0f8ee419e6a9b832a2",
"subKategori": [
{
"nama": "Food",
"_id": "6195bc0f8ee419e6a9b832a2"
}
]
}
}
]
}
here is my $kategori schema:
const schema = mongoose.Schema(
{
kategori: {
type: String,
required: true,
unique: true,
},
subKategori: [
{
id: mongoose.Types.ObjectId,
nama: String,
},
],
},
{
timestamps: false,
}
);
any suggestion?
I fix the problem by add $filter inside $project like this:
dataKategori: {
idKategori: "$kategori._id",
kategori: "$kategori.kategori",
subKategori: {
$arrayElemAt: [
{
$filter: {
input: "$kategori.subKategori",
as: "sub",
cond: { $eq: ["$$sub._id", "$idSubKategori"] },
},
},
0,
],
},
},
reference: https://stackoverflow.com/a/42490320/6412375

Mongo Sort Aggregation not working on Sub Document

Trying to sort the sub-key on the document.
Example of data from the pipeline. Incorrect sort order for subkey availability.startIso
{
"_id": "60e458d7b896de9c8e44d6c9",
"uid": "6233ed1d8b154aa79d1435b5",
"name": "Pale",
"phoneNumber": "+19999813917",
"profileMedia": {
"url": "https://storage.googleapis.com/refresh-me-dev.appspot.com/dummy_photos/dummy_1.jpg",
"type": "photo"
},
"createdIso": "2021-07-06T13:21:27.513Z",
"isDeleted": false,
"isFlagged": false,
"isBanned": false,
"isAdmin": false,
"isVendor": true,
"lastOpenedAppIso": "2021-07-06T13:21:27.513Z",
"vendorMeta": {
"servicesOffered": [
"swedish"
],
"location": [
0,
0
]
},
"distanceFromPoint": 0,
"availability": [
{
"_id": "60e458d7b896de9c8e44d6cc",
"uid": "dec97d4b1dea44f7b2fa45a5",
"vendorUid": "6233ed1d8b154aa79d1435b5",
"startIso": "2021-07-12T04:07:21.349Z",
"endIso": "2021-07-12T05:07:21.360Z"
},
{
"_id": "60e458d7b896de9c8e44d6ce",
"uid": "a5928ea5c18c4321bd6a9a9b",
"vendorUid": "6233ed1d8b154aa79d1435b5",
"startIso": "2021-07-11T01:52:18.323Z",
"endIso": "2021-07-11T02:52:18.335Z"
}
]
}
Example of the aggregation
let vendors = await mongoDb
.collection<User>(collectionNames.users)
.aggregate([
{
$geoNear: {
near: { type: "Point", coordinates: [lat, lng] },
spherical: true,
maxDistance: 7500,
distanceField: "distanceFromPoint",
},
},
{
$match: { isVendor: true },
},
{
$match: { "vendorMeta.servicesOffered": { $in: services } },
},
{
$lookup: {
from: "vendor.availability",
localField: "uid",
foreignField: "vendorUid",
as: "availability",
},
},
{
$addFields: {
availability: {
$filter: {
input: "$availability",
as: "availability",
cond: { $and: [{ $gte: ["$$availability.startIso", nowIso] }, { $lte: ["$$availability.endIso", nDaysIso] }] },
},
},
},
},
{ $sort: { "availability.startIso": 1 } },
{ $match: { availability: { $ne: [] } } },
])
.toArray();
This is working as intended, $sort does not work on arrays and can't be used like this. What you can do is $unwind, then $sort and end by $grouping to restore the structure, like so:
[
// ...,
{
$unwind: "$availability"
},
{ $sort: { "availability.startIso": 1 } },
{
$group: {
_id: '$_id',
root: {$first: "$$ROOT"},
availability: {$push: '$availability'}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
'$root',
{ availability: '$availability'}
]
}
}
}
]
Note that i removed the :
{ $match: { availability: { $ne: [] } } },
As it's no longer required because $unwind will remove those documents for you.

Mongoose lookup returning wrong result

I have two schemas:
const userSchema = new Schema(
{
username: {
type: String,
required: true,
},
password: {
type: String,
required: true,
},
},
{
timestamps: true,
}
);
const userLikeSchema = new Schema(
{
userId: {
type: String,
required: true,
ref: 'User',
},
likedUserId: {
type: String,
required: true,
ref: 'User',
},
isActive: {
type: Boolean,
required: true,
default: true,
},
},
{
timestamps: true,
}
);
I am trying to get a list of users ordered descending by their number of likes.
I am not sure if the schema I did sql-like is correct.
I have written the following query:
const likedUsers = await UserLike.aggregate(
[
{
$group: {
_id: '$likedUserId',
likes: { $sum: 1 },
},
},
{ $sort: { likes: -1 } },
{
$lookup: {
from: 'users',
localField: 'likedUserId',
foreignField: '_id.str',
as: 'user',
},
},
]
);
I am trying to get a result like:
[
{
"_id": "604bb648be8680063009fddc",
"likes": 2,
"user": {
"_id": "604bb648be8680063009fddc",
"username": "muhamed",
"password": "$2b$10$EVYWZb4vl2TFGmIrCWe1sO/QogdU6/Ui8TgujY4PMKLJKIVOzmOi6",
"createdAt": "2021-03-12T18:43:20.806Z",
"updatedAt": "2021-03-12T18:46:17.635Z",
"__v": 0
},
}
]
But instead I am getting the following which shows every user for each item:
[
{
"_id": "604bb648be8680063009fddc",
"likes": 2,
"user": [
{
"_id": "604bb648be8680063009fddc",
"username": "muhamed",
"password": "$2b$10$EVYWZb4vl2TFGmIrCWe1sO/QogdU6/Ui8TgujY4PMKLJKIVOzmOi6",
"createdAt": "2021-03-12T18:43:20.806Z",
"updatedAt": "2021-03-12T18:46:17.635Z",
"__v": 0
},
{
"_id": "604bc703ea4bf93fb427056a",
"username": "krasniqi",
"password": "$2b$10$dnKumHhKNIfA6BM3uekymOpIdMFuQy9aYYKmGBxnW401CjTAuMLIy",
"createdAt": "2021-03-12T19:54:43.368Z",
"updatedAt": "2021-03-12T19:54:43.368Z",
"__v": 0
},
{
"_id": "604c90ab9f7b970cd46ff668",
"username": "matin",
"password": "$2b$10$CEUCaGk.JF5PBwsTBE3fRufVXzUBt.eLyo28eTt8zhBezVSFflMhS",
"createdAt": "2021-03-13T10:15:07.877Z",
"updatedAt": "2021-03-13T10:15:07.877Z",
"__v": 0
}
]
},
]
What am I missing ?
I think there is an issue about _id.str.
You have set your userLikeSchema wrong. You should have set the type of your ID fields mongoose.Schema.ObjectId:
const userLikeSchema = new Schema(
{
userId: {
type: mongoose.Schema.ObjectId,
required: true,
ref: 'User',
},
likedUserId: {
type: mongoose.Schema.ObjectId,
required: true,
ref: 'User',
},
isActive: {
type: Boolean,
required: true,
default: true,
},
},
{
timestamps: true,
}
);
After that, you can easily lookup by _id:
const likedUsers = await UserLike.aggregate(
[
{
$group: {
_id: '$likedUserId',
likes: { $sum: 1 },
},
},
{ $sort: { likes: -1 } },
{
$lookup: {
from: 'users',
localField: '_id',
foreignField: '_id',
as: 'user',
},
}, {
$unwind: "$user"
}
]
);
As a result of $lookup you will get an array of user, so you need to use $unwind to get a single User object.
Result will be like:
{
"_id": "604bb648be8680063009fddc",
"likes": 2,
"user": {
"_id": "604bb648be8680063009fddc",
"username": "muhamed",
"password": "$2b$10$EVYWZb4vl2TFGmIrCWe1sO/QogdU6/Ui8TgujY4PMKLJKIVOzmOi6",
"createdAt": "2021-03-12T18:43:20.806Z",
"updatedAt": "2021-03-12T18:46:17.635Z",
"__v": 0
},
}

MongoDB nested Aggregate Grouping

Sample Data:
[
{type: 'partial', jobId: '121', browser: 'chrome', status:'true', jobName:'one'},
{type: 'partial', jobId: '122', browser: 'chrome', status:'false', jobName:'two'},
{type: 'partial', jobId: '121', browser: 'firefox', status:'false', jobName:'one'},
{type: 'partial', jobId: '122', browser: 'firefox', status:'true', jobName:'two'},
{type: 'full', jobId: '123', browser: 'chrome', status:'true', jobName:'three'},
{type: 'full', jobId: '123', browser: 'chrome', status:'true', jobName:'three'},
{type: 'full', jobId: '123', browser: 'chrome', status:'false', jobName:'three'},
{type: 'full', jobId: '124', browser: 'firefox', status:'false', jobName:'four'},
]
Output Needed:
[
{
"type": "partial",
"browsers": [
{
"browser": "chrome",
"jobIds": [
{
"jobId": "121",
"results": [
{
"jobName": "one",
"status": "true",
},
]
},
{
"jobId": "122",
"results": [
{
"jobName": "two",
"status": "false"
},
]
}
]
},
{
"browser": "firefox",
"testIds": [
{
"jobId": "121",
"results": [
{
"jobName": "one",
"status": "false"
},
]
},
{
"jobId": "122",
"results": [
{
"jobName": "two",
"status": "true"
},
]
}
]
}
]
},
{
"type": "full",
"browsers": [
{
"browser": "chrome",
"jobIds": [
{
"jobId": "123",
"results": [
{
"jobName": "three",
"status": "true"
},
{
"jobName": "three",
"status": "true"
},
{
"jobName": "three",
"status": "false"
}
]
},
]
},
{
"browser": "firefox",
"testIds": [
{
"jobId": "124",
"results": [
{
"jobName": "four",
"status": "false"
},
]
},
]
}
]
}
]
I understand how to use group, but then I don't understand how to make the nested grouping. I tried the below query, it is not fetching needed results, I don't know how to proceed further.
db.collection.aggregate([
{
$match: {
jobId: {
"$exists": true
}
}
},
{
$sort: {
_id: -1
}
},
{
$group: {
_id: {
type: "$type",
browser: "$browser",
jobId: "$jobId"
},
results: {
$push: {
jobName: "$jobName",
status: "$status",
type: "$type",
jobId: "$jobId"
}
}
}
},
{
$addFields: {
results: {
$slice: [
"$results",
30
]
}
}
},
{
$group: {
_id: "$_id.browser",
results: {
$push: {
results: "$results"
}
}
}
},
])
Need fetch recent 30 results, that's why I added $addFields in query.
https://mongoplayground.net/p/pt3H1O445GA
$group by type, browser and jobId and make results array
$group by type and browser and make jobs array
$group by type and make browsers array
db.collection.aggregate([
{ $match: { jobId: { $exists: true } } },
{ $sort: { _id: -1 } },
{
$group: {
_id: {
type: "$type",
browser: "$browser",
jobId: "$jobId"
},
results: {
$push: {
jobName: "$jobName",
status: "$status"
}
}
}
},
{ $addFields: { results: { $slice: ["$results", 30] } } },
{
$group: {
_id: {
type: "$_id.type",
browser: "$_id.browser"
},
browser: { $first: "$_id.browser" },
jobIds: {
$push: {
jobId: "$_id.jobId",
results: "$results"
}
}
}
},
{
$group: {
_id: "$_id.type",
type: { $first: "$_id.type" },
browsers: {
$push: {
browser: "$_id.browser",
jobIds: "$jobIds"
}
}
}
},
{ $project: { _id: 0 } }
])
Playground