Facing problems to retrieve data using mongodb Pipeline method using Nodejs - node.js

I am facing some problems to crack this particular challenge where I would like to retrieve data from MongoDb using pipeline in Nodejs.
Here is the data from DB:
{
"_id" : ObjectId("623ec44a6900ca5e3a88ece0"),
"student_id" : ObjectId("5fca683c239e4e2693ee20e4"),
"isSession" : true,
"record_list" : [
{
"_id" : ObjectId("623ec4ae319ebd3d243adeb4"),
"Name" : "Adam",
"Type" : 1,
"class_id" : "aa0a2311-5989-4a7b-855f-4c4b73ae6315",
"isPresent" : true
},
{
"_id" : ObjectId("623ec4ae319ebd3d243adeb5"),
"Name" : "Jack",
"Type" : 1,
"class_id" : "fa54389b-4504-465b-9c6c-a386918b8d67",
"isPresent" : true
}
]
}
Here is the output that I expect:
{
"_id" : ObjectId("623ec44a6900ca5e3a88ece0"),
"student_id" : ObjectId("5fca683c239e4e2693ee20e4"),
"isSession" : true,
"record_list" : [
{
"_id" : ObjectId("623ec4ae319ebd3d243adeb5"),
"Name" : "Jack",
"Type" : 1,
"class_id" : "fa54389b-4504-465b-9c6c-a386918b8d67",
"isPresent" : true
}
]
}
I am sending class_id and student_id from UI and based on that it should filter the data and return the entire collection based on these parameters.
controller.ts
let pipeline: any = await Builder.User.SchoolBuilder.studentMatchRecord(payload);
What I tried:
school.builder.ts
export const studentMatchRecord = (payload: any) => {
let pipeline: any = [];
pipeline.push({ $match: { student_id: Types.ObjectId(payload.studentId) } });
pipeline.push(
{ "$unwind": "$record_list" },
{
"$group": {
"record_list.class_id": payload.classId,
}
});
pipeline.push({
$project: {
student_id: 1,
isSession: 1,
record_list: 1,
},
});
return pipeline;
};
and even this:
export const studentMatchRecord = (payload: any) => {
let pipeline: any = [];
let filterConditions: any = { $match: { $and: [] } };
let matchCriteria: any = { $match: { $and: [] } };
matchCriteria.$match.$and.push({
student_id: Types.ObjectId(payload.student_id),
});
if (payload.classId) {
filterConditions.$match.$and.push({
"record_list.class_id": payload.class_id,
});
}
if (filterConditions.$match.$and.length > 0) {
pipeline.push(filterConditions);
}
pipeline.push({
$project: {
student_id: 1,
isSession: 1,
record_list: 1,
},
});
return pipeline;
};
but none of the case is working here. Can you please highlight what am I doing wrong here? Cheers.

Option 1: find/$elemMatch:
db.collection.find({
"student_id": ObjectId("5fca683c239e4e2693ee20e4")
},
{
"record_list": {
"$elemMatch": {
"class_id": "aa0a2311-5989-4a7b-855f-4c4b73ae6315"
}
},
"isSession": 1,
"student_id": 1
})
playground1
Option 2: aggregate/$filter
db.collection.aggregate([
{
$match: {
"student_id": ObjectId("5fca683c239e4e2693ee20e4")
}
},
{
"$addFields": {
"record_list": {
"$filter": {
"input": "$record_list",
"as": "rec",
"cond": {
$eq: [
"$$rec.class_id",
"fa54389b-4504-465b-9c6c-a386918b8d67"
]
}
}
}
}
}
])
playground2

Related

Get _id to collect another collection detail in expressjs mongodb

collection 'bookborrow'
{
"_id" : ObjectId("62f66aa9b744696c2d08cade"),
"userId" : "62f492ace559e59ee288b73a",
"data" : {
"bookname" : "62e53c8af5d4c45fb7853d9f",
"todayDate" : "12/08/2022",
"dateofreturn" : "27/08/2022"
}
}
collection 'bookdetails'
{
"_id" : ObjectId("62e53c8af5d4c45fb7853d9f"),
"bookname" : "Steve Jobs",
"bookauthor" : "Walter Isaacson ",
"counterbooks" : "5",
"bookPublisher" : "Simon & Schuster "
}
i need bookborrow collection data.bookname in bookdetails collection (bookborrow data.bookname(62e53c8af5d4c45fb7853d9f) === _id (62e53c8af5d4c45fb7853d9f) bookdetails
i used aggregate method
const getHistoryData=await db.get().collection('bookborrow').aggregate([
{
$lookup: {
from: 'bookdetails',
let: {
bookid:
{$toObjectId: "$data.bookname"}
},
pipeline: [
{
$match: {
$expr: {
$eq: [
'$_id',
'$$bookid'
]
}
}
},
{
$project: {
bookname: 1
}
}
],
as: 'getbookdetails'
}
},
{
$unwind: '$bookid'
},
{
$project: {
_id: 1,
bookname: '$getbookdetails.bookname',
returndate: 1
}
}
])
output looklike :=
"bookname" : "Steve Jobs"
"todayDate" : "12/08/2022"
"dateofreturn" : "27/08/2022"

Count Documents in mongoose in single query

const postsCount = await Post.find(query).countDocuments();
const allPosts = await Post.find(query)
.populate('likes')
.skip(startIndex)
.limit(noOfRecordsPerPage)
.sort({ createdAt: 'desc' });
I am querying two times in Database to find the total document count . How can i do this with a single query to the database ?
//you can use aggregation pipeline and $facet to combine two DB requests into one
//Actual Code output from windows Mongoshell/Command Line Interface(CLI)
> print("mongoDB",db.version())
mongoDB 4.2.6
> db.groups1.aggregate([
... {$facet:{
... "docCount":[
... {$group:{
... _id:null,
... count:{$sum:1}
... }
... }
... ],
... "allDocs":[
... {$match:{}}
... ]
... }
... }
... ]).pretty();
{
"docCount" : [
{
"_id" : null,
"count" : 4
}
],
"allDocs" : [
{
"_id" : ObjectId("5f817d142689512ef0bd3bd7"),
"uid" : 1,
"group_id" : "g1"
},
{
"_id" : ObjectId("5f817d142689512ef0bd3bd8"),
"uid" : 1,
"group_id" : "g2"
},
{
"_id" : ObjectId("5f817d142689512ef0bd3bd9"),
"uid" : 2,
"group_id" : "g2"
},
{
"_id" : ObjectId("5f817d142689512ef0bd3bd1"),
"uid" : 2,
"group_id" : "g3"
}
]
}
>
Use the $facet aggregation stage to combine multiple nested pipelines into your master pipeline.
Here's a working code of the above idea -
const countPipeline = [
{
$match: query
},
{
$group: {
_id: null,
count: {
$sum: 1
}
}
},
{
$project: {
_id: 0,
count: '$count'
}
}
];
const postPipeline = [
{
$match: query
},
{
$skip: startIndex
},
{
$limit: noOfRecordsPerPage
},
{
$sort: {
createdAt: -1
}
}
];
const pipeline = [
{
$facet: {
posts: postPipeline,
count: countPipeline
}
}
];
const results = await Posts.aggreagate(pipeline).exec();

wrong counting with $filter

My db cofiguration looks like:
{
"_id" : ObjectId("5ece47aa6510a611b47aac5a"),
"boats" : [
{
"_id" : ObjectId("5ece47aa6510a611b47aac6e"),
"model" : "Dufour",
"year" : 2019,
"about" : [
{
"_id" : ObjectId("5ece47aa6510a611b47aac71"),
"Capacity" : 14,
"characteristics" : [
{
"_id" : ObjectId("5ece47aa6510a611b47aac73"),
"fuel" : "petrol",
"fuelCap" : 200
},
{
"_id" : ObjectId("5ece47aa6510a611b47aac73"),
"fuel" : "petrol",
"fuelCap" : 120
},
]
},
{
"_id" : ObjectId("5ece47aa6510a611b47aac71"),
"Capacity" : 8,
"characteristics" : [
{
"_id" : ObjectId("5ece47aa6510a611b47aac73"),
"fuel" : "benzin",
"fuelCap" : 180
},
{
"_id" : ObjectId("5ece47aa6510a611b47aac73"),
"fuel" : "petrol",
"fuelCap" : 100
},
]
},
{...},
{...},
]
}
Now i am trying to count the number of boats which have "fuel" : "petrol", so i use the code bellow:
router.get('/boat', async(req, res)=>{
try{
const fuelData = await Boat.aggregate([
{
$project: {
fuelData: {
$filter: {
input: "$boats",
as: "boats",
cond: {
$filter:{
input:"$$boats.about",
as:"about",
cond:{
$filter:{
input:"$$about.characteristics",
as:"characteristics",
cond:{
$eq:["$$activity1.activity.type", "STILL"]
}
}
}
}
}
}
}
}
},
{
$project: {
boatsCount: {$size : "$fuelData" }
}
}
])
res.status(201).send(fuelData)
}catch(e){
res.send(e)
}
})
The problem is that return wrong number of boatCount. And it seems like it returns the number of the boats which are inside the db. Any help how to count correctly the boats which have "fuel" : "petrol"?
Is there anything wrong in my code?
https://mongoplayground.net/p/D0FEhTMJEJ1
Hope this is what you need.
The sample data you have provided is missing a ] & }.
So I added 1 additional boat with 2 about.
db.collection.aggregate([
{
$match: {
"boats.about.characteristics.fuel": "petrol"
}
},
{
$unwind: "$boats"
},
{
$unwind: "$boats.about"
},
{
$match: {
"boats.about.characteristics.fuel": "petrol"
}
},
{
$group: {
_id: null,
count: {
$sum: 1
}
}
}
])

How to use $match in aggregate in mongodb for optional parameter

I m new in mongodb and stucked while using aggregate function.
my query is -> I want to filter database record based on my applied filter in dashboard.
How can i add filter value using $match operator? it should apply when filter value is present and ignore if not avaliable.
if (req.body.filterSet !== undefined) {
const filterData = req.body.filterSet[0];
var violation_id = filterData.violation_id;
var start_notice = filterData.start_notice;
var end_notice = filterData.end_notice;
var rc_number = filterData.rc_number;
var circle = filterData.circle;
var start_date = filterData.start_date;
var end_date = filterData.end_date;
var status = filterData.status;
var source = filterData.source;
var sms_status = filterData.sms_status;
var notice_status = filterData.notice_status;
}
Complaint.aggregate([
{ $match : { is_active : { $eq : 1 } } },
{ $match : { id : { $eq : 1103186 } } },
{ $lookup:{ from: 'offences', localField:'offences', foreignField: 'offence_id', as: 'offenceSetail' } },
{ $project: { 'offences.is_active' : 0 } },
{ $replaceRoot : { newRoot : { $mergeObjects : [ { $arrayElemAt: [ "$offenceSetail", 0] }, "$$ROOT"] } } },
{ $project: { 'offenceSetail' : 0 } },
{ $lookup: { from: 'registers', localField: 'user_id', foreignField: 'id', as: 'sender'} },
{ $project: { 'registers.is_active' : 0 } },
{ $replaceRoot : { newRoot : { $mergeObjects : [ { $arrayElemAt: ['$sender', 0] }, "$$ROOT"] } } },
{ $project: { 'sender': 0 } },
{ $facet : { length : [ { $count : "total" }], data : [ { $skip: skip }, {$limit: limit } ] } },
])
you can construct your aggregation stages dynamically like this:
const stages = [
{ $lookup:{ from: 'offences', localField:'offences', foreignField: 'offence_id', as: 'offenceSetail' } },
{ $project: { 'offences.is_active' : 0 } },
{ $replaceRoot : { newRoot : { $mergeObjects : [ { $arrayElemAt: [ "$offenceSetail", 0] }, "$$ROOT"] } } },
{ $project: { 'offenceSetail' : 0 } },
{ $lookup: { from: 'registers', localField: 'user_id', foreignField: 'id', as: 'sender'} },
{ $project: { 'registers.is_active' : 0 } },
{ $replaceRoot : { newRoot : { $mergeObjects : [ { $arrayElemAt: ['$sender', 0] }, "$$ROOT"] } } },
{ $project: { 'sender': 0 } },
{ $facet : { length : [ { $count : "total" }], data : [ { $skip: skip }, {$limit: limit } ] } }
]
if (optional_parameter_exists) {
stages.unshift(
{ $match: { is_active: { $eq: 1 } } },
{ $match: { id: { $eq: 1103186 } } }
);
}
Complaint.aggregate(stages);

How to get group with groupby get the result with dynamic status in mongoDB

I have the collection like below
{
"_id" : ObjectId("5b6538704ba0292b6c197770"),
"Name":"Name1",
"Status":"Good",
},
{
"_id" : ObjectId("5b6538704ba0292b6c197773"),
"Name":"Name2",
"Status":"Bad"
},
{
"_id" : ObjectId("5b6538704ba0292b6c197774"),
"Name":"Name3",
"Status":"Bad"
},{
"_id" : ObjectId("5b6538704ba0292b6c197774"),
"Name":"Name1",
"Status":"Bad"
},
{
"_id" : ObjectId("5b6538704ba0292b6c197775"),
"Name":"Name1",
"Status":"Good"
}
I have used the query to get the status wise count like below
db.Students.aggregate( [
{ $group: { _id: {
"Name": "$Name","Status":"$Status"}, StatusCount: { $sum: 1 } } }
, { "$project": { _id: 0, Name: "$_id.Name",Status : "$_id.Status", StatusCount:1 } }
] );
The result was
{
"Name":"Name1",
"StatusCount" : 2,
"Status" : "Good"
},
{
"Name":"Name2",
"StatusCount" : 1,
"Status" : "Bad"
}, {
"Name":"Name2",
"StatusCount" : 1,
"Status" : "Bad"
},
{
"Name":"Name1",
"StatusCount" : 1,
"Status" : "Bad"
}
The result what I am approaching is like
{
"Name":"Name1",
"Good" : 2,
"Bad" :1
},
{
"Name":"Name2",
"Good" : 0,
"Bad" :1
}
The result I am expecting to have the status of field names and count as its values. I have tried to do this but I could not make it happen. The status, for now, is only two like Good or Bad but may increase in real dataset.
By using the $arrayToObject operator and a final $replaceRoot pipeline step which has a $mergeObjects operator you will get your desired result.
You would need to run the following aggregate pipeline on MongoDB Server 3.4.4 or newer:
const pipeline = [
{ '$group': {
'_id': {
'Name': '$Name',
'Status': '$Status'
},
'StatusCount': { '$sum': 1 }
} },
{ '$group': {
'_id': '$_id.Name',
'counts': {
'$push': {
'k': '$_id.Status',
'v': '$StatusCount'
}
}
} },
{ '$replaceRoot': {
'newRoot': { '$mergeObjects': [
{ '$arrayToObject': '$counts' },
{ 'Name': '$_id' }
] }
} }
];
db.Students.aggregate(pipeline);

Resources