I have two tables in my database.
teams table:
[
{
"users": [
{ "id": '5f9971e6f4f60959c0668896', "role": 'admin' },
{ "id": '5fb11570ece5672ba95850ee', "role": 'read' }
],
"_id": '5fb0f993795d1634d4e486dc',
"name": 'team1'
},
{
"users": [
{ "id": '5fb11570ece5672ba95850ee', "role": 'admin' }
],
"_id": "5fb0f993795d1634d4e48fed",
"name": 'team2'
}
]
users table:
[
{
"_id" : ObjectId("5f9971e6f4f60959c0668896"),
"firstname" : "foo",
"lastname" : "bar"
},
{
"_id" : ObjectId("5fb11570ece5672ba95850ee"),
"firstname" : "hey",
"lastname" : "you"
},
{
"_id" : ObjectId("5fb11570ece5672ba95grs0"),
"firstname" : "12",
"lastname" : "34"
}
]
and what i want to get is this:
{
users:
[
{ id: '5f9971e6f4f60959c0668896',firstname: "foo",lastname: "bar", role: 'admin' },
{ id: '5fb11570ece5672ba95850ee',firstname: "hey",lastname: "you", role: 'read' }
],
_id: 5fb0f993795d1634d4e486dc,
name: 'team1'
}
i'm trying to solve this a week and i didn't find solution.
Teams.aggregate([
{
$lookup: {
from: "users",
localField: "_id",
foreignField: "users.id",
as: "users",
},
},
]).then((data) => {
console.log("data", data);
});
tnx for the help
Your teams collection users.id need to be also ObjectId , not a string ... , or alternatively you need to change the users collection _id's to strings ...
Related
I need to retrieve the entire single object hierarchy from the database as a JSON. Actually the proposal about any other solution to achive this result would be highly appriciated. I decided to use MongoDB with its $lookup support.
So I have four collections:
Users
{ "_id" : "2", "name" : "john" }
{ "_id" : "1", "name" : "Doe" }
Posts
{"_id": "2","body": "hello", likes: []},
{"_id": "1","name": "hello 4", likes: [1,]},
Comments
{"_id": "c2","body": "hello 3",postId: "1",likes: [1,2]},
{"_id": "c1","body": "hello 2",postId: "1",likes: [1,2]},
Replies
{"_id": "r1","name": "hello 4",commentId: "c1",likes: [1]},
{"_id": "r3","name": "hello five",commentId: "c2",likes: [1,2]}
I basically need to retrieve all posts with all corresponding comments and comments.replies as part of my result . My aggregation:
const posts = await PostModel.aggregate([
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "postId",
as: "comments",
},
},
{
$unwind: "$comments",
},
{
$lookup: {
from: "replies",
localField: "comments._id",
foreignField: "commentId",
as: "comments.replies",
},
},
]).sort({
createdAt: -1,
});
MongoDb PlayGround
The result is pretty weird. Some records are ok. But comments return an object . There are also some duplications on the post with _id="1".
[
{
"_id": "1",
"comments": {
"_id": "c2",
"body": "hello 3",
"likes": [
1,
2
],
"postId": "1",
"replies": [
{
"_id": "r3",
"commentId": "c2",
"likes": [
1,
2
],
"name": "hello five"
}
]
},
"likes": [
1
],
"name": "hello 4"
},
{
"_id": "1",
"comments": {
"_id": "c1",
"body": "hello 2",
"likes": [
1,
2
],
"postId": "1",
"replies": [
{
"_id": "r1",
"commentId": "c1",
"likes": [
1
],
"name": "hello 4"
}
]
},
"likes": [
1
],
"name": "hello 4"
}
]
Brief, This is my expected result.
I want to get all posts, with all comments and replies associated with them .
I want to append a count of likes likesCount:{$size:["likes"]} and since I have the user auth id(uid) ready I want to check if the user liked the post , comment or reply based on if isLiked: {$in:[ID(uid),"likes"]}
Since each post have multiple comments, after unwinding comments you need to group it together to form an array
Update
I have updated the fetch approach a lil bit like the below.
db.posts.aggregate([
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "postId",
as: "comments",
},
},
{
$unwind: "$comments",
},
{
$lookup: {
from: "replies",
localField: "comments._id",
foreignField: "commentId",
as: "replies",
},
},
{
$unwind: "$replies",
},
{
"$addFields": {
"replies.countOflikes": {
$size: {
$ifNull: [
"$replies.likes",
[]
]
}
},
"replies.isLiked": {
$cond: {
if: {
$eq: [
{
$size: {
$filter: {
input: "$replies.likes",
as: "item",
cond: {
$eq: [
"$$item",
1//id of the user whom you wanna check if liked the reply
]
}
}
}
},
0
]
},
then: false,
else: true
}
}
}
},
{
$group: {
_id: "$comments._id",
postId: {
$first: "$_id"
},
body: {
$first: "$body"
},
"comments": {
$first: "$comments"
},
replies: {
$push: "$replies"
}
}
},
{
$addFields: {
"comments.replies": "$replies"
}
},
{
$group: {
_id: "$postId",
body: {
$first: "$body"
},
comments: {
$push: "$comments"
}
}
}
])
Summary of the change
Unwinded both comments and it's replies
Added new fields for displaying isLiked and countOfLikes using addFields stage
grouped twice to reform original structure of the data(first grouped by comments then posts)
https://mongoplayground.net/p/lymCfeIIy9j
i have a competition doc with field teams array of object with _id of team and a score doc with teamId field
competitions.teams = [{_id: 100,..}, {..}]
score.teamId = 100
when aggregatig score i want to group it to the competition teams but imm getting all team inside the group innstead of matching id
sample document https://mongoplayground.net/p/yJ34IBnnuf5
db.scores.aggregate([
{
"$match": {
"type": "league"
}
},
{
"$lookup": {
"from": "competitions",
"localField": "competitionId",
"foreignField": "_id",
"as": "comp"
}
},
{
"$unwind": {
"path": "$comp",
"preserveNullAndEmptyArrays": true
}
},
{
"$project": {
"comp.teams": 1,
"teamId": 1
}
},
{
"$group": {
"_id": "$teamId",
"results": {
"$push": "$comp.teams"
}
}
}
])
returns all team in group instead of matched teamid
{
"_id" : 100
"results" : [
{
"_id": 100,
"name": "team 1"
},
{
"_id": 101,
"name": "team 2"
}
]
}
{
"_id" 101
"results" : [
{
"_id": 100,
"name": "team 1"
},
{
"_id": 101,
"name": "team 2"
}
]
}
this is the result im trying to accomplish please guide me
{
"_id" : 100
"results" : [
{
"_id": 100,
"name": "team 1"
}
]
}
{
"_id" 101
"results" : [
{
"_id": 101,
"name": "team 2"
}
]
}
what should i do i've read the docs this seems to be the way?
Demo - https://mongoplayground.net/p/ETeroLftcZZ
You have to add $unwind: { "path": "$comp.teams" }
and after that group by { $group: { "_id": "$comp.teams._id" ... }
db.scores.aggregate([
{ $match: { "type": "league" } },
{ $lookup: { "from": "competitions", "localField": "competitionId", "foreignField": "_id", "as": "comp" } },
{ $unwind: { "path": "$comp", "preserveNullAndEmptyArrays": true } },
{ $unwind: { "path": "$comp.teams", "preserveNullAndEmptyArrays": true }},
{ $group: { "_id": "$comp.teams._id", "results": { $push: "$comp.teams" } } }
])
Demo with more data - https://mongoplayground.net/p/b41Ch5ge2Wp
I try to learn aggregation concept in MongoDB. I create an object like this for training.
"_id": "601c4bb56e018211b02abbf8",
"isDeleted": false,
"name": "TeacherName1",
"class": "7",
"students": [
{ "_id": "601c4bb56e018211b02abbf9", isDeleted:true, "name": "student-1", "studentGroup": "A", "avgResult": 36},
{ "_id": "601c4bb56e018211b02abbfa", isDeleted:false, "name": "student-2", "studentGroup": "A", "avgResult": 55},
{ "_id": "601c4bb56e018211b02abbfb", isDeleted:false, "name": "student-3", "studentGroup": "B", "avgResult": 44.66},
{ "_id": "601c4bb56e018211b02abbfc", isDeleted:false, "name": "student-4", "studentGroup": "C", "avgResult": 83.66},
{ "_id": "601c4bb56e018211b02abbfd", isDeleted:true, "name": "student-5", "studentGroup": "B", "avgResult": 37},
{ "_id": "601c4bb56e018211b02abbfe", isDeleted:true, "name": "student-6", "studentGroup": "C", "avgResult": 39.66},
]
I want to get teacher information and deleted students (isDeleted=true). So I try to get this result.
"_id": "601c4bb56e018211b02abbf8",
"isDeleted": false,
"name": "TeacherName1",
"class": "7",
"students": [
{ "_id": "601c4bb56e018211b02abbf9", isDeleted:true, ...},
{ "_id": "601c4bb56e018211b02abbfd", isDeleted:true, ...},
{ "_id": "601c4bb56e018211b02abbfe", isDeleted:true, ...},
]
I get result with use $unwind and $filter. But can I get this result with only $elemMatch?
If I use this query
this.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId("601c4bb56e018211b02abbf8"),
isDeleted: false,
"students.isDeleted":true
},
},
]);
It returns all object.
If I try this
this.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId("601c4bb56e018211b02abbf8"),
isDeleted: false,
students:{
$elemMatch:{
isDeleted:true
}
}
},
},
]);
It returns all object.
$match will just give you the whole doc should you match
however you may use $project with $filter using another stage
given
db.dummy.insert({studs:[{isDeleted:true, name:'a'},{isDeleted: true, name:'b'},{name:'c'}]})
db.dummy.insert({studs:[{name:'c'}]})
> match = {$match:{studs:{$elemMatch: {isDeleted:true}}}}
> project = {$project: { deletedStuds: {$filter:{input: '$studs', as:'stud', cond:{ $eq: ['$$stud.isDeleted', true]} } } }}
{
"$project" : {
"deletedStuds" : {
"$filter" : {
"input" : "$studs",
"as" : "stud",
"cond" : {
"$eq" : [
"$$stud.isDeleted",
true
]
}
}
}
}
}
> db.dummy.aggregate(match, project)
{ "_id" : ObjectId("6020351eb965951ac8a1eb62"), "deletedStuds" : [ { "isDeleted" : true, "name" : "a" }, { "isDeleted" : true, "name" : "b" } ] }
So for my example database set up:
db.lists.insertMany([
{ _id: "1", name: "list1", included_lists: ["2"], items: ["i1"] },
{ _id: "2", name: "list2", included_lists: [], items: ["i2", "i3"] }
])
db.items.insertMany([
{ _id: "i1", name: "item1", details: [{}, {}, {}] },
{ _id: "i2", name: "item2", details: [{}, {}, {}] },
{ _id: "i3", name: "item3", details: [{}, {}, {}] }
])
I'm currently getting my items data via:
db.lists.aggregate([
{ "$match": { "_id": { "$in": ["1", "2"] } } },
{
"$lookup": {
"from": "items",
"localField": "items",
"foreignField": "_id",
"as": "item"
}
},
{ "$unwind": "$item" },
{
"$facet": {
"results": [
{ "$skip": 0 },
{ "$limit": 10 },
{
"$project": {
name: 1,
item: 1
}
}
],
"total": [
{ "$count": "total" },
]
}
}
]).pretty()
which returns:
{
"results" : [
{
"_id" : "1",
"name" : "list1",
"item" : {
"_id" : "i1",
"name" : "item1",
"details" : [
{
},
{
},
{
}
]
}
},
{
"_id" : "2",
"name" : "list2",
"item" : {
"_id" : "i2",
"name" : "item2",
"details" : [
{
},
{
},
{
}
]
}
},
{
"_id" : "2",
"name" : "list2",
"item" : {
"_id" : "i3",
"name" : "item3",
"details" : [
{
},
{
},
{
}
]
}
}
],
"total" : [
{
"total" : 3
}
]
}
What I'm trying to do, is remove the { "$match": { "_id": { "$in": ["1", "2"] } } }, as I want to remove the query needed to get the array of ids, and instead just get all the ids from list _id and its included_lists ids. Then have return all the items return like my result.
This question is similar to: mongodb - unwinding nested subdocuments but I've reasked due to ambiguity and lack of db documents.
you can do it with graph lookup and then group
db.lists.aggregate([
{ "$match": { "_id": { "$in": ["1"] } } },
{
$graphLookup: {
from: "lists",
startWith: "$_id" ,
connectFromField: "included_lists",
connectToField: "_id",
as: "connected",
}
},
{$unwind:"$connected"},
{ $group:{_id:"$connected._id",items:{$first:'$connected.items'},name:{$first:'$connected.name'}}},
{
"$lookup": {
"from": "items",
"localField": "items",
"foreignField": "_id",
"as": "item"
}
},
{ "$unwind": "$item" },
{
"$facet": {
"results": [
{ "$skip": 0 },
{ "$limit": 10 },
{
"$project": {
name: 1,
item: 1
}
}
],
"total": [
{ "$count": "total" },
]
}
}
]).pretty()
I am using MongoDB 3.6 for my project.
I have 2 collections "users" and "follow". I want to extract out details of user's followers and following (like an Instagram app).
users collection
{
"id" : "1",
"name" : "abc",
"age" : "26"
},
{
"id" : "2",
"name" : "xyz",
"age" : "22"
},
{
"id" : "3",
"name" : "qwe",
"age" : "23"
}
follow collection
{
"id" : "2",
"follow id" : "1"
},
{
"id" : "3",
"follow id" : "1"
},
{
"id" : "1",
"follow id" : "2"
},
{
"id" : "2",
"follow id" : "3"
},
{
"id" : "1",
"follow id" : "3"
}
Now i want following list of id 2 So id 2 is following id 1 and id 3
So, Output should be like this
{
"id" : "1",
"name" : "abc",
"age" : "26"
},
{
"id" : "3",
"name" : "qwe",
"age" : "23"
}
For that, I am using $lookup aggregation. But this is not giving the desired output which I want.
Here is my code -
Follow.aggregate([
{
$lookup:{
from:"users",
localField:"id",
foreignField:"id",
as:"fromItems"
}
},
{
$replaceRoot:{newRoot: {$mergeObjects: [ { $arrayElemAt: ["$fromItems", 0 ] }, "$$ROOT" ] } }
},
{ $project :
{
fromItems : 0
}
}
], callback)
For more understanding please refer the image
To get following list of id 2 you can use following query:
Follow.aggregate([
{
$match: { "id": "2" }
},
{
$lookup:{
from:"users",
localField:"follow id",
foreignField:"id",
as:"fromItems"
}
},
{
$replaceRoot:{newRoot: {$mergeObjects: [ { $arrayElemAt: ["$fromItems", 0 ] }, "$$ROOT" ] } }
},
{ $project :
{
id : "$follow id",
name: 1,
age: 1
}
}
])
So the point here is that you have a relation between id and follow id and after $lookup phase follow id becomes the new id since it's parent-child relation.
EDIT:
3.4 solution below
Follow.aggregate([
{
$match: { "id": "2" }
},
{
$lookup:{
from:"users",
localField:"follow id",
foreignField:"id",
as:"fromItems"
}
},
{
$project: {
id: "$follow id",
from: { $arrayElemAt: ["$fromItems", 0 ] }
}
},
{ $project :
{
id : 1,
name: "$from.name",
age: "$from.age"
}
}
])