mongodb pipeline limit to 1 - mongodb

I am trying to limit the returning array to just 1, currently when I run this it shows me all the results under the added season How do I limit the season to return 1.
{
'from': 'episode',
'let': {
'episodeId': '$_id'
},
'pipeline': [{
"$match": {
"$expr": {
"$eq": ["$show_id", "$$episodeId"]
}
}
}, {
"$sort": {
"pubDate": -1
}
}],
'as': 'season'
}
Basically all I need from the sub-array is the first season number in the picture below you can see it is season 3.
when I add the limit to the pipeline I get "Stage must be a properly formatted document."

Try this,
db.show.aggregate([
{
"$lookup": {
"from": "episode",
"let": {
"episodeId": "$_id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$show_id",
"$$episodeId"
]
}
}
},
{
"$sort": {
"pubDate": -1
}
},
{
$limit: 1
}
],
"as": "season"
}
}
])
You would get the out put as
[
{
"_id": 1,
"season": [
{
"_id": 2,
"pubDate": ISODate("2021-08-04T00:00:00Z"),
"show_id": 1
}
],
"show": 1
}
]

Related

How to group same record into multiple groups using mongodb aggregate pipeline

I have a two collections.
OrgStructure (visualise this as a tree structure)
Example Document:
{
"id": "org1",
"nodes": [
{
"nodeId": "root",
"childNodes": ["child1"]
},
{
"nodeId": "child1",
"childNodes": ["child2"]
},
{
"nodeId": "child2",
"childNodes": []
}
]
}
Activity
Example Document:
[
{
"id":"A1",
"orgUnit": "root"
},
{
"id":"A2",
"orgUnit": "child1"
},
{
"id":"A3",
"orgUnit": "child2"
}
]
Now my expectation is to group activities by orgUnit such a way that by considering the child nodes as well.
Here i don't want to do a lookup and i need to consider one OrgStructure document as an input, so that i can construct some condition using the document such a way that the query will return the below result.
Expected result
[
{
"_id": "root",
"activities": ["A1","A2","A3"]
},
{
"_id": "child1",
"activities": ["A2","A3"]
},
{
"_id": "child2",
"activities": ["A3"]
}
]
So im ecpecting an aggregate query something like this
{
"$group": {
"_id": {
"$switch": {
"branches": [
{
"case": {"$in": ["$orgUnit",["root","child1","child2"]]},
"then": "root"
},
{
"case": {"$in": ["$orgUnit",["child1","child2"]]},
"then": "child1"
},
{
"case": {"$in": ["$orgUnit",["child2"]]},
"then": "child2"
}
],
"default": null
}
}
}
}
Thanks in advance!
You will need 2 steps:
create another collection nodes for recursive lookup. The original OrgStructure is hard to perform $graphLookup
db.OrgStructure.aggregate([
{
"$unwind": "$nodes"
},
{
"$replaceRoot": {
"newRoot": "$nodes"
}
},
{
$out: "nodes"
}
])
Perform $graphLookup on nodes collection to get all child nodes. Perform $lookup to Activity and do some wrangling.
db.nodes.aggregate([
{
"$graphLookup": {
"from": "nodes",
"startWith": "$nodeId",
"connectFromField": "childNodes",
"connectToField": "nodeId",
"as": "nodesLookup"
}
},
{
"$lookup": {
"from": "Activity",
"let": {
nodeId: "$nodesLookup.nodeId"
},
"pipeline": [
{
$match: {
$expr: {
$in: [
"$orgUnit",
"$$nodeId"
]
}
}
},
{
$group: {
_id: "$id"
}
}
],
"as": "activity"
}
},
{
$project: {
_id: "$nodeId",
activities: "$activity._id"
}
}
])
Here is the Mongo playground for your reference.

Mongodb lookup for not equal fields

I want to join two collections and find the documents where has one equal field and one unequal field!
This is what I was tried, But not work
db.collectionOne.aggregate[
{
"$match": {
"$and": [
{ "$text": { "$search": "this is my query" } },
{ "b": { "$eq": "60e849054d2f0d409041b6a2" } }
]
}
},
{ "$addFields": { "pID": { "$toString": "$_id" }, "score": { "$meta": "textScore" } } },
{
"$lookup": {
"from": "collectionsTwo",
"as": "collectionsTwoName",
"pipeline": [{
"$match": {
"$expr": {
"$and": [{
"$ne": ["$fieldOne", "60dd0f98d10f072e2a225502"] // This one is unqual field
}, { "$eq": ["$pID", "$fieldTwo"] }] // This one is equal field
}
}
}]
}
},
{ "$sort": { "score": -1 } },
{ "$limit": 1 }
])
Fields in the source document, i.e. $pID are not available inside the lookup pipeline.
In order to reference those values, you would need to define a variable using let, such as:
{
"$lookup": {
"from": "collectionsTwo",
"as": "collectionsTwoName",
"let": { "srcpID":"$pID" },
"pipeline": [{
"$match": {
"$expr": {
"$and": [{
"$ne": ["$fieldOne", "60dd0f98d10f072e2a225502"] // This one is unqual field
}, { "$eq": ["$$srcpID", "$fieldTwo"] }] // This one is equal field
}
}
}]
}
},
See https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#join-conditions-and-uncorrelated-sub-queries

How do I use a different condition if there are empty results on the condition of a $match of a $lookup?

Using the mongoDb Aggregation framework; suppose I wanted to $lookup a set of results in another collection with a condition that if returned no results - would then return the results of another condition. This is what I have.
srp collection
[
{
"_id": ObjectId("5fb6727790f41fef3ee7db87"),
"dates_e": {
"from": ISODate("2021-10-01"),
"to": ISODate("2021-10-03")
}
},
{
"_id": ObjectId("5f034bfa4c0000abdfc7df2e"),
"dates_e": {
"from": ISODate("2021-03-10"),
"to": ISODate("2021-03-15")
}
},
..
]
uth collection
[
{
"_id": ObjectId("5fb6727790f41fef3ee7db88"),
"dateTime": ISODate("2021-10-01"),
"res": 1.7
},
{
"_id": ObjectId("5fb6727790f41fef3ee7db89"),
"dateTime": ISODate("2021-10-02"),
"res": 0.5
},
..
]
The aggregation query (on the srp collection):
[
{
$match: { "_id": ObjectId("5fb6727790f41fef3ee7db87") }
},
{
$lookup: {
"from": "uth",
"let": {
"fromDate": "$dates_e.from",
"toDate": "$dates_e.to"
},
"pipeline": [
$match: {
$expr: {
$and: [
{
$gte: ["$varData_e.dateTime", "$$fromDate"]
},
{
$lt: ["$varData_e.dateTime", "$$toDate"]
}
]
}
}
],
"as": "uth_e"
}
}
]
Which would return:
[
{
"_id": ObjectId("5fb6727790f41fef3ee7db87"),
"dates_e": {
"from": ISODate("2021-10-01"),
"to": ISODate("2021-10-03")
},
"uth_e": [
{
"_id": ObjectId("5fb6727790f41fef3ee7db88"),
"dateTime": ISODate("2021-10-01"),
"res": 1.7
},
{
"_id": ObjectId("5fb6727790f41fef3ee7db89"),
"dateTime": ISODate("2021-10-02"),
"res": 0.5
},
{
"_id": ObjectId("5fb6727790f41fef3ee7db90"),
"dateTime": ISODate("2021-10-03"),
"res": 2.8
}
]
]
So this works just fine. However if the $match was "_id": ObjectId("5f034bfa4c0000abdfc7df2e") and there weren't any results returned (on the $lookup from uth) then I would like to return a set of results for a broader condition:
[
{
$match: { "_id": ObjectId("5f034bfa4c0000abdfc7df2e") }
},
{
$lookup: {
"from": "uth",
"let": {
"fromDate": "$dates_e.from",
"toDate": "$dates_e.to"
},
"pipeline": [
$match: {
$expr: {
$gte: ["$varData_e.dateTime", "$$fromDate"]
}
}
],
"as": "uth_e"
}
}
]
Any help appreciated!

$lookup in nested array

I need a MongoDB query to return the aggregation result from a collection of events, users and confirmations.
db.events.aggregate([
{
"$match": {
"_id": "1"
}
},
{
"$lookup": {
"from": "confirmations",
"as": "confirmations",
"let": {
"eventId": "$_id"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$eventId",
"$$eventId"
]
}
}
},
{
"$lookup": {
"from": "users",
"as": "user",
"let": {
"userId": "$confirmations.userId"
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$userId"
]
}
}
},
]
},
},
]
}
}
])
Desired
[
{
"_id": "1",
"confirmations": [
{
"_id": "1",
"eventId": "1",
"user": {
"_id": "1",
"name": "X"
},
"userId": "1"
},
{
"_id": "2",
"eventId": "1",
"user": {
"_id": "2",
"name": "Y"
},
"userId": "2"
}
],
"title": "foo"
}
]
Everything works except the embedded user in confirmations array. I need the output to show the confirmations.user, not an empty array.
Playgound: https://mongoplayground.net/p/jp49FW59WCv
You made mistake in variable declaration of inner $lookup. Try this Solution:
db.events.aggregate([
{
"$match": {
"_id": "1"
}
},
{
$lookup: {
from: "confirmations",
let: { "eventId": "$_id" },
pipeline: [
{
$match: {
"$expr": {
$eq: ["$eventId", "$$eventId"]
}
}
},
{
$lookup: {
from: "users",
let: { "userId": "$userId" },
pipeline: [
{
$match: {
$expr: {
$eq: ["$_id", "$$userId"]
}
}
}
],
as: "user"
}
},
{ $unwind: "$user" }
],
as: "confirmations"
}
}
])
Also instead of $unwind of user inside inner $lookup you can use:
{
$addFields: {
user: { $arrayElemAt: ["$user", 0] }
}
}
since $unwind will not preserve empty results from previous stage by default.

How to Populate data using Mongodb Aggregate framework?

I have a current MongoDB Aggregate framework pipeline from a previous question and I am unable to add populate query to grab user profile by id.
My code is below
Product.aggregate([
{
$group: {
_id: {
hub: "$hub",
status: "$productStatus",
},
count: { $sum: 1 },
},
},
{
$group: {
_id: "$_id.hub",
counts: {
$push: {
k: "$_id.status",
v: "$count",
},
},
},
},
{
$group: {
_id: null,
counts: {
$push: {
k: { $toString: "$_id" },
v: "$counts",
},
},
},
},
{
$addFields: {
counts: {
$map: {
input: "$counts",
in: {
$mergeObjects: [
"$$this",
{ v: { $arrayToObject: "$$this.v" } },
],
},
},
},
},
},
{
$replaceRoot: {
newRoot: { $arrayToObject: "$counts" },
},
},
]);
and got the following result
[
{
"5fe75679e6f7a62ddaf5b2e9": {
"in progress": 5,
"Cancelled": 4,
"return": 1,
"on the way": 3,
"pending": 13,
"Delivered": 4
}
}
]
Expected Output
I need to grab user information from user collections using the hubId "5fe75679e6f7a62ddaf5b2e9" and expect a final result of the form below
[
{
"hub": {
"photo": "avatar.jpg",
"_id": "5fe75679e6f7a62ddaf5b2e9",
"name": "Dhaka Branch",
"phone": "34534543"
},
"statusCounts": {
"in progress": 5,
"Cancelled": 4,
"return": 1,
"on the way": 3,
"pending": 13,
"Delivered": 4
}
}
]
First id is user id and available in user collections.
You need to tweak your aggregate pipeline a little bit and include new pipeline stage like $lookup that populates the
hub
Product.aggregate([
{ "$group": {
"_id": {
"hubId": "$hubId",
"status": "$productStatus"
},
"count": { "$sum": 1 }
} },
{ "$group": {
"_id": "$_id.hubId",
"statusCounts": {
"$push": {
"k": "$_id.status",
"v": "$count"
}
}
} },
{ "$lookup": {
"fron": "users",
"localField": "_id",
"foreignField": "_id",
"as": "user"
} },
{ "$project": {
"user": { "$arrayElemAt": ["$user", 0] },
// "hub": { "$first": "$hub" },
"statusCounts": { "$arrayToObject": "$statusCounts" }
} }
])
To project only some fields in the user profile, you can update your $lookup pipeline to have the form
{ "$lookup": {
"from": "users",
"let": { "userId": "$_id" },
"pipeline": [
{ "$match": {
"$expr": { "$eq": ["$_id", "$$userId"] }
} },
{ "$project": {
"name": 1,
"phone": 1,
"photo": 1
} }
],
"as": "user"
} }