Mongoose aggregate with lookup and object array - node.js

I have a collection as follows
// collection: appointments
{
"_id" : ObjectId("5c50682b663e854a1c2d9401"),
"status" : "Pending",
"discount" : 0,
"removed" : false,
"services" : [
{
"_id" : ObjectId("5c505a29af3a655b98812ca7"),
"service" : ObjectId("5c505a12af3a655b98812ca5"),
"cost" : 200
},
{
"_id" : ObjectId("5c50691ab9081f53287d2354"),
"service" : ObjectId("5c5069a600ec0d7a1800aa73"),
"cost" : 200
}
],
"doctor" : ObjectId("5c5059b2af3a655b98812ca1"),
"patient" : ObjectId("5c5059e5af3a655b98812ca4"),
"date" : ISODate("2018-11-12T00:00:00.000+02:00"),
"clinic" : ObjectId("5c5059d8af3a655b98812ca3"),
"diagnosis" : [ ],
"rx" : [ ],
"labs" : [ ],
"scans" : [ ],
"__v" : 0
}
I'm trying to aggregate that collection, but i want to populate services.service as it's an object id
let appointments = await Appointment.aggregate([
{ $lookup: { from: 'services',localField: 'services.service',foreignField: '_id',as: 'services' } },
{ $project: {
'date': 1 ,
'status': 1 ,
'services': 1 ,
} },
{ $limit: Number(req.query.limit) },
{ $skip: Number(req.query.skip) }
]);
what i'm getting
"appointments": [
{
"_id": "5c50682b663e854a1c2d9401",
"status": "Pending",
"discount": 0,
"paidAmount": 0,
"services": [
{
"_id": "5c505a12af3a655b98812ca5",
"removed": false,
"name": "kashf",
"clinic": "5c5059d8af3a655b98812ca3",
"updatedAt": "2019-01-29T13:50:10.651Z",
"createdAt": "2019-01-29T13:50:10.651Z",
"__v": 0
},
{
"_id": "5c5069a600ec0d7a1800aa73",
"removed": false,
"name": "arza3",
"clinic": "5c5059d8af3a655b98812ca3",
"updatedAt": "2019-01-29T14:56:38.314Z",
"createdAt": "2019-01-29T14:56:38.314Z",
"__v": 0
}
],
"date": "2018-11-11T22:00:00.000Z"
}
]
so i lost the cost attribute, also the id of the object array
any solution for this ?
i tried unwinding the services, but it results in two appointments objects with the same id

I've got it
let appointments = await Appointment.aggregate([
{ $unwind: '$services' },
{ $lookup: { from: 'services',localField: 'services.service',foreignField: '_id',as: 'services.service' } },
{ $unwind: '$services.service' },
{
$group: {
'_id': '$_id',
'services': { $push: '$services' },
}
},
{ $project: {
'services': 1 ,
} },
]);

u can use multiple populate lik that
CHAR.findOneAndUpdate({id: info.id}, char, {upsert: true, new: true})
.populate({path : 'intels', populate : {path : 'intels', populate : {path : 'from'}}})
.populate({path : 'alts', populate : {path : 'alts', populate : {path : 'intels', populate : {path : 'intels.from'}}}})

Related

Mongoose aggregation not returning latest document

I have an aggregation that returns a document, but I need it to return a document that is the latest one in the collection messageTrackingId. The one that has the most recent value in the creationDate field I have a sort to accomplish this, but I keep getting returned documents that aren't the latest and I'm not sure why. The purpose is to show the user the time stamp of the last message sent between that user and another user regardless of who sent it. appreciate any help.
app.get("/api/messages", (req, res, next) => {
query = {};
inbox = false;
messageId = false;
console.log(req.query.recipientId);
if (req.query.recipientId) {
query = { recipientId: req.query.recipientId };
inbox = true;
Messages.aggregate([
{
$match: {
$or: [
{ recipientId: req.query.recipientId },
{ creator: mongoose.Types.ObjectId(req.query.recipientId) }
]
}
},
{
$addFields: {
conversant: {
$cond: [
{ $ne: ["$recipientId", req.query.recipientId] },
"$recipientId",
"$creator"
]
}
}
},
{
$sort: { creationDate: 1 }
},
{
$group: {
_id: "$conversant",
message: { $last: "$message" },
recipientId: { $last: "$recipientId" },
creator: { $last: "$creator" },
messageTrackingId: { $last: "$messageTrackingId" },
recipient: { $last: "$recipient" },
creationDate: { $last: "$creationDate" }
}
},
{
$lookup: {
from: "users",
let: { creator: "$creator" },
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$creator"] } } },
{ $project: { instagramName: 1 } }
],
as: "creatorName"
}
}
]).then(documents => {
if (inbox === true) {
res.status(200).json({
message: "User's Inbox Retrieved!",
posts: documents
});
}
});
I cleared out the message table and sent 3 messsages back and forth to demonstrate. When I reload the page, I only receive 2 of the docs in the JSON response instead of the latest one. Of the 2 received, none of them seem to be the latest either. I'm not sure why it's not returning one. Please see output on page load below.
Messages :
/* 1 */
{
"_id" : "5e16463be5fcba1d2c56ebb7",
"creator" : "5e0a1d41f86e1e4234fc1a77",
"recipient" : "andy",
"recipientId" : "5df0014e25ee451beccf588a",
"message" : "First message",
"messageTrackingId" : "17c4fc99-d92f-46ef-9bae-1408f415287a",
"creatorName" : "buyer1",
"creationDate" : ISODate("2020-01-08T21:14:35.104Z"),
"__v" : 0.0
}
/* 2 */
{
"_id" : "5e16464de5fcba1d2c56ebb9",
"creator" : "5df0014e25ee451beccf588a",
"recipient" : "buyer1",
"recipientId" : "5e0a1d41f86e1e4234fc1a77",
"message" : "Second Message",
"messageTrackingId" : "17c4fc99-d92f-46ef-9bae-1408f415287a",
"creatorName" : "andy",
"creationDate" : ISODate("2020-01-08T21:14:53.618Z"),
"__v" : 0.0
}
/* 3 */
{
"_id" : "5e16465ce5fcba1d2c56ebbb",
"creator" : "5e0a1d41f86e1e4234fc1a77",
"recipient" : "andy",
"recipientId" : "5df0014e25ee451beccf588a",
"message" : "Third Message",
"messageTrackingId" : "17c4fc99-d92f-46ef-9bae-1408f415287a",
"creatorName" : "buyer1",
"creationDate" : ISODate("2020-01-08T21:15:08.500Z"),
"__v" : 0.0
}
users :
/* 1 */
{
"_id" : "5df00d08c713f722909c99c1",
"email" : "joe#gmail.com",
"password" : "$2b$10$5eWhwL4RNT8TyQRCC191J.myYBwFcJ8hCARqGfofYWUmRaq6jJFQm",
"instagramName" : "joe",
"over21" : "Yes",
"role" : "Artist",
"fullName" : "testdsfdfsfs",
"address1" : "adfsdf",
"address2" : "dssdf",
"city" : "sdfsdfsdf",
"state" : "dsffsdsd",
"zip" : "dsdfsdf",
"passwordCreated" : ISODate("2020-01-07T23:46:57.861Z"),
"__v" : 0
}
/* 2 */
{
"_id" : "5df0014e25ee451beccf588a",
"email" : "andy#gmail.com",
"password" : "$2b$10$fNHmIoDb4mX6x.YeMLXHbu2yIaeW6HVQvNpZR8Nt/a4xVFkhxM1Ey",
"instagramName" : "andy",
"over21" : "Yes",
"role" : "Artist",
"fullName" : "dsfdsfdfdsf",
"address1" : "111",
"address2" : "111",
"city" : "atlanta",
"state" : "ga",
"zip" : "33222",
"passwordCreated" : ISODate("2019-12-19T15:29:46.528Z"),
"__v" : 0
}
Required output (Return last message from each user) :
/* 1 */
{
"_id" : {
"conversant" : "5df0014e25ee451beccf588a",
"recipient" : "andy"
},
"message" : "Third Message",
"recipientId" : "5df0014e25ee451beccf588a",
"creator" : "5e0a1d41f86e1e4234fc1a77",
"messageTrackingId" : "17c4fc99-d92f-46ef-9bae-1408f415287a",
"recipient" : "andy",
"creationDate" : ISODate("2020-01-08T21:15:08.500Z"),
"creatorName" : [
{
"_id" : "5df0014e25ee451beccf588a",
"instagramName" : "andy"
}
]
}
/* 2 */
{
"_id" : {
"conversant" : "5df0014e25ee451beccf588a",
"recipient" : "buyer1"
},
"message" : "Second Message",
"recipientId" : "5e0a1d41f86e1e4234fc1a77",
"creator" : "5df0014e25ee451beccf588a",
"messageTrackingId" : "17c4fc99-d92f-46ef-9bae-1408f415287a",
"recipient" : "buyer1",
"creationDate" : ISODate("2020-01-08T21:14:53.618Z"),
"creatorName" : [
{
"_id" : "5df0014e25ee451beccf588a",
"instagramName" : "andy"
}
]
}
Changes has to be
1) If I understand correctly at this stage you wanted to group by recipientId on a field(since you can't group on two fields for a single value) So you're creating a new field called conversant, So it has to be $eq or you need to swap recipientId & creator & also it has to be $toString:
{
$addFields: {
conversant: {
$cond: [
{ $eq: ["$recipientId", req.query.recipientId] },
"$recipientId",
{$toString : "$creator"}
]
}
}
}
2) You don't need this $sort stage, as all latest additions to collection will be added at the end (Expecting creationDate is once created but never updated) :
// Not useful
{
$sort: { creationDate: 1 }
}
Also you need to group by _id: { conversant: "$conversant", recipient: '$recipient' } for group on last messages based on user. In $lookup change to this let: { conversant: "$_id.conversant" }.
Try this :
Messages.aggregate([
{
$match: {
$or: [
{ recipientId: req.query.recipientId },
{ creator: mongoose.Types.ObjectId(req.query.recipientId) }
]
}
},
{
$addFields: {
conversant: {
$cond: [
{ $eq: ["$recipientId", req.query.recipientId] },
"$recipientId",
{ $toString: "$creator" }
]
}
}
},
{
$group: {
_id: { conversant: "$conversant", recipient: '$recipient' },
message: { $last: "$message" },
recipientId: { $last: "$recipientId" },
creator: { $last: "$creator" },
messageTrackingId: { $last: "$messageTrackingId" },
recipient: { $last: "$recipient" },
creationDate: { $last: "$creationDate" }
}
},
{
$lookup: {
from: "users",
let: { conversant: "$_id.conversant" },
pipeline: [
{ $match: { $expr: { $eq: ["$_id", "$$conversant"] } } },
{ $project: { instagramName: 1 } }
],
as: "creatorName"
}
},
// this is optional only if you wanted to return latest document irrespective of user.
{ $sort: { creationDate: -1 } }
])

how to get data in mongoose where last element in array?

how to get data in mongoose where last element in array?
I have data looks like this:
[
{
"_id" : ObjectId("5b56eb3deb869312d85a8e69"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
}
]
},
{
"_id" : ObjectId("5b56eb3deb869312d8589765"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:03:30.347Z")
},
{
"status" : "done",
"createdAt" : ISODate("2018-07-24T09:04:22.347Z")
}
]
}
]
And, I want to get data above where last object transactionStatus.status = process, so the result should be:
{
"_id" : ObjectId("5b56eb3deb869312d85a8e69"),
"transactionStatus" : [
{
"status" : "pending",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
},
{
"status" : "process",
"createdAt" : ISODate("2018-07-24T09:02:53.347Z")
}
]
}
how to do that with mongoose?
You can use $expr (MongoDB 3.6+) inside of match. Using $let and $arrayElemAt passing -1 as second argument you can get the last element as a temporary variable and then you can compare the values:
db.col.aggregate([
{
$match: {
$expr: {
$let: {
vars: { last: { $arrayElemAt: [ "$transactionStatus", -1 ] } },
in: { $eq: [ "$$last.status", "process" ] }
}
}
}
}
])
The same result can be achieved for lower versions of MongoDB using $addFields and $match. You can add $project then to remove that temporary field:
db.col.aggregate([
{
$addFields: {
last: { $arrayElemAt: [ "$transactionStatus", -1 ] }
}
},
{
$match: { "last.status": "process" }
},
{
$project: { last: 0 }
}
])
//Always update new status at Position 0 using $position operator
db.update({
"_id": ObjectId("5b56eb3deb869312d85a8e69")
},
{
"$push": {
"transactionStatus": {
"$each": [
{
"status": "process",
"createdAt": ISODate("2018-07-24T09:02:53.347Z")
}
],
"$position": 0
}
}
}
)
//Your Query for checking first element status is process
db.find(
{
"transactionStatus.0.status": "process"
}
)
refer $position, $each

How to use aggregation correctly in MongoDB with embbeded documents?

I have a collection with folowing data:
{
"_id" : ObjectId("5b5066b716d3112cfc2a5deb"),
"username" : "admin",
"password" : "123456",
"token" : "0123",
"bots" : [
{
"name" : "mybot",
"installations" : [
{
"date" : ISODate("2018-07-19T10:23:51.774Z")
},
{
"date" : ISODate("2018-07-19T10:23:51.774Z")
}
],
"commands" : [
{
"name" : "read",
"date" : ISODate("2018-07-19T10:23:51.774Z")
},
{
"name" : "answer",
"date" : ISODate("2018-07-19T10:23:51.774Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:55:28.858Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:56:47.419Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:56:48.499Z")
},
{
"name" : "get",
"date" : ISODate("2018-07-19T11:56:49.089Z")
}
]
}
]
},
{
"_id" : ObjectId("5b50bbfe3ed35b6f2bde6923"),
"username" : "user",
"password" : "123456",
"token" : "44444",
"bots" : [
{
"name" : "anotherBotName",
"installations" : [
{
"date" : ISODate("2018-07-19T16:27:42.012Z")
},
{
"date" : ISODate("2018-07-19T16:27:42.012Z")
}
],
"commands" : [
{
"name" : "update",
"date" : ISODate("2018-07-19T16:27:42.012Z")
},
{
"name" : "update",
"date" : ISODate("2018-07-19T16:27:42.012Z")
}
]
}
]
}
I want to execute SQL-equivalent query
SELECT commands.name, COUNT(commands.name), GROUP BY commands.name
and get a result like:
[
{update: 2},
{get: 4},
{read: 1},
{answer: 1}
]
but when I execute this query in mongo:
.collection(collectionName).aggregate({{'$group': {_id: "$bots.commands.name",count:{$sum:1}}}
}).toArray(callback)
I get such a result:
[
{
_id: [
[ 'test', 'test1' ]
],
count: 1
},
{
_id: [
[ 'read', 'answer', 'get', 'get', 'get', 'get' ]
],
count: 1
}
]
I googled and read about agregation in MongoDB and still don't get much. It's hard to move from SQL to NoN-SQL database
My questions are:
Why my query shows not the result I want to see?
How to fix it?
Thanks in advance!
Since you have two nested arrays in your schema you should use $unwind operator twice before you apply your $group. After $unwind you'll get separate document for each name. Try:
db.col.aggregate([
{
$unwind: "$bots"
},
{
$unwind: "$bots.commands"
},
{
$group: {
_id: "$bots.commands.name",
count: { $sum: 1 }
}
},
{
$replaceRoot: {
newRoot: {
$let: {
vars: { obj: [ { k: "$_id", v: "$count" } ] },
in: { $arrayToObject: "$$obj" }
}
}
}
}
])
In the last stage you can use $replaceRoot with $arrayToObject to set _id as keys in your final objects.
Outputs:
{ "update" : 2 }
{ "get" : 4 }
{ "answer" : 1 }
{ "read" : 1 }

comapare array of id in and get value through lookup mongoDB

my collection Stucture is like below:
{"_id" : ObjectId("5aec2ce5020ba15d2fb2665a"),
"password" : "e10adc3949ba59abbe56e057f20f883e",
"unique_id" : 22,
"first_name" : "Foram",
"last_name" : "Test ",
"country_phone_code" : "+61",
"email" : "a#a.com",
"phone" : "1231231231",
"device_type" : "android",
"admintypeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"vehicle_detail" : [
{
"service_name" : "You",
"_id" : ObjectId("5aec2d08020ba15d2fb2665c"),
"name" : "Qee",
"plate_no" : "Qwe111",
"model" : "Qee",
"color" : "Blue",
"passing_year" : "2005",
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"is_selected" : true,
"pictureData" : "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
},
{
"pictureData" : "vehicle_images/5aec35e00efc106080e14ec7y2tO.jpg",
"is_selected" : false,
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1"),
ObjectId("5ac954b7075e16583dc3311f")
],
"passing_year" : "2018",
"color" : "No",
"model" : "Bcs",
"plate_no" : "12112",
"name" : "Bcs",
"_id" : ObjectId("5aec35e00efc106080e14ec7"),
"service_name" : "You"
}
],
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"is_vehicle_document_uploaded" : true,
"is_trip" : [],
"__v" : 14
}
i want to campare ids in my service_type array which is in vehicle_detail array
to another collection named city_types than from that i have to comapare typid of city_types collection to tyes collection and get value of name from type collection
my city_type collection would be something like below
{
"_id" : ObjectId("5a9f9c53b9e8fa66f9b934c1"),
"countryid" : ObjectId("5abb275fd20731597cc01229"),
"cityid" : ObjectId("5abb27cbd20731597cc0122a"),
"typeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0")
"__v" : 0}
containing type_id
and my types collection is like below
{
"_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"typename" : "You",
"description" : "KARRU You",
"__v" : 0,
"main_type" : 0
}
what i want it name of type
from id
i have used code below:
exports.get_provider_vehicle_list = function (req, res, next) {
var mongoose = require('mongoose');
var Schema = mongoose.Types.ObjectId;
var condition = {$match: {"_id": Schema(req.body.provider_id)}};
var vunwind = {$unwind: "$vehicle_detail"}
var lookup = {
$lookup:
{
from: "types",
localField: "vehicle_detail.admin_type_id",
foreignField: "_id",
as: "type_detail"
}
};
var unwind = {$unwind: {
path: "$type_detail",
preserveNullAndEmptyArrays: true
}
};
var group = {$group: {
_id: null,
"vehicle_detail": {$push: {
is_selected: "$vehicle_detail.is_selected",
admin_type_id: "$vehicle_detail.admin_type_id",
service_type: "$vehicle_detail.service_type",
passing_year: "$vehicle_detail.passing_year",
color: "$vehicle_detail.color",
model: "$vehicle_detail.model",
plate_no: "$vehicle_detail.plate_no",
name: "$vehicle_detail.name",
_id: "$vehicle_detail._id",
type_image_url: '$type_detail.type_image_url',
service_name:"$vehicle_detail.service_name",
pictureData:"$vehicle_detail.pictureData"
}}
}
}
Provider.aggregate([condition, vunwind, lookup, unwind, group], function (err, provider) {
if (err || provider.length == 0) {
res.json({success: true, vehicle_list: []})
} else {
res.json({success: true, vehicle_list: provider[0].vehicle_detail})
}
})
};
and my res is like below:
{
"success": true,
"vehicle_list": [
{
"is_selected": true,
"admin_type_id": "5a9f9b55b9e8fa66f9b934c0",
"service_type": [
"5a9f9c53b9e8fa66f9b934c1",
"5ac954b7075e16583dc3311f"
],
"passing_year": "2005",
"color": "Blue",
"model": "Qee",
"plate_no": "Qwe111",
"name": "Qee",
"_id": "5aec2d08020ba15d2fb2665c",
"type_image_url": "service_type_images/5a9f9b55b9e8fa66f9b934c0mjYA.png",
"service_name": "You",
"pictureData": "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
}
]
}
and res i want is below
{
"success": true,
"vehicle_list": [
{
"is_selected": true,
"admin_type_id": "5a9f9b55b9e8fa66f9b934c0",
"service_type": [
"5a9f9c53b9e8fa66f9b934c1",
"5ac954b7075e16583dc3311f"
],
"passing_year": "2005",
"color": "Blue",
"model": "Qee",
"plate_no": "Qwe111",
"name": "Qee",
"_id": "5aec2d08020ba15d2fb2665c",
"type_image_url": "service_type_images/5a9f9b55b9e8fa66f9b934c0mjYA.png",
"service_type_name": [
"You"
],
"service_name": "You",
"pictureData": "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
}
]
}
please check.
db.getCollection('vehicle').aggregate([
{
$unwind: {
path: '$vehicle_detail',
preserveNullAndEmptyArrays: true,
},
},
{
$unwind: {
path: '$vehicle_detail.service_type',
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: 'city_type',
localField: 'vehicle_detail.service_type',
foreignField: '_id',
as: 'city_type',
},
},
{
$unwind: {
path: '$city_type',
preserveNullAndEmptyArrays: true,
},
},
{
$lookup: {
from: 'types',
localField: 'city_type.typeid',
foreignField: '_id',
as: 'types',
},
},
{
$unwind: {
path: '$types',
preserveNullAndEmptyArrays: false,
},
},
{
$group: {
_id: '$_id',
first_name: { $first: 1 },
last_name: { $first: 1 },
types: { $first: '$types' },
},
},
])
Result
{
"_id" : ObjectId("5aec2ce5020ba15d2fb2665a"),
"first_name" : 1.0,
"last_name" : 1.0,
"types" : {
"_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"typename" : "You",
"description" : "KARRU You",
"__v" : 0,
"main_type" : 0
}
}
Note : You can added fields in group which you need.
I am sending what I understood from your explanation. I hope it helps.
Some points:
I changed your objects city_types and types to an array, since you said Collections;
I link the id to match Vehicle->city->types
function ObjectId(id){
return id;
}
var obj = {"_id" : ObjectId("5aec2ce5020ba15d2fb2665a"),
"password" : "e10adc3949ba59abbe56e057f20f883e",
"unique_id" : 22,
"first_name" : "Foram",
"last_name" : "Test ",
"country_phone_code" : "+61",
"email" : "a#a.com",
"phone" : "1231231231",
"device_type" : "android",
"admintypeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"vehicle_detail" : [
{
"service_name" : "Service 1",
"_id" : ObjectId("5aec2d08020ba15d2fb2665c"),
"name" : "Qee",
"plate_no" : "Qwe111",
"model" : "Qee",
"color" : "Blue",
"passing_year" : "2005",
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"is_selected" : true,
"pictureData" : "vehicle_images/5aec2d08020ba15d2fb2665cWjyP.jpg"
},
{
"pictureData" : "vehicle_images/5aec35e00efc106080e14ec7y2tO.jpg",
"is_selected" : false,
"admin_type_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1"),
ObjectId("5ac954b7075e16583dc3311f")
],
"passing_year" : "2018",
"color" : "No",
"model" : "Bcs",
"plate_no" : "12112",
"name" : "Bcs",
"_id" : ObjectId("5aec35e00efc106080e14ec7"),
"service_name" : "Service 2"
}
],
"service_type" : [
ObjectId("5a9f9c53b9e8fa66f9b934c1")
],
"is_vehicle_document_uploaded" : true,
"is_trip" : [],
"__v" : 14
};
var city_types = [{
"_id" : ObjectId("5aec2d08020ba15d2fb2665c"),
"countryid" : ObjectId("5abb275fd20731597cc01229"),
"cityid" : ObjectId("5abb27cbd20731597cc0122a"),
"typeid" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"__v" : 0
}];
var types = [{
"_id" : ObjectId("5a9f9b55b9e8fa66f9b934c0"),
"typename" : "You",
"description" : "KARRU You",
"__v" : 0,
"main_type" : 0
}];
function print() {
//iterate over vehicle_detail
obj.vehicle_detail.forEach((vehicle)=>{
console.log("vehicle="+ vehicle._id);
//iterate over cities type
city_types.forEach((city)=> {
console.log("city="+ city._id);
if (city._id == vehicle._id) {
//iterate over types
types.forEach((type)=> {
console.log("type="+ type._id);
if (city.typeid == type._id) {
console.log("Type Name: "+type.typename);
}
})
}
})
});
}
print();
The output of the program is:
>node index4.js
vehicle=5aec2d08020ba15d2fb2665c
city=5aec2d08020ba15d2fb2665c
type=5a9f9b55b9e8fa66f9b934c0
Type Name: You
vehicle=5aec35e00efc106080e14ec7
city=5aec2d08020ba15d2fb2665c

MongoDB aggregate on Nested data

I have nested data as below,
{
"_id" : ObjectId("5a30ee450889c5f0ebc21116"),
"academicyear" : "2017-18",
"fid" : "be02",
"fname" : "ABC",
"fdept" : "Comp",
"degree" : "BE",
"class" : "1",
"sem" : "8",
"dept" : "Comp",
"section" : "Theory",
"subname" : "BDA",
"fbValueList" : [
{
"_id" : ObjectId("5a30eecd3e3457056c93f7af"),
"score" : 20,
"rating" : "Fair"
},
{
"_id" : ObjectId("5a30eefd3e3457056c93f7b0"),
"score" : 10,
"rating" : "Fair"
},
{
"_id" : ObjectId("5a337e53341bf419040865c4"),
"score" : 88,
"rating" : "Excellent"
},
{
"_id" : ObjectId("5a337ee2341bf419040865c7"),
"score" : 75,
"rating" : "Very Good"
},
{
"_id" : ObjectId("5a3380b583dde50ddcea350e"),
"score" : 72,
"rating" : "Very Good"
}
]
},
{
"_id" : ObjectId("5a3764f1bc19b77dd9fd9a57"),
"academicyear" : "2017-18",
"fid" : "be02",
"fname" : "ABC",
"fdept" : "Comp",
"degree" : "BE",
"class" : "1",
"sem" : "5",
"dept" : "Comp",
"section" : "Theory",
"subname" : "BDA",
"fbValueList" : [
{
"_id" : ObjectId("5a3764f1bc19b77dd9fd9a59"),
"score" : 88,
"rating" : "Excellent"
},
{
"_id" : ObjectId("5a37667aee64bce1b14747d2"),
"score" : 74,
"rating" : "Good"
},
{
"_id" : ObjectId("5a3766b3ee64bce1b14747dc"),
"score" : 74,
"rating" : "Good"
}
]
}
We are trying to perform aggregation using this,
db.fbresults.aggregate([{$match:{academicyear:"2017-18",fdept:'Comp'}},{$group:{_id: {fname: "$fname", rating:"$fbValueList.rating"},count: {"$sum":1}}}])
and we get result like,
{ "_id" : { "fname" : "ABC", "rating" : [ "Fair","Fair","Excellent","Very Good", "Very Good", "Excellent", "Good", "Good" ] }, "count" : 2 }
but we are expecting result like,
{ "_id" : { "fname" : "ABC", "rating_group" : [
{
rating: "Excellent"
count: 2
},
{
rating: "Very Good"
count: 2
},
{
rating: "Good"
count: 2
},
{
rating: "Fair"
count: 2
},
] }, "count" : 2 }
We want to get individual faculty group by their name and inside that group by their rating response and count of rating.
We have already tried this one but we did not the result.
Mongodb Aggregate Nested Group
This should get you going:
db.collection.aggregate([{
$match: {
academicyear: "2017-18",
fdept:'Comp'
}
}, {
$unwind: "$fbValueList" // flatten the fbValueList array into multiple documents
}, {
$group: {
_id: {
fname: "$fname",
rating:"$fbValueList.rating"
},
count: {
"$sum": 1 // this will give us the count per combination of fname and fbValueList.rating
}
}
}, {
$group: {
_id: "$_id.fname", // we only want one bucket per fname
rating_group: {
$push: { // we push the exact structure you were asking for
rating: "$_id.rating",
count: "$count"
}
},
count: {
$avg: "$count" // this will be the average across all entries in the fname bucket
}
}
}])
This is a long aggregation pipeline, there may be some aggregations that are un-necessary, so please check and discard whichever are irrelevant.
NOTE: This will only work with Mongo 3.4+.
You need to use $unwind and then $group and $push ratings with their counts.
matchAcademicYear = {
$match: {
academicyear:"2017-18", fdept:'Comp'
}
}
groupByNameAndRating = {
$group: {
_id: {
fname: "$fname", rating:"$fbValueList.rating"
},
count: {
"$sum":1
}
}
}
unwindRating = {
$unwind: "$_id.rating"
}
addFullRating = {
$addFields: {
"_id.full_rating": "$count"
}
}
replaceIdRoot = {
$replaceRoot: {
newRoot: "$_id"
}
}
groupByRatingAndFname = {
$group: {
_id: {
"rating": "$rating",
"fname": "$fname"
},
count: {"$sum": 1},
full_rating: {"$first": "$full_rating"}
}
}
addFullRatingAndCount = {
$addFields: {
"_id.count": "$count",
"_id.full_rating": "$full_count"
}
}
groupByFname = {
$group: {
_id: "$fname",
rating_group: { $push: {rating: "$rating", count: "$count"}},
count: { $first: "$full_rating"}
}
}
db.fbresults.aggregate([
matchAcademicYear,
groupByNameAndRating,
unwindRating,
addFullRating,
unwindRating,
replaceIdRoot,
groupByRatingAndFname,
addFullRatingAndCount,
replaceIdRoot,
groupByFname
])

Resources