MongoDB aggregation make inner array in root - arrays

Hi I have two collection person and department. Those collection have the properties as below.
I want to aggregate and add a inner array in to the root of the document. Please check the result I need below:
person:
{
_id:ObjectID("5ff93b43535bera64de4"),
first_name: some name,
last_name : some name,
dept: department1,
}
department:
{
dept_name: department1,
dept_descp : this is description,
books: [
{
book_name: book1,
subject: subject1,
},
{
book_name: book2,
subject: subject2,
},
]
}
So far I have tried but I didn't get the needed result
person.aggregate([
{
$match:
{
"_id": ObjectId("5ff93b43535bera64de4")
}
},
{
$lookup: {
from: "department",
localField: "dept",
foreignField: "dept_name",
as: "ndept"
}
},
{
$project:{"books": '$ndept.books'}
}
])
the result I would like to get is
Result:
{
_id:ObjectID("5ff93b43535bera64de4"),
first_name: some name,
last_name : some name,
dept: department1,
books: [
{
book_name: book1,
subject: subject1,
},
{
book_name: book2,
subject: subject2,
}
]
}

Try $arrayElemAt to select first element from array
{
$addFields: {
books: { $arrayElemAt: ["$ndept.books", 0] }
}
}
Playground

Related

MongoDB Aggregation - Fetch data from ObjectId subarray

I have these collections:
author
[
{
_id: "63c242130b17d5516e0cb499",
author_name:'Vyom',
book_ids:["63c242330b17d5516e0cb49a","63c242410b17d5516e0cb49b"]
}
]
book
[
{
_id:"63c242330b17d5516e0cb49a",
author_id:'63c242130b17d5516e0cb499',
book_name:'True Love',
genere:'horror'
},
{
_id:"63c242410b17d5516e0cb49b",
author_id:'63c242130b17d5516e0cb499',
book_name:'Monster Strike',
genere:'romance'
},
]
I want to fetch details of books in author collection aggregation if book_ids exists.
For this I tried as:
db.author.aggregate([
{
$match: {
_id: ObjectId("63c242130b17d5516e0cb499")
}
},
{
$lookup:{
from: 'book',
localField: '_id',
foreignField: 'author_id',
as: 'book_details'
}
},
{
$addFields:{
book_info: {
$map: {
input: '$book_details'
as: 'el'
in: {
$match: {_id:ObjectId('$$el._id')},
$paroject: {book_name: 1},
}
}
}
}
}
])
But it throws:
Unrecognized error: '$match'.
Expected O/P:
[
{
_id: "63c242130b17d5516e0cb499",
author_name:'Vyom',
book_ids:["63c242330b17d5516e0cb49a","63c242410b17d5516e0cb49b"],
book_info: [
{
_id:"63c242330b17d5516e0cb49a",
book_name:'True Love',
},
{
_id:"63c242410b17d5516e0cb49b",
book_name:'Monster Strike',
}
]
}
]
Is there any other way to loop and get details? I tried looking for other solutions but was unable to find.
I don't see why you need a $set stage to format the element in the book_details array for the book_info field.
You can use $lookup with pipeline to join both collections and format the array of documents returned.
db.author.aggregate([
{
$match: {
_id: ObjectId("63c242130b17d5516e0cb499")
}
},
{
$lookup: {
from: "book",
localField: "book_ids",
foreignField: "_id",
as: "book_info",
pipeline: [
{
$project: {
_id: 1,
book_name: 1
}
}
]
}
}
])
Demo # Mongo Playground
db.authors.aggregate([
{
$lookup: {
from: "books",
localField: "book_ids",
foreignField: "_id",
as: "books"
}
},
{
$match: { "books": { $ne: [] } }
},
{
$project: {
_id: 1,
author_name: 1,
books: {
_id: 1,
author_id: 1,
book_name: 1,
genre: 1
}
}
}
])
This pipeline will give you the result where all the authors' book details will be there if they have any books

Mongo aggregation framework match a given _id

My model :
const scheduleTaskSchema = new Schema({
activity: { type: Object, required: true },
date: { type: Date, required: true },
crew: Object,
vehicle: Object,
pickups: Array,
details: String,
});
const ScheduleTaskModel = mongoose.model("schedule_task", scheduleTaskSchema),
and this aggregation pipeline :
let aggregation = [
{
$sort: {
"pickups.0.time": 1,
},
},
{
$group: {
_id: "$date",
tasks: { $push: "$$ROOT" },
},
},
{ $sort: { _id: -1 } },
];
if (hasDateQuery) {
aggregation.unshift({
$match: {
date: { $gte: new Date(start_date), $lte: new Date(end_date) },
},
});
} else {
aggregation.push({ $limit: 2 });
}
const scheduledTasksGroups = await ScheduleTaskModel.aggregate(aggregation);
the crew object can have arbitrary number of keys with this structure :
crew : {
drivers: [
{
_id: "656b1e9cf5b894a4f2v643bc",
name: "john"
},
{
_id: "567b1e9cf5b954a4f2c643bhh",
name: "bill"
}
],
officers: [
{
_id: "655b1e9cf5b6632a4f2c643jk",
name: "mark"
},
{
_id: "876b1e9af5b664a4f2c234bb",
name: "jane"
}
],
//...any number of keys that contain an array of objects that all have an _id
}
I'm looking for a way to return all documents (before sorting/grouping) that contain a given _id anywhere within the crew object without knowing which key to search,it can be many different keys that all contain an array of objects that all have an _id
Any ideas ?
You can use $objectToArray for this:
db.collection.aggregate([
{$addFields: {crewFilter: {$objectToArray: "$crew"}}},
{$set: {
crewFilter: {$size: {
$reduce: {
input: "$crewFilter",
initialValue: [],
in: {$concatArrays: [
"$$value",
{$filter: {
input: "$$this.v",
as: "member",
cond: {$eq: ["$$member._id", _id]}
}
}
]
}
}
}}
}},
{$match: {crewFilter: {$gt: 0}}}
])
See how it works on the playground example

Problemm using concat in array mongodb

i have 3 tables
1) Actor: actor_id, first_name, last_name
2) Film: film_id, title
3) Film_Actor: film_id, actor_id
Example document:
_id
:
60aedac769985522a024daca
actor_id
:
"1"
first_name
:
"Penelope"
last_name
:
"Guiness"
I wanto to result not only first_name, but last_name too. I'm facing problem with using concat in $group function.
my FULLY codes:
db.film.aggregate([
{
$lookup: {
from: "film_actor",
localField: "film_id",
foreignField: "film_id",
as: "film_actor"
}
},
{
$lookup: {
from: "actor",
localField: "film_actor.actor_id",
foreignField: "actor_id",
as: "actor"
}
},
{
$group: {
_id: "$film_id",
title: {"$first":"$title"},
name: {$push: "$actor.first_name"}
}
}
]);
Error report:
$concat only supports strings, not array
desired output:
id:"207"
title:"Dangerous Uptown"
name:Array
0:"Penelope Guiness"
1:"Mary Watson"
2:"Ralph Holts"
3:"Spencer Dani"
What you have done is appreciated, I've done some changes in your code
$lookup to join collections. I have started form Flim collection
$unwind to deconstruct the array
$group to reconstruct the array that we already deconstructed, this will
Since we have nested array we need to use $map to loop over them to collect the first name and lastname
The above stage will end up with again nested array, so we use $reduce to loop again and remove inner arrays using $setUnion
remove some duplicate entries, depends on your requirements
Here is the code
db.Film.aggregate([
{
$lookup: {
from: "Film_Actor",
localField: "film_id",
foreignField: "film_id",
as: "join_flim"
}
},
{ "$unwind": "$join_flim" },
{
$lookup: {
from: "Actor",
localField: "join_flim.actor_id",
foreignField: "actor_id",
as: "join_flim.join_actor"
}
},
{
$group: {
_id: "$_id",
title: { $first: "$title" },
join_flim: { $push: "$join_flim" }
}
},
{
"$project": {
title: 1,
actornames: {
$map: {
input: "$join_flim",
as: "f",
in: {
$map: {
input: "$$f.join_actor",
as: "a",
in: {
$concat: [ "$$a.first_name", " ", "$$a.last_name" ]
}
}
}
}
}
}
},
{
"$project": {
title: 1,
actornames: {
"$reduce": {
"input": "$actornames",
"initialValue": [],
"in": {
"$setUnion": [ "$$this", "$$value" ]
}
}
}
}
}
])
Working Mongo playground

Get number of followers for specific page in Mongodb

let's say I have a collection called pages as
{
_id: "pageid",
name: "Mongodb"
},
{
_id: "pageid2",
name: "Nodejs"
}
and user collection as follows
{
_id : "userid1",
following: ["pageid"],
...
},
{
_id : "userid2",
following: ["pageid", "pageid2"],
...
}
how could I make a query to retrieve the pages information along with the number of users follow each page in mongodb, expected result as follows
[
{
_id: "pageid",
name: "MongoDB",
followers: 2
},
{
_id: "pageid2",
name: "Nodejs",
followers: 1
},
]
You can use $lookup and $size to count total followers,
db.pages.aggregate([
{
$lookup: {
from: "user",
localField: "_id",
foreignField: "following",
as: "followers"
}
},
{
$addFields: {
followers: { $size: "$followers" }
}
}
])
Playground

Lookup VS Lookup with pipeline MongoDB (Performace & How it internally works)

I'm making a blog and have an query about which would give me better performace, simple lookup or lookup with pipeline because sometime simple lookup gave me fast result and sometime pipleline lookup. So, I am bit confused now which one to use or where to use. Suppose I have 2 collection, user and comment collection.
// Users Collection
{
_id: "MONGO_OBJECT_ID",
userName: "Web Alchemist"
}
// Comments Collection
{
_id: "MONGO_OBJECT_ID",
userId: "USER_MONGO_OBJECT_ID",
isActive: "YES", // YES or NO
comment: "xyz"
}
Now I want to Lookup from users collection to comments, which one would be better for this. I made two query which giving me same result.
[
{
$match: { _id: ObjectId("5d68c019c7d56410cc33b01a") }
},
{
$lookup: {
from: "comments",
as: "comments",
localField: "_id",
foreignField: "userId"
}
},
{
$unwind: "$comments"
},
{
$match: {
"comments.isActive": "YES"
}
},
{ $limit: 5},
{
_id: 1, userName: 1, comments: { _id: "$comments._id", comment: "$comments.comment"}
},
{
$group: {
_id: "$_id",
userName: { '$first': '$userName' },
comments: { $addToSet: "comments"}
}
}
]
OR
[
{
$match: { _id: ObjectId("5d68c019c7d56410cc33b01a") }
},
{
$lookup: {
from: "comments",
as: "comments",
let: { userId: "$_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ['$userId', '$$userId'] },
{ $eq: ['$isActive', 'YES'] }
]
}
}
},
{ limit: 5 },
{
$project: { _id: 1, comment: 1 }
}
]
}
}
]

Resources