Aggregate multiple collections based on student Id - arrays

I am trying to aggregate 2 collections in MongoDB based on a student's ID. One collection consists of student personal information, another one consists of the students logs. The issue is that the data is in array which is why I think my aggregation is not working. Any help will be appreciated.
student collection
{
"_id" : ObjectId("(Object ID here"),
"data" : [
{
"name" : "John",
"id" : 1
},
{
"name" : "Sandy",
"id" : 2
}
]
}
logs collection
{
"_id" : ObjectId("(Object ID here"),
"logs" : [
{
"studentId" : 1,
"activity" : "11112,334,123"
},
{
"studentId" : 2,
"activity" : "11112,334,123"
}
]
}
Here is what I have tried:
dbo.collection("student").aggregate([
{ "$lookup": {
"localField": "data.id",
"from": "logs",
"foreignField": "logs.studentId",
"as": "studentInfo"
}
}]).toArray(function(err, results) {
console.log(results);
});
Expected result:
studentinfo: {
id: 1,
name: "John",
activity" : "11112,334,123"
}

You can use below aggregation with mongodb 3.6
So basically your foreign field is an array you need to use $lookup with the pipeline to $unwind the foreign array inside the $lookup pipeline and to match the corresponding ids.
db.students.aggregate([
{ "$lookup": {
"from": "logs",
"let": { "dataId": "$data.id" },
"pipeline": [
{ "$unwind": "$logs" },
{ "$match": { "$expr": { "$in": ["$logs.studentId", "$$dataId"] }}},
{ "$replaceRoot": { "newRoot": "$logs" }}
],
"as": "students"
}}
])
or use this to merge both the arrays
db.students.aggregate([
{ "$lookup": {
"from": "logs",
"let": { "dataId": "$data.id" },
"pipeline": [
{ "$unwind": "$logs" },
{ "$match": { "$expr": { "$in": ["$logs.studentId", "$$dataId"] }}},
{ "$replaceRoot": { "newRoot": "$logs" }}
],
"as": "students"
}},
{ "$project": {
"students": {
"$map": {
"input": "$students",
"in": {
"studentId": "$$this.studentId",
"activity": "$$this.activity",
"name": { "$arrayElemAt": ["$data.name", { "$indexOfArray": ["$data.id", "$$this.studentId"] }]}
}
}
}
}}
])
Output
[
{
"students": [
{
"activity": "11112,334,123",
"name": "John",
"studentId": 1
},
{
"activity": "11112,334,123",
"name": "Sandy",
"studentId": 2
}
]
}
]

Related

Join collection with array object field with another collection in MongoDB

I'm working in MongoDB and getting stuck at one aggregation case. Let me show you my collection.
First collection (data):
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal" : "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"place": "Chennai"
},
{
"animal" : "fcfdd527-5885-48b0-a91f-03f72f78528f",
"place": "Kolkata"
}
]
}
]
Second collection (Animal):
[
{
"_id": "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"name": "Dog"
},
{
"_id": "7d44251a-b308-4deb-875a-33ef0a69fe2b",
"name": "Cat"
}
]
I'm using this query:
db.data.aggregate([
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
"$unwind": "$doc"
},
{
"$project": {
"_id": 1,
"data.animal": "$doc.name",
"data.place": 1
}
}
])
and it result me this
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Dog",
"place": "Chennai"
},
{
"animal": "Dog",
"place": "Kolkata"
}
]
},
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Cat",
"place": "Chennai"
},
{
"animal": "Cat",
"place": "Kolkata"
}
]
}
]
Where I'm expecting like this
[
{
"_id": "8e7b3fa0-4230-448c-8f70-1d7300632834",
"data": [
{
"animal": "Dog",
"place": "Chennai"
},
{
"animal": "Cat",
"place": "Kolkata"
}
]
}
]
Mongo Playground
Also sharing this question in Mongo playgroud. Thanks in advance!!
Solution 1
$unset - Deconstruct the data array into multiple documents.
$lookup - Perform join with animal collection.
$project - Decorate the output document. For data.animal field, get the first value via $first.
$group - Group by _id. Push the data document into the data array.
db.data.aggregate([
{
"$unwind": "$data"
},
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
"$project": {
"_id": 1,
"data.animal": {
$first: "$doc.name"
},
"data.place": 1
}
},
{
$group: {
_id: "$_id",
data: {
$push: "$data"
}
}
}
])
Demo Solution 1 # Mongo Playground
Solution 2
$lookup
$set - Set data field.
2.1. $map - Iterate the data array and returns a new array.
2.1.1. $mergeObjects - Merge current iterated document with place field and the document from 2.1.1.1.
2.1.1.1. $first - Get the first document from the filtered doc arrays by matching the ids via $filter.
$unset - Remove _id and animals._id fields.
db.data.aggregate([
{
"$lookup": {
"from": "animal",
"localField": "data.animal",
"foreignField": "_id",
"as": "doc"
}
},
{
$set: {
data: {
$map: {
input: "$data",
as: "data",
in: {
$mergeObjects: [
{
place: "$$data.place"
},
{
$first: {
$filter: {
input: "$doc",
cond: {
$eq: [
"$$this._id",
"$$data.animal"
]
}
}
}
}
]
}
}
}
}
},
{
$unset: [
"doc",
"data._id"
]
}
])
Demo Solution 2 # Mongo Playground

Filter based on values after join operation - MongoDB

I have two collections in the following format -
collection 1
{
"_id": "col1id1",
"name": "col1doc1",
"properties": [ "<_id1>", "<_id2>", "<_id3>"]
}
collection 2
{
"_id": "<_id1>",
"name": "doc1",
"boolean_field": false
}
{
"_id": "<_id2>",
"name": "doc2",
"boolean_field": true
}
{
"_id": "<_id3>",
"name": "doc3",
"boolean_field" : false
}
the desired output is -
{
"_id": "col1id1",
"name": "col1doc1",
"property_names": ["doc1", "doc3"]
}
The field proerties of document in collection1 has three IDs of documents in collection2 but the output after join operation should contain only those which have the boolean_field value as false. How can I perform this filter with join operation in MongoDB?
$lookup can be used along with $unwind to achieve this.
db.col1.aggregate([
{
"$unwind": "$properties"
},
{
"$lookup": {
from: "col2",
localField: "properties",
"foreignField": "_id",
"as": "property_names"
}
},
{
"$match": {
"property_names": {
"$elemMatch": {
"bool_field": false
}
}
}
},
{
"$unwind": "$property_names"
},
{
"$group": {
"_id": "$_id",
"properties": {
"$push": "$properties"
},
"property_names": {
"$push": "$property_names"
}
}
},
{
"$project": {
"_id": 1,
"name": 1,
"property_names": {
"name": 1
}
}
}
]);

MongoDB how to $project (limit fields) from the $lookup remote collection?

I want to $lookup for a remote collection like SQL Join but with Mongo. And I don't want all of the keys from the remote document to be pulled to the origin collection - just some specific keys.
This is what I have tried:
[
{
$lookup: {
from: "tables",
localField: "type",
foreignField: "_id",
as: "type"
}
},
{
$unwind: "$type"
},
},
{
$project: {
"type.title": 1
}
}
]
However this prints only "type.title" and ignores all of other keys even from the origin document.
Is there any way to tell MongoDB to pull only specific fields from the remote collection?
You can use below aggregation with mongodb 3.6 and above
[
{ "$lookup": {
"from": "tables",
"let": { "type": "$type" },
"pipeline": [
{ "$addFields": { "owners": { "$cond": { "if": { "$ne": [ { "$type": "$owners" }, "array" ] }, "then": [], "else": "$owners" } } }},
{ "$match": { "$expr": { "$eq": ["$_id", "$$type"] }}},
{ "$project": { "title": 1 }}
],
"as": "type"
}},
{ "$unwind": "$type" }
]

lookup on ObjectId's in an array

I have shops collection and user collection with list of shops ids inside of it as strings.
example of shop document:
{
"_id" : ObjectId("5a0c6797fd3eb67969316ce2"),
"picture" : "http://placehold.it/150x150",
"name" : "Genmom",
"email" : "leilaware#genmom.com",
"city" : "Rabat",
"location" : {
"type" : "Point",
"coordinates" : [
-6.79387,
33.83957
]
}
}
example of user collection:
{
"_id" : ObjectId("5c04b943ff491824b806686a"),
"email" : "ayoub.khial#gmail.com",
"password" : "$2a$10$4Wt5Rn6udxREdXCIt3hGb.sKhKUKOlyiYKmLTjYG3SqEPKFSw9phq",
"likedShops" : [
"5a0c6797fd3eb67969316ce2",
"5c07ada8ff49183284e509d1",
"5c07acc1ff49183284e509d0"
],
"dislikedShops" : [ ]
}
I want to return the detail of the likedShops.
You can use below $lookup aggregation
db.users.aggregate([
{ "$lookup": {
"from": "shops",
"let": { "likedShops": "$likedShops" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$_id", "$$likedShops"] }}}
],
"as": "likedShops"
}}
])
Or if your ids are string then use $toString aggregation with the ObjectIds
db.users.aggregate([
{ "$lookup": {
"from": "shops",
"let": { "likedShops": "$likedShops" },
"pipeline": [
{ "$match": { "$expr": { "$in": [{ "$toString": "$_id" }, "$$likedShops"] }}}
],
"as": "likedShops"
}}
])

MongoDB count array in array

How to realize count array in array using aggregate? I have this document structure:
{
"_id" : 1,
"link" : [
{
"linkHistory" : [
{
"_id" : 1,
},
{
"_id" : 2,
}
]
}
]
}
and MongoDB code:
db.emailGroup.aggregate([
{
"$lookup":
{
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
},
},
{
"$unwind": "$link"
},
{
"$match": {
'link.originalLink': ""
}
},
{
"$group" : {
_id: '$_id',
link: {
$push: '$link'
}
}
}
])
I want to get count in other field for linkHistory. Is this possible?

Resources