How to combined two output in lambda function using mongodb? - mongodb

I have two collection 1) profile ,2) posts find the below pic for your reference. user_prfoile collection
user_posts collection
In the lambda function, when i passed the userid, then we will get the userid revlevant data display. but i need user details in feelings array.
I tried with the below code, but i get the empty output
def lambda_handler(event, context):
print("Received event: " + json.dumps(event, indent=1))
Userid = event['userid']
uid = ObjectId(Userid)
dispost = list( db.user_posts.aggregate([{ "$match" : { "userid" : uid } },
{ "$unwind" : "$user_profile" },
{"$lookup":
{
"from" : 'user_profile',
"localField" : 'feelings.userid',
"foreignField" : '_id',
"as" : 'user_details'
}
},
{ "$addFields" : { "user_details.feelings.username" : "$user_profile.username", "user_details.feelings.photo" : "$user_profile.photo"}},
{ "$group" : {
"_id" : "$_id",
"user_profile" : {
"$push" : {
"$arrayElemAt" : ["$user_details", 0]
}}}},
{ "$project" : { "_id" : "$_id", "userid" : 1, "type" : 1, "feelings" : 1 }}
]))
disair = json.dumps(dispost, default=json_util.default)
return json.loads(disair)
I get an empty output.
I need output like this below.
_id :
userid :
type :
location :
feelings :
[ {userid : objectid(""),
username : "nipun",
photo : " "},
{userid : objectid(""),
username : "ramesh",
photo : " "}
]
in feelings array , i need user details from user_profile collection based on userid in feelings array.

Ok, there are couple of issues with that aggregation query, keeping most of it as is, I've modified it to work, as of now please try this and make any changes needed :
db.getCollection('user_posts').aggregate([{ "$match": { "userid": uid } },
{ "$unwind": "$feelings" },
{
"$lookup":
{
"from": 'user_profile',
"localField": 'feelings.userid',
"foreignField": '_id',
"as": 'feelings'
}
},
{
"$group": {
"_id": "$_id", userPost: { "$first": "$$CURRENT" }, "feelings": {
"$push": { "$arrayElemAt": ["$feelings", 0] }
}
}
}, { "$project": { userid: '$userPost.userid', type: '$userPost.type', "feelings": 1 } }
])

Related

How can I use MongoDB's aggregate with $lookup to replace an attribute that holds an id with the whole document?

I have two collections:
user:
{
"_id" : "9efb42e5-514d-44bd-a4b8-6f74e6313ec2",
"name" : "Haralt",
"age" : 21,
"bloodlineId" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"villageId" : "foovillage"
}
bloodlines:
{
"_id" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"name" : "Tevla",
"legacy" : 0
}
Now I'd like to do an aggregate to replace user.bloodlineId with the whole bloodline document.
This is what I tried to far:
db.getCollection('character').aggregate([
{
"$match": { _id: "9efb42e5-514d-44bd-a4b8-6f74e6313ec2" }
},
{
"$lookup": {
from: "bloodline",
localField: "bloodlineId",
foreignField: "_id",
as: "bloodline"
}
}])
The result is almost where I want it:
{
"_id" : "9efb42e5-514d-44bd-a4b8-6f74e6313ec2",
"name" : "Haralt",
"age" : 21,
"bloodlineId" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"villageId" : "foovillage",
"bloodline" : [
{
"_id" : "c59a2d02-f304-49a8-a52a-44018fc15fe6",
"name" : "Tevla",
"legacy" : 0
}
]
}
Only two issues here. The first is that bloodlineId is still there and bloodline was just added to the result. I'd like to have bloodline replace the bloodlineId attribute.
The second problem is that bloodline is an array. I'd love to have it a single object.
I think this pipeline might do the trick:
[
{
"$match": {
_id: "9efb42e5-514d-44bd-a4b8-6f74e6313ec2"
}
},
{
"$lookup": {
from: "bloodlines",
localField: "bloodlineId",
foreignField: "_id",
as: "bloodline"
}
},
{
$project: {
"age": 1,
"bloodlineId": {
$arrayElemAt: [
"$bloodline",
0
]
},
"name": 1,
"villageId": 1
}
}
]
Mongo Playground
If there's anything I'm missing, please let me know!

Do count with condition inside mongDB aggregation's function

I have 2 collections inside my MongoDB:
Order:
{
"date" : ISODate("2020-07-30T22:00:00.000Z"),
"item" : "banana",
"price": "4$",
"order_type" : "INTERNATIONAL", // There are 2 order types: INTERNATIONAL and MAINLAND
"user" : { // I use a dbref to the User collection
"$ref" : "user",
"$id" : "user_0"
}
}
User:
{
"_id": "user_0"
"login" : "user1",
"password" : "$2a$10$mE.qmcV0mFU5NcKh73TZx.z4ueI/.bDWbj0T1BYyqP481kGGarKLG",
"first_name" : "Henry",
"last_name" : "Windsor",
"email" : "hw#gmail.com",
}
Each order contains a DB reference to the corresponding user who made it. This is my mongo code to calculate the total number of orders that each user makes.
db.getCollection('order').aggregate([
{$group: {
_id: '$user',
totalNbOfOrders: {$sum: 1}
}},
{$addFields: {foreign_key: {$objectToArray: "$_id"}}},
{$lookup: {
from: 'user',
localField: 'foreign_key.1.v',
foreignField: '_id',
as: 'userInfo'
}
},
{ $unwind: '$userInfo'},
{ $project: {
'_id': 0,
'first_name': '$userInfo.first_name',
'last_name': '$userInfo.last_name',
'totalNbOfOrders': '$totalNbOfOrders'
}
}
])
And the result is:
/* 1 */
{
"first_name" : "John",
"last_name" : "Kennedy",
"totalNbOfOrders" : 2.0
}
/* 2 */
{
"first_name" : "Peter",
"last_name" : "Parker",
"totalNbOfOrders" : 4.0
}
/* 3 */
{
"first_name" : "Bruce",
"last_name" : "Banner",
"totalNbOfOrders" : 2.0
}
Now, what I also want to calculate is the number of international orders (and eventually of mainland orders) that each user made to have something like this:
{
"first_name" : "Tony",
"last_name" : "Stark",
"totalNbOfOrders" : 10.0,
"totalNbOfInternationalOrders": 4.0
"totalNbOfMainlandOrders": 6.0
}
I haven't figured out how to write the code.
I tried to use "$accumulator" operator (new feature in version 4.4 of MongoDB) inside "$group" but I used MongoDB 4.2.7, I have to use operators from older versions to accomplish this. Does anybody know how to solve this problem?
You can do it inside $group, using $cond and $eq,
{
$group: {
... // skipped
// INTERNATIONAL COUNT
totalNbOfInternationalOrders: {
$sum: {
$cond: {
if: {
$eq: ["$order_type", "INTERNATIONAL"]
},
then: 1,
else: 0
}
}
},
// MAINLAND COUNT
totalNbOfMainlandOrders: {
$sum: {
$cond: {
if: {
$eq: ["$order_type", "MAINLAND"]
},
then: 1,
else: 0
}
}
}
}
},
set in $project
{
$project: {
... // skipped
"totalNbOfInternationalOrders": "$totalNbOfInternationalOrders",
"totalNbOfMainlandOrders": "$totalNbOfMainlandOrders"
}
}
Playground: https://mongoplayground.net/p/_IeVcSFt_nY

Aggregate with lookup and contcat Strings in MongoDb

I have two collections events & members :
events Schema :
{
name : String,
members: [{status : Number, memberId : {type: Schema.Types.ObjectId, ref: 'members'}]
}
events Sample Doc :
"_id" : ObjectId("5e8b0bac041a913bc608d69d")
"members" : [
{
"status" : 4,
"_id" : ObjectId("5e8b0bac041a913bc608d69e"),
"memberId" : ObjectId("5e7dbf5b257e6b18a62f2da9"),
"date" : ISODate("2020-04-06T10:59:56.997Z")
},
{
"status" : 1,
"_id" : ObjectId("5e8b0bf2041a913bc608d6a3"),
"memberId" : ObjectId("5e7e2f048f80b46d786bfd67"),
"date" : ISODate("2020-04-06T11:01:06.463Z")
}
],
members Schema :
{
firstname : String
photo : String
}
members Sample Doc :
[{
"_id" : ObjectId("5e7dbf5b257e6b18a62f2da9"),
"firstname" : "raed",
"photo" : "/users/5e7dbf5b257e6b18a62f2da9/profile/profile-02b13aef6e.png"
},
{
"_id" : ObjectId("5e7e2f048f80b46d786bfd67"),
"firstname" : "sarra",
"photo" : "/5e7e2f048f80b46d786bfd67/profile/profile-c79f91aa2e.png"
}]
I made a query with aggregate, and lookup to get populated data of members, and I want to concat the photo fields of the members by a string, but I get an error,
How can I do the concat ?
Query :
db.getCollection('events').aggregate([
{ $match: { _id: ObjectId("5e8b0bac041a913bc608d69d")}},
{
"$lookup": {
"from": "members",
"localField": "members.memberId",
"foreignField": "_id",
"as": "Members"
}
},
{
$project: {
"Members.firstname" : 1,
"Members.photo": 1,
//"Members.photo": {$concat:["http://myurl", "$Members.photo"]},
"Members._id" : 1,
},
}
])
Result without the concat :
{
"_id" : ObjectId("5e8b0bac041a913bc608d69d"),
"Members" : [
{
"_id" : ObjectId("5e7dbf5b257e6b18a62f2da9"),
"firstname" : "raed",
"photo" : "/users/5e7dbf5b257e6b18a62f2da9/profile/profile-02b13aef6e.png"
},
{
"_id" : ObjectId("5e7e2f048f80b46d786bfd67"),
"firstname" : "sarra",
"photo" : "/5e7e2f048f80b46d786bfd67/profile/profile-c79f91aa2e.png"
}
]
}
Error :
$concat only supports strings, not array
You can do that simply by adding pipeline to $lookup stage
db.events.aggregate([
{
$match: {
_id: ObjectId("5e8b0bac041a913bc608d69d"),
},
},
{
$lookup: {
from: "members",
let: { memberId: "$members.memberId" },
pipeline: [
{ $match: { $expr: { $in: ["$_id", "$$memberId"] } } },
{
$project: {
firstname: 1,
photo: { $concat: ["http://myurl", "$photo"] }
}
}
],
as: "Members",
}
},
/** Optional */
{$project : {Members: 1}}
]);
Test : MongoDB-Playground
the alternative of using a pipeline in the above answer
we may use project and group
db.events.aggregate([
{
$match: { _id: ObjectId("5e8b0bac041a913bc608d69d") }
},
{
$unwind: '$members' // to spread the members array into a stream of documents
},
{
$lookup: {
from: "members",
localField: "members.memberId",
foreignField: "_id",
as: "member"
}
},
{
$unwind: '$member' // each document will have array of only one member, so do this unwind to convert it to an object
},
{
$project: { // do the project here to be able to use the $concat operator
'member._id': 1,
'member.firstname': 1,
'member.photo': 1,
'member.photo': { $concat: ['http://myurl', '$member.photo'] } // now we can use the $concat as member is an object, then member.photo exists
}
},
{
$group: { // do that grouping stage to gather all the members belong to the same document in one array again
_id: '$_id',
Members: {
$addToSet: '$member'
}
}
}
])

Aggregation unwind multiple arrays $lookup as multiple fields for projection

This is my current query:
Follow.aggregate([
{
$match: {
"user": Types.ObjectId(user_id)
}
},
{
$unwind: "$following"
},
{
$lookup: {
"from": "users",
"localField": "following",
"foreignField": "createdById",
"as": "followingUsers"
}
}, {
$project: {
"user": 1,
"followingUsers": 1
}
}
])
This query is run on a document like this:
{
"_id" : ObjectId("5a271a93a19d690b25a3e181"),
"user" : ObjectId("5a271a4f50261a3c1695a391"),
"following" : [
ObjectId("5a257c87086eb00712fd02ec"),
ObjectId("5a257a79086eb00712fd02eb")
],
"followers" : [
ObjectId("5a257a79086eb00712fd02eb")
]
}
And gives a result like this:
{
"_id" : ObjectId("5a271a93a19d690b25a3e181"),
"user" : ObjectId("5a271a4f50261a3c1695a391"),
"followingUsers" : [
{
"_id" : ObjectId("5a257a79086eb00712fd02eb"),
"email" : "email#gmail.com",
"username" : "username",
"email_verified" : true,
"created" : ISODate("2010-12-04T16:40:25.670Z"),
"__v" : 0,
"last_login" : ISODate("2010-12-06T21:14:25.538Z"),
"is_active" : false
}
]
}
The above unwinds only one of the arrays and follows up the lookup.
I want to, in one scoop (assuming that is possible)
unwind both the $following and $followers array fields
$lookup both of the array fields independently
then have both come out as, say, followingUsers and followersUsers
So end up with something like this:
{
"_id" : ObjectId("5a271a93a19d690b25a3e181"),
"user" : ObjectId("5a271a4f50261a3c1695a391"),
"followingUsers" : [
{
"_id" : ObjectId("5a257a79086eb00712fd02eb"),
....
}
],
"followersUsers": [
{
"_id" : ObjectId("5a257a79086eb00712fd02eb"),
....
}
]
}
Am I even sane to imagine something like this in MongoDB?

Compare two fields in a MongoDB aggregation's match on a looked up value

I want to compare two dynamic fields like I'd do with:
$where: [ "$foo_update > $bar_update" ]
I need this to get a bunch of objects that must be updated. It depends on several conditions if they must get updated, so that's why I want to make it with an aggregation.
The current query for the related part looks like:
[
{ $sort: { "updated_at": 1 }
{ $group: {
"_id" : "$bar",
"foo" : { "$first" : "$foo" },
"bar" : { "$first" : "$bar" },
"last_update" : { "$last " : "$updated_at" }
} },
{ $lookup: {
"from" : "table_foo",
"localField" : "foo",
"foreignField" : "_id",
"as" : "foo"
} },
{ $lookup: {
"from" : "table_bar",
"localField" : "bar",
"foreignField" : "_id",
"as" : "bar"
} }
]
Here I could follow with another $group operator to get the values I need out to the top-level. But I cannot do that with the lookup values as it is mostly an array of items.
Here, one item is expected (and I make a query for that too as we need update if the other item is removed).
So now I want to compare the $last_update and the foo.update_at field. It would look something like this in my head.
$match: {
"foo": {
$elemMatch: {
"updated_at": { "$gte": "$last_update" }
}
}
}
Is this even possible?
If yes, how would you do it?
And yes, it is possible. It turned out that you can move the value of the array to the top with operators like $max.
So my solution looks like this now:
[
/** the beginning of the query above **/
{ $group: {
"_id" : "$_id",
"foo" : { "$first" : "$foo" },
"foo_updated" : { "$max" : "$foo_updated_at" },
"bar" : { "$first" : "$bar" },
"bar_updated" : { "$max" : "$bar.updated_at" },
"last_update" : { "$last " : "$updated_at" }
}, $project: {
"_id" : "$_id",
"foo" : "$foo",
"foo_updated" : { "$gt": [ "$foo_updated", "$last_create" ] },
"bar" : "$bar",
"bar_updated" : { "$gt": [ "$bar_updated", "$last_create" ] },
"last_create" : "$last_create"
}, $match: {
"$or": [
/** other conditions **/
{ "foo_updated": true },
{ "bar_updated": true }
]
} }
]