How to get linked data from 2 collections with aggregation mongoDB - node.js

I'm new as MongoDB, I have 2 collections: User and Video with below structure. When user like a video, video's _id will be added to liked_videos on user collection. liked_videos is an array of _id
video collection
{
_id
mp4_url,
liked_count
}
User collection
{
_id,
username,
password,
liked_videos: [ // videos _id array ]
}
How do I query to get the user's liked videos? Like below?
[
{
_id: 1
mp4_url,
liked_count
},
{
_id: 2
mp4_url,
liked_count
},
...
]
Thank you

You need to use $lookup to join collections and $size to get the array size
db.video.aggregate([
{
"$lookup": {
"from": "user",
"localField": "_id",
"foreignField": "liked_videos",
"as": "join_video"
}
},
{
"$project": {
liked_count: {
$size: "$join_video"
}
}
}
])
Working Mongo playground

Try this way
db={
"video": [
{
"_id": 1,
"mp4_url": "url1",
"liked_count": 2,
"quantity": 2
},
{
"_id": 2,
"mp4_url": "url2",
"liked_count": 3,
"quantity": 1
},
{
"_id": 3,
"mp4_url": "url2",
"liked_count": 3,
"quantity": 4
}
],
"User": [
{
"_id": 1,
"username": "almonds",
"password": "pwd",
"Likedvideos": [
"aaa",
"ffff"
]
},
{
"_id": 2,
"username": "almonds",
"password": "pwd",
"Likedvideos": [
"qqq",
"bbbb"
]
},
{
"_id": 3,
"username": "almonds",
"password": "pwd",
"Likedvideos": [
"ccc",
"ffff"
]
},
{
"_id": 4,
"username": "almonds",
"password": "pwd",
"Likedvideos": [
"bbbb",
"ffff"
]
},
{
"_id": 5,
"username": "almonds",
"password": "pwd",
"Likedvideos": [
"qqq",
"ffff"
]
},
{
"_id": 6
}
]
}
Query :
db.video.aggregate([
{
$match: {
_id: 1
}
},
{
"$lookup": {
"from": "User",
"localField": "_id",
"foreignField": "_id",
"as": "data"
},
},
{
$unwind: "$data"
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$data",
"$$ROOT"
]
}
}
},
{
$project: {
data: 0
}
}
])
Output:
[
{
"Likedvideos": [
"aaa",
"ffff"
],
"_id": 1,
"liked_count": 2,
"mp4_url": "url1",
"password": "pwd",
"quantity": 2,
"username": "almonds"
}
]
mongoplayground

Related

How to filter results from collection the $lookup in mongoose

i want to filter the result as the following in mongodb. I use $lookup to populate the result from another collection. Please check my following code
This code below is what i get
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"_id": "5f3a9bbc325a074240a1a815",
"firstname": "test",
"lastname": "test",
"storename": "test",
"gst": "test",
"phoneNumber": 1,
"email": "1#demo.com",
"address1": "test",
"address2": "test",
"city": "test",
"state": "test",
"country": "test",
"zip": "1",
"password": "1",
"usertype": 3,
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0
}
]
},
How to retrieve only unique_SHOP and zip from source listing.I want result like the one below with one or more fields
{
"_id": "5f3d563122de0730d0f6a754",
"barcode": "1234",
"productname": "Lays Packet",
"brandName": "Lays",
"productSize": "12",
"price": "12",
"quant": "12",
"imageurl": "http://localhost:3000/images/imageurl-1597855281050.jpg",
"remaining": "12",
"creator": "3d943b957fb5db510d824c5cbd6e8f7d",
"__v": 0,
"source": [
{
"zip": "1",
"unique_SHOP": "3d943b957fb5db510d824c5cbd6e8f7d",
}
]
},
The query i use
List.aggregate([
{$match:
{ productname: { $regex: req.params.query,$options: "i" }}
},
{ $lookup:{
from: "suppliers",
localField: "creator",
foreignField: "unique_SHOP",
as: "source"
}
},
])
You can try $lookup with pipeline,
$match condition of creator id
$project to display required fields
{
$lookup: {
from: "suppliers",
as: "source",
let: { creator: "$creator" },
pipeline: [
{
$match: {
$expr: { $eq: ["$$creator", "$_id"] }
}
},
{
$project: {
_id: 0,
zip: 1,
unique_SHOP: 1
}
}
]
}
}
Playground

How to change this response to simple array?

{
"success": true,
"message": "Result",
"data": [
{
"Here": [
{
"_id": "5ee97ee7f25d1c1482717bdf",
"email": "test1#test.io",
"profileImages": [],
"username": "test1",
"birthday": "2020-06-11T10:11:32.000Z",
"phoneNumber": "+910000000000",
"location": "Test Location",
"firstName": "test1",
"lastName": "test1",
}
]
},
{
"Here": [
{
"_id": "5ee97ef2f25d1c1482717be1",
"email": "test2#test.io",
"profileImages": [],
"username": "test2",
"birthday": "2020-06-11T10:11:32.000Z",
"phoneNumber": "+910000000000",
"location": "Test Location"
}
]
}
],
}
What I am expecting is this
{
"success": true,
"message": "Result",
data: [
{
"_id": "5ee97ee7f25d1c1482717bdf",
"email": "test1#test.io",
"profileImages": [],
"username": "test1",
"birthday": "2020-06-11T10:11:32.000Z",
"phoneNumber": "+910000000000",
"location": "Test Location",
"firstName": "test1",
"lastName": "test1"},
{
"_id": "5ee97ef2f25d1c1482717be1",
"email": "test2#test.io",
"profileImages": [],
"username": "test2",
"birthday": "2020-06-11T10:11:32.000Z",
"phoneNumber": "+910000000000",
"location": "Test Location"
}
]
}
Query I am using is for this response is below using aggregation in mongodb, lookup and project which is leading me to the some undesired response
db.collections.aggregate( [
{
$lookup: {
from: 'users',
as: 'Here',
let: {
whoDid: '$whoDid'
},
pipeline: [
{
"$match": { "$expr": { "$eq": ["$_id", "$$whoDid"] } }
},
{
$project: {
_id: 1,
email: 1,
profileImages: 1,
username: 1,
birthday: 1,
phoneNumber: 1,
firstName: 1,
lastName: 1,
fullName: 1,
// age: {$year: "$birthday"}
age: {
$divide: [{ $subtract: [new Date(), "$birthday"] },
(31558464000)]
}
}
}
],
}
},
{
$project:{
Here:1,
_id:0
}
} ,
])
who did table is one of the collection I have where I have stored the user Id and later I am populating the data using lookup
{
"_id" : ObjectId("5ee988eb1aac0022e15dbb7b"),
"whoDid" : ObjectId("5ee97ef2f25d1c1482717be1"),
"toWhomDid" : ObjectId("5ee97ec0f25d1c1482717bdd"),
"modified_at" : ISODate("2020-06-17T03:07:23.217Z"),
"created_at" : ISODate("2020-06-17T03:07:23.217Z"),
"__v" : 0
}
{
"_id" : ObjectId("5ee988eb1aac0022e15dbb7c"),
"whoDid" : ObjectId("5ee97ec0f25d1c1482717bdd"),
"toWhomDid" : ObjectId("5ee97ef2f25d1c1482717be1"),
"modified_at" : ISODate("2020-06-17T03:07:23.220Z"),
"created_at" : ISODate("2020-06-17T03:07:23.220Z"),
"__v" : 0
}
Can anyone suggest me any better option so that I can get a desired respose?
It is possible to use reduce method:
obj.data = obj.data.reduce((a, c) => {
a.push(...c.Here);
return a;
}, [])
An example:
let obj = {
"success": true,
"message": "Result",
"data": [ {
"Here": [ {
"_id": "5ee97ee7f25d1c1482717bdf", "email": "test1#test.io",
"profileImages": [], "username": "test1",
"birthday": "2020-06-11T10:11:32.000Z", "phoneNumber": "+910000000000", "location": "Test Location",
"firstName": "test1", "lastName": "test1",
}
]
},
{
"Here": [ {
"_id": "5ee97ef2f25d1c1482717be1",
"email": "test2#test.io",
"profileImages": [],
"username": "test2",
"birthday": "2020-06-11T10:11:32.000Z",
"phoneNumber": "+910000000000",
"location": "Test Location"
}
]
}
]
};
obj.data = obj.data.reduce((a, c) => {
a.push(...c.Here);
return a;
}, [])
console.log(obj);
Add these extra steps into your aggregation pipeline:
{
$unwind: "$Here"
},
{
$replaceWith: "$Here"
}
MongoPlayground
Note: You can replace $project: { _id: 1, email: 1, ... to this:
{
$addFields:{
age: {
$divide: [{ $subtract: [new Date(), "$birthday"] },(31558464000)]
}
}
}

MongoDB $lookup on one document's array of object

I have searched online but could not find any match my case. Here is the situation.
I am using aggregate to combine one collection and one document which is from another collection together
restaurants.aggregate([
{
$match: {
_id: {
$in: idList
}
}
},
{
$lookup: {
from: "tags",
localField: "details.restaurantType",
foreignField: "details.restaurantType._id",
as: "types"
}
},
{
$project: {
restaurantName: "$details.restaurantName",
restaurantType: "$details.restaurantType",
type: {
$filter: {
input: "$types",
as: "type",
cond: {
$eq: ["$$type._id", "$details.restaurantType"]
}
}
},
currency: "$details.currency",
costPerPax: "$details.costPerPax"
}
}
]);
current result
The 'type' field in my current result is [], I need a matched value instead
[
{
"id": "5c20c7a0036dda80a8baabcc",
"restaurantName": "Villagio Restaurant Sutera Mall",
"type": [],
"currency": "RM",
"costPerPax": 22,
},
{
"id": "5c20ceb07715216d3c217b7a",
"restaurantName": "Thai Food Thai Now Sutera Mall",
"type": [],
"currency": "RM",
"costPerPax": 16,
}
]
expected result
I need the 'type' fields has match tag name from another collection like this
[
{
"id": "5c20c7a0036dda80a8baabcc",
"restaurantName": "Villagio Restaurant Sutera Mall",
"type": "Western",
"currency": "RM",
"costPerPax": 22,
},
{
"id": "5c20ceb07715216d3c217b7a",
"restaurantName": "Thai Food Thai Now Sutera Mall",
"type": "Thai",
"currency": "RM",
"costPerPax": 16,
}
]
Extra Information
two document from restaurants collection
{
"details": {
"restaurantName": "Villagio Restaurant Sutera Mall",
"restaurantType": "5c01fb57497a896d50f498a8"
},
"_id": "5c20c7a0036dda80a8baabcc",
"status": "OP",
"__v": 0
},
{
"details": {
"restaurantName": "Kingshahi Japanese Shop",
"restaurantType": "5c01fb57497a896d50f49879"
},
"_id": "5c20cb4fb7e75180480690c2",
"status": "OP",
"__v": 0
}
One document from tag collection
{
"_id": "5c01fb57497a896d50f49876",
"details": {
"restaurantTypeId": "5c01fb57497a896d50f49877",
"restaurantTypes": [
{
"_id": "5c01fb57497a896d50f49879",
"name": "Asian",
"counter": 1
},
{
"_id": "5c01fb57497a896d50f4987a",
"name": "Bakery",
"counter": 0
}
]
}
}
You can use below optimised aggregation pipeline
db.restaurants.aggregate([
{ "$lookup": {
"from": "tags",
"let": { "restaurantType": "$details.restaurantType" },
"pipeline": [
{ "$match": {
"$expr": { "$in": ["$$restaurantType", "$details.restaurantTypes._id"] }
}},
{ "$unwind": "$details.restaurantTypes" },
{ "$match": {
"$expr": { "$eq": ["$details.restaurantTypes._id", "$$restaurantType"] }
}}
],
"as": "types"
}},
{ "$project": {
"restaurantName": "$details.restaurantName",
"restaurantType": "$details.restaurantType",
"type": { "$arrayElemAt": ["$types.details.restaurantTypes.name", 0] },
"currency": "$details.currency",
"costPerPax": "$details.costPerPax"
}}
])

How can i join 3 collection with aggregation?

Here is my structure
country
[
{
"studentid": [
"5bb482fdac231f1354c47aab"
],
"_id": "5bb672fb92c98117d004c733",
"name": "china",
"__v": 0
}
]
users
[
{
"books": [
"5bb657ea05bf6d0ef096e529",
"5bb6580d05bf6d0ef096e52a"
],
"_id": "5bb482fdac231f1354c47aab",
"firstName": "will",
"lastName": "smith",
"email": "user3#gmail.com",
"address": "rajkot",
"salary": 5000,
"department": "electrical",
"__v": 0
}
]
books
[
{
"_id": "5bb657ea05bf6d0ef096e529",
"name": "book 1",
"price": 100,
"pages": 200,
"__v": 0
}
]
Now i am trying to join this 3 collections each array wise
Here is code that i tried
CountryModel.aggregate([
{ $match: { name: country } },
{
$lookup: {
from: UserModel.collection.name,
let: { student_id: "_id" },
pipeline: [
{ "$match": { "_id": "$student_id" } },
{
$lookup: {
from: UserModel.collection.name,
let: { book_id: "$books" },
pipeline: [
{
$lookup: {
from: BookModel.collection.name,
let: { bookid: "$_id" },
pipeline: [
{ "$match": { "$expr": { "$in": ["$book_id", "$bookid"] } } },
],
as: "more"
}
}
],
as: "books"
}
}
],
as: "studentid"
},
},
])
can someone tell me were i am doing wrong ?

Mongoose Aggregate with Lookup

I have a simple two collections like below :
assignments:
[
{
"_id": "593eff62630a1c35781fa325",
"topic_id": 301,
"user_id": "59385ef6d2d80c00d9bdef97"
},
{
"_id": "593eff62630a1c35781fa326",
"topic_id": 301,
"user_id": "59385ef6d2d80c00d9bdef97"
}
]
and users collection:
[
{
"_id": "59385ef6d2d80c00d9bdef97",
"name": "XX"
},
{
"_id": "59385b547e8918009444a3ac",
"name": "YY"
}
]
and my intent is, an aggregate query by user_id on assignment collection, and also I would like to include user.name in that group collection. I tried below:
Assignment.aggregate([{
$match: {
"topic_id": "301"
}
},
{
$group: {
_id: "$user_id",
count: {
$sum: 1
}
}
},
{
$lookup: {
"from": "kullanicilar",
"localField": "user_id",
"foreignField": "_id",
"as": "user"
}
},
{
$project: {
"user": "$user",
"count": "$count",
"_id": "$_id"
}
},
But the problem is that user array is always blank.
[ { _id: '59385ef6d2d80c00d9bdef97', count: 1000, user: [] } ]
I want something like :
[ { _id: '59385ef6d2d80c00d9bdef97', count: 1000, user: [_id:"59385ef6d2d80c00d9bdef97",name:"XX"] } ]

Resources