How to get expected output from MongoDB? - javascript

I'm new to MongoDB aggregation. I am not getting desired output
The output I'm getting from aggregation:-
[
{tweet:{key:value}},
{tweet:{key:value}},
{tweet:{key:value}},
{tweet:{key:value}},
]
but I want the following output from the pipeline:-
[
{key:value},
{key:value},
{key:value},
]
and lastly, pipeline I'm running:-
const pipeline = [[
{
$match: {
$expr: {
$in: [
Mongoose.Types.ObjectId(userid), '$likedBy.user'
]
}
}
}, {
$lookup: {
from: 'tweets',
localField: 'tweet',
foreignField: '_id',
as: 'tweet'
}
}, {
$unwind: {
path: '$tweet'
}
}, {
$lookup: {
from: 'users',
localField: 'tweet.user',
foreignField: '_id',
as: 'user'
}
}, {
$unwind: {
path: '$user'
}
}, {
$addFields: {
'tweet.user': '$user'
}
},
{
$addFields: {
'tweet.isLiked': true,
}
},{
$project:{
tweet:1,
}
},
]
];
const likedTweets = await TweetLike.aggregate(pipeline)
I know I can do this with javascript but I want to do it with the pipeline

You can replace your last project stage with the following to achieve what you need:
{$project:{key:"$tweet.key"}}

Answering my own question
i wanted to return sub-document so i found this https://stackoverflow.com/a/43411988/12332711
all i had to do is use
{
$replaceRoot: {newRoot: "$tweet"}
}
it worked for me

Related

$addFields $size property always returns zero - mongoose

Hi I have added $addFields property in aggregate query and $size of my documents always return 0. here are my tables and query
table post:
{
_id: 1,
text: 'some text',
}
table comments:
{
_id: 1,
text: 'comment text',
postId: 1
}
In aggregate i have the following
let aggregateDataQuery = [
{
$lookup: {
from: 'comments',
localField: '_id',
foreignField: 'postId',
as: 'numberOfComments',
},
},
{
$addFields: {numberOfComments: { $size: { $ifNull: ['$numberOfComments', []] } }},
},
];
This query always result in numberOfComments: 0. I am sure that there are comments against postId 1 but result is always zero. Any Idea what i'm missing here. thanks
Hi the clause from in $lookup stay of the collection than you want to join.
The starter collection must be posts collection.
So you should put comments instead posts.
The $size attribute not work because the join not join.
The code must be shomething like:
let aggregateDataQuery = [
{
$lookup: {
from: 'comments',
localField: '_id',
foreignField: 'postId',
as: 'numberOfComments',
},
},
{
$addFields: {numberOfComments: { $size: { $ifNull: ['$numberOfComments', []] } }},
},
];
Posts.aggregate(aggregateDataQuery);

how to use mongoose aggregation to get sum of two matching documents depending on field

I have two collections "Employee", "Office"
I am trying to find how many employees are in each area which contains office code. But there might be more than one office in the same area.
This is how my Office documents might look like
[
{
_id: "5b7d0f77e231b6b530b0ee5a",
code: "OB123456",
city: "Canmore"
// some other fields
},
{
_id: "5b7d0f77e531b6b530b0ee5b",
code: "OB858758",
city: "Vancouver"
},
{
_id: "5b7d0f77e531b6b530b0ee5d",
code: "EE858758",
city: "Vancouver"
},
]
this is how my Employee documents might look like
[
{
_id: "5b7d0f77e531b6b530b0edda",
name: 'Charlie',
office: {
code: 'OB123456'
// some other fields
}
},
{
_id: "5b7d0f73e531b6b530b0ee5b",
name: 'Bill',
office: {
code: 'EE858758'
}
},
{
_id: "5b7d0f77e531b6b530b0ee5n",
name: 'Echo',
office: {
code: 'OB123456'
}
},
];
I am looking into mongoose aggregate, and only tried
await Employee.aggregate([
{
$lookup: {
from: 'offices',
localField: 'office.code',
foreignField: 'code',
as: 'officeCode'
},
$group: {
_id: 'officeCode.city',
count: { $sum: 1 }
}
}
]);
which for sure does not work, I tried reading some of the aggregation documention but cannot come up with a good idea how to get this done
Thanks in advance for any suggestions or advices.
Sample output of what I am looking for
{
"Vancouver": 1,
"Canmore": 2
}
You have to start from office instead of employee, so you can create a list of code for each area (city), then lookup to map with your employees.
db.office.aggregate([
{
$group: {
_id: "$city",
codes: {
$addToSet: "$code"
}
}
},
{
$lookup: {
from: "employee",
localField: "codes",
foreignField: "office.code",
as: "employees"
},
},
{
$group: {
_id: null,
data: {
$push: {
k: "$_id",
v: {
$size: "$employees"
}
}
}
}
},
{
$replaceRoot: {
newRoot: {
"$arrayToObject": "$data"
}
}
}
])
The two last stages are here only to format your result as described in your expected output.
You can test it here

Group by Date part only (without the time) in MongoDB + NodeJS

Suppose we have the query :
EightWeekGamePlan.aggregate(
[
{
$group: {
_id: {
LeadId: "$LeadId",
Week: "$Week",
InsertDate: "$InsertDate" , // I want to group by the date part
Status: "$Status"
},
count: { $count: 1 }
}
},
{
$lookup: {
from: "leads",
localField: "_id",
foreignField: "LeadId",
as: "Joined"
}
},
{ $unwind: "$Joined" },
{ $replaceRoot: { newRoot: { $mergeObjects: ["$Joined", "$$ROOT"] } } },
{ $sort: { total: -1 } }
],
function(err, results) {
if (err) {
console.log(err);
}
// ... do some manipulations ...
console.log(_filtered);
return res.json(_filtered);
}
);
I grouping by multiple fields and I want to take only the date part of InsertDate and disregard the time.
How can we do that ?
I believe your question is addressed in mongodb documentations under Group by Day of the Year:
https://docs.mongodb.com/manual/reference/operator/aggregation/group/
You have to convert the date into date-formatted string using $dateToString and add it to $group _id
_id : {$dateToString: { format: "%Y-%m-%d", date: "$InserDate" }}
I hope this helps!

Aggregation if local field exist in foreign field

I am using MeteorJS. Now I am trying to fetch data by using meteor call method. It's working well. But I have $lookup for aggregation it's also working fine. Now I am trying to only fetch data by unique, no need duplicate.
[![Meteor.methods({
allIndications(someId) {
const indications = Promise.await(
Medicines.aggregate(\[
{
$lookup: {
from: "indications",
localField: "medicine_indications",
foreignField: "_id",
as: "AllIndications"
}
},
{
$unwind: {
path: "$AllIndications",
preserveNullAndEmptyArrays: true
}
},
{ $project: { _id: 1, AllIndications: 1 } }
\]).toArray()
);
return indications;
}
});][1]][1]
You can try this
[![Meteor.methods({
allIndications(someId) {
const indications = Promise.await(
Medicines.aggregate(\[
{
$lookup: {
from: "indications",
localField: "medicine_indications",
foreignField: "_id",
as: "AllIndications"
}
},
{
$unwind: {
path: "$AllIndications",
preserveNullAndEmptyArrays: true
}
},
{
$group:{
_id:null,
AllIndications:{$addToSet: "$AllIndications"}
}
},
{ $project: { _id: 1, AllIndications: 1 } }
\]).toArray()
);
return indications;
}
});][1]][1]

using max query with mongoose

I am new with mongoose and still trying to understand how make correct queries
I have 2 simple Models
User :
const UserSchema = new Schema({
name: String,
age: Number,
movies:[{
type: Schema.Types.ObjectId,
ref: 'movie'
}]
}, { collection: 'USER_COLLEC' });
Movie :
const MovieSchema = new Schema({
title:String ,
duration: Number
}, { collection: 'MOVIE_COLLEC' });
What I want is the user with le longest movie ( highest duration )
For now I got that :
db.getCollection('USER_COLLEC') .
aggregate([
{ "$unwind": "$movies" } ,
{ $lookup:
{from: "MOVIE_COLLEC",
localField: "movies",
foreignField: "_id",
as: "movieContent"},
} ,
{ $unwind: "$movieContent" },
{ $group:
{ maxDuration: { $max: "$movieContent.duration" },
}
}
])
But it will only find the max duration with no user attached to it...
And indeed I only ask for the max duration on my query, but after the lookup I lose my user :(
How can I can keep it, or retrieve my user data ?
If you have any idea, I am completely stuck...
Thanks guys !
you can use $push to get the movie object as well.
db.getCollection('USER_COLLEC') .
aggregate([
{ "$unwind": "$movies" } ,
{ $lookup:
{from: "MOVIE_COLLEC",
localField: "movies",
foreignField: "_id",
as: "movieContent"},
} ,
{ $unwind: "$movieContent" },
{ $group:
{ _id: { $max: "$movieContent.duration" },
"movie": {
"$push": "movieContent"
}
}
}
])
After this, just get search for the Movie's _id in the user's movies array
UserSchema.find({movies:{$in:movieContent[0]._id}});
OR, instead of $push you can also use $first
{ $first: "$movieContent" }
Then you won't get it in an array.
Update:
Instead of {$push: $movieContent} or{$first: $movieContent}, you could just push $$ROOT:
{$push: $$ROOT} or {$first: $$ROOT}
and then you'll get the entire object. You don't need to fire another query to get the user.
I finally managed to find the solution, the $group was not the solution
db.getCollection('USER_COLLEC') .
aggregate([
{ "$unwind": "$movies" } ,
{ $lookup:
{from: "MOVIE_COLLEC",
localField: "movies",
foreignField: "_id",
as: "movieContent"},
} ,
{ $unwind: "$movieContent" },
{$sort: {"movieContent.duration":-1}},
{ $project: { "user":"$name","duration" : "$movieContent.duration"} } ,
{ $limit : 1 }
Which gives me something like :
{
"_id" : ObjectId("59d2f64dded1c008192f7e73"),
"user" : "Michael",
"duration" : 96
}

Categories

Resources