how to use $groupby and transform distinct value mongodb - mongodb

How to transform the data using $if $else groupby condition MongoDB?
This playground should return two object who belongs to text with "tester 2" and "tester 3" also if I have multiple object in history collection it should also check with last object not will all object how it is possible
So condition should say if history's date is $gt then main collection should return nothing else return the matched criteria data.
db.main.aggregate([
{
$lookup: {
from: "history",
localField: "history_id",
foreignField: "history_id",
as: "History"
}
},
{
$unwind: "$History"
},
{
"$match": {
$expr: {
$cond: {
if: {
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
},
then: {
$and: [
{
$gt: [
"$date",
"$History.date"
]
},
{
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
}
]
},
else: {}
}
}
}
}
])
MongoPlayground

If I understand you correctly, it is what you are trying to do:
db.main.aggregate([
{
$lookup: {
from: "history",
let: {
main_history_id: "$history_id",
main_user_id: { $toString: "$sender_id" }
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
"$history_id",
"$$main_history_id"
]
},
{
$eq: [
"$user_id",
"$$main_user_id"
]
}
]
}
}
}
],
as: "History"
}
},
{
$unwind: {
path: "$History",
preserveNullAndEmptyArrays: true
}
},
{
$sort: {
_id: 1,
"History.history_id": 1,
"History.date": 1
}
},
{
$group: {
_id: "$_id",
data: { $last: "$$ROOT" },
History: { $last: "$History" }
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$data",
{ History: "$History" }
]
}
}
},
{
"$match": {
$expr: {
$or: [
{
$eq: [
{ $type: "$History.date" },
"missing"
]
},
{
$ne: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
},
{
$and: [
{
$eq: [
"5e4e74eb380054797d9db623",
"$History.user_id"
]
},
{
$gte: [
"$date",
"$History.date"
]
}
]
}
]
}
}
}
])
MongoPlayground

Related

find available rooms querying to bookings with aggregation

I have two collections, and i want to find available rooms between two dates 2021-10-01T00:00:00.000Z and 2021-10-31T23:59:59.999Z
bookings
{from:Date, to:Date, room:ObjectId, status:Boolean}
rooms
{_id:ObjectId, code:String, status:Boolean}
Any idea?
aggregate
db.bookings.aggregate([
{
"$match": {
"$and": [
{
"from": {
"$lt": "2021-10-16T23:59:59.999Z"
}
},
{
"to": {
"$gt": "2021-10-13T23:59:59.999Z"
}
}
],
"status": true
}
},
{
"$group": {
"_id": "1",
"notAvailableRooms": {
$addToSet: "$room"
}
}
},
{
"$lookup": {
from: "rooms",
let: {
ids: "$notAvailableRooms"
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$not: {
$in: [
"$_id",
"$$ids"
]
}
},
{
$eq: [
"$status",
true
]
}
]
}
}
}
],
as: "availableRooms"
}
},
{
"$project": {
"availableRooms": 1
}
}
])
mongoplayground

MongoDb Create Aggregate Create query

I have 3 table users,shifts,temporaryShifts,
shifts:[{_id:ObjectId(2222),name:"Morning"},{_id:ObjectId(454),name:"Night"}]
users:[{_id:ObjectId(123),name:"Albert",shift_id:ObjectId(2222)}]
temporaryShifts:[
{_id:2,userId:ObjectId(123),shiftId:ObjectId(454),type:"temporary",date:"2020-02-01"},
{_id:987,userId:ObjectId(123),shiftId:ObjectId(454),type:"temporary",date:"2020-02-03"},
{_id:945,userId:ObjectId(123),shiftId:ObjectId(454),type:"temporary",date:"2020-02-08"},
{_id:23,userId:ObjectId(123),shiftId:ObjectId(454),date:"2020-02-09"}]
i want to make a mongoose aggregate query then give me result :
get result between two dates for example :2020-02-01 2020-02-05,
resullts is :
[
{_id:ObjectId(123),name:"Albert",shift:[
{_id:2,shiftId:ObjectId(454),type:"temporary",date:"2020-02-01"},
{_id:2,shiftId:ObjectId(2222),type:"permanent",date:"2020-02-02"},
{_id:2,shiftId:ObjectId(454),type:"temporary",date:"2020-02-03"},
{_id:2,shiftId:ObjectId(2222),type:"permanent",date:"2020-02-04"},
{_id:2,shiftId:ObjectId(2222),type:"permanent",date:"2020-02-05"},
]}
]
in result type temporary mean selected date in table temporaryShift document available else type permanent
MongoPlayGround You Can edit
You can first project a date range array using $range, in your example it will be like [2020-02-01, 2020-02-02, 2020-02-03, 2020-02-04, 2020-02-05], then you can use the array to perform $lookup
db.users.aggregate([
{
$limit: 1
},
{
"$addFields": {
"startDate": ISODate("2020-02-01"),
"endDate": ISODate("2020-02-05")
}
},
{
"$addFields": {
"dateRange": {
"$range": [
0,
{
$add: [
{
$divide: [
{
$subtract: [
"$endDate",
"$startDate"
]
},
86400000
]
},
1
]
}
]
}
}
},
{
"$addFields": {
"dateRange": {
$map: {
input: "$dateRange",
as: "increment",
in: {
"$add": [
"$startDate",
{
"$multiply": [
"$$increment",
86400000
]
}
]
}
}
}
}
},
{
"$unwind": "$dateRange"
},
{
"$project": {
"name": 1,
"shiftId": 1,
"dateCursor": "$dateRange"
}
},
{
"$lookup": {
"from": "temporaryShifts",
"let": {
dateCursor: "$dateCursor",
shiftId: "$shiftId"
},
"pipeline": [
{
"$addFields": {
"parsedDate": {
"$dateFromString": {
"dateString": "$date",
"format": "%Y-%m-%d"
}
}
}
},
{
$match: {
$expr: {
$and: [
{
$eq: [
"$$dateCursor",
"$parsedDate"
]
}
]
}
}
}
],
"as": "temporaryShiftsLookup"
}
},
{
"$unwind": {
path: "$temporaryShiftsLookup",
preserveNullAndEmptyArrays: true
}
},
{
$project: {
shiftId: 1,
type: {
"$ifNull": [
"$temporaryShiftsLookup.type",
"permanent"
]
},
date: "$dateCursor"
}
}
])
Here is the Mongo Playground for your reference.

Mongo db - How to get two counts from same collection with a single lookup?

I have a working query below:
{
$lookup: {
from: "watchList",
as: "watchList",
let: {
postId: "$_id",
userId: userCurrent
},
pipeline: [
{
$match: {
$and: [
{ $expr: {$eq: ["$postId","$$postId"]} },
{ $expr: {$eq: ["$$userId", "$user"]} },
]
}
},
{
$count: "watchCount"
},
]
}
},
The above query gives me a count of two conditions combined in AND:
{ $expr: {$eq: ["$postId","$$postId"]} },
{ $expr: {$eq: ["$$userId", "$user"]} },
In addition, is it possible to get the count of ONLY { $expr: {$eq: ["$postId","$$postId"]} } without doing another lookup ?
So there will be two counts -
Count 1:
AND condition for
{ $expr: {$eq: ["$postId","$$postId"]} },
{ $expr: {$eq: ["$$userId", "$user"]} },
Count 2:
Single condition
{ $expr: {$eq: ["$postId","$$postId"]} }
You can do something like this,
$group first match count to firstCount and second match count to secondCount
{
$lookup: {
from: "watchList",
as: "watchList",
let: {
postId: "$_id",
userId: userCurrent
},
pipeline: [
{
$group: {
_id: null,
firstCount: {
$sum: {
$cond: [
{
$and: [
{ $eq: ["$postId", "$$postId"] },
{ $eq: ["$$userId", "$user"] }
]
},
1,
0
]
}
},
secondCount: {
$sum: {
$cond: [{ $eq: ["$postId", "$$postId"] }, 1, 0]
}
}
}
}
]
}
}

Query MongoDb aggregate join two collections

I need help for querying mongoDb
So I have two collections like
Collection A:
{someField: "123", anotherField: "456"},
{someField: "1234", anotherField: "4567"}
Collection B
{someField: "123", otherField: "789"}
with Query:
db.A.aggregate([
{
$lookup:
{
from: "B",
let: { someField: "$someField", otherField: "$otherField" },
pipeline: [
{ $match:
{ $expr:
{ $and:
[
{ $eq: [ "$someField", "$$someField" ] },
{ $eq: [ "$otherField", "789" ] }
]
}
}
},
],
as: "B"
}
}
])
I get all collection A, with B empty in {someField: "1234", anotherField: "4567"}
What I want to achieve is like:
{someField: "123", anotherField: "456", b: {someField: "123", otherField: "789"}}
Thank you in advance
you only need to declare $someField in the let section.
db.collectionA.aggregate([
{
$lookup: {
from: 'collectionB',
let: { some_field: '$someField' },
pipeline: [
{ $match: {
$expr: {
$and: [
{ $eq: [ "$someField", "$$some_field" ] },
{ $eq: [ "$otherField", "789" ] }
]
}
}
}
],
as: 'B'
}
},
{
$match: {
$expr: {
$gt: [ { $size: "$B" }, 0 ]
}
}
}
])
https://mongoplayground.net/p/RTiUMWl8QaX
This is how I removed the empty B array documents:
db.A.aggregate( [
{
$lookup: {
from: "B",
localField: "someField",
foreignField: "someField",
as: "B"
}
},
{
$addFields: {
B: {
$filter: {
input: "$B",
cond: {
$eq: [ "$$this.otherField", "789" ]
}
}
}
}
},
{
$match: {
$expr: {
$gt: [ { $size: "$B" }, 0 ]
}
}
}
] ).pretty()

mongo aggregate with $cond not working with $eq on nested lookups

I am having issues with referencing a nested array item in a $cond statement.
db.getCollection('bookings').aggregate([
{
$lookup: {
from: "listings",
localField: "listingId",
foreignField: "_id",
as: "listing"
}
},
{
$match: {
$and: [
{
locationId: ObjectId("5c0f0c882fcf07fb08890c27")
},
{
$or: [
{
$and: [
{
state: "booked"
},
{
startDate: {
$lte: new Date()
}
},
{
startDate: {
$gte: ISODate("2019-12-18T07:00:00.000Z")
}
}
]
},
{
$and: [
{
listing: {
$elemMatch: {
inspectionStatus: "none"
}
}
},
{
endDate: {
$lte: new Date()
}
},
{
endDate: {
$gte: ISODate("2019-12-18T07:00:00.000Z")
}
},
{
state: {
$in: [
"active",
"returned"
]
}
}
]
},
{
$and: [
{
state: {
$ne: "cancelled"
}
},
{
$or: [
{
$and: [
{
startDate: {
$gte: ISODate("2019-12-20T07:00:00.993Z")
}
},
{
startDate: {
$lte: ISODate("2019-12-21T06:59:59.999Z")
}
}
]
},
{
$and: [
{
endDate: {
$gte: ISODate("2019-12-20T07:00:00.993Z")
}
},
{
endDate: {
$lte: ISODate("2019-12-21T06:59:59.999Z")
}
}
]
}
]
}
]
}
]
}
]
}
},
{
$addFields: {
isLate: {
$cond: [
{
$or: [
{
$and: [
{
$eq: [
"$listing.0.inspectionStatus",
"none"
]
},
{
$lte: [
"$endDate",
new Date()
]
},
{
$gte: [
"$endDate",
ISODate("2019-12-18T07:00:00.000Z")
]
},
{
$in: [
"$state",
[
"active",
"returned"
]
]
},
]
},
{
$and: [
{
$eq: [
"$state",
"booked"
]
},
{
$lte: [
"$startDate",
new Date()
]
},
{
$gte: [
"$startDate",
ISODate("2019-12-18T07:00:00.000Z")
]
}
]
}
]
},
true,
false
]
}
}
}
])
In the above, the following lines in the $cond statement does not work at all:
$eq: [
"$listing.0.inspectionStatus",
"none"
]
My question is - how do I make the above work? Note that there is always only one array item in the listing field after the lookup (never more than one array item in there). I've tried different variations like $listing.$0.$inspectionStatus - but nothing seems to work. I could go down the trajectory of researching group and filter - but I feel like this is overkill when I simply always want to access the first and only item in the listing array.
Please use $in keyword instead of $eq keyword in $cond keyword
db.demo1.aggregate([
{
$lookup: {
from: "demo2",
localField: "listingId",
foreignField: "_id",
as: "listing"
}
},
{
$match: {
$and: [
{
locationId: ObjectId("5c0f0c882fcf07fb08890c27")
},
{
$or: [
{
$and: [
{
state: "booked"
},
{
startDate: {
$lte: new Date()
}
},
{
startDate: {
$gte: ISODate("2019-12-18T07:00:00.000Z")
}
}
]
},
{
$and: [
{
listing: {
$elemMatch: {
inspectionStatus: "none"
}
}
},
{
endDate: {
$lte: new Date()
}
},
{
endDate: {
$gte: ISODate("2019-12-18T07:00:00.000Z")
}
},
{
state: {
$in: [
"active",
"returned"
]
}
}
]
},
{
$and: [
{
state: {
$ne: "cancelled"
}
},
{
$or: [
{
$and: [
{
startDate: {
$gte: ISODate("2019-12-20T07:00:00.993Z")
}
},
{
startDate: {
$lte: ISODate("2019-12-21T06:59:59.999Z")
}
}
]
},
{
$and: [
{
endDate: {
$gte: ISODate("2019-12-20T07:00:00.993Z")
}
},
{
endDate: {
$lte: ISODate("2019-12-21T06:59:59.999Z")
}
}
]
}
]
}
]
}
]
}
]
}
},
{
$addFields: {
isLate: {
$cond: [
{
$or: [
{
$and: [
{
$in: [
"none",
"$listing.inspectionStatus",
]
},
{
$lte: [
"$endDate",
new Date()
]
},
{
$gte: [
"$endDate",
ISODate("2019-12-18T07:00:00.000Z")
]
},
{
$in: [
"$state",
[
"active",
"returned"
]
]
},
]
},
{
$and: [
{
$eq: [
"$state",
"booked"
]
},
{
$lte: [
"$startDate",
new Date()
]
},
{
$gte: [
"$startDate",
ISODate("2019-12-18T07:00:00.000Z")
]
}
]
}
]
},
true,
false
]
}
}
}
])