mongodb join on array elements - database

I have 1 collection user which looks as follows
{
_id: ObjectId("70c7ad56ea3ef6002f76df7b"),
username: "Joe",
following: [ ObjectId("50c7ad56ea3ef6002f76df7b"), ObjectId("40c7ad56ea3ef6002f76df7b") ]
}
the following object contains _ids of other users from the user collection. I would like to have a query which gives me the username of each of the users in the following array for any selected user and get the followin results
{
_id: ObjectId("70c7ad56ea3ef6002f76df7b"),
username: "Joe",
following: [
{ ObjectId("50c7ad56ea3ef6002f76df7b"), username: "Bill" },
{ ObjectId("40c7ad56ea3ef6002f76df7b"), username: "Ann"}
]
}

use lookup and then map result to create your final result
db.user.aggregate([
{
"$lookup": {
from: "user",
"localField": "following",
"foreignField": "_id",
"as": "following"
}
},
{
"$project": {
_id: 1,
following: {
"$map": {
"input": "$following",
"as": "z",
"in": {
_id: "$$z._id",
username: "$$z.username"
}
}
}
}
}
])
example :https://mongoplayground.net/p/pLgvZenAG84
you could add $match to find specific user

Related

Retrieving related elements from MongoDB

I have the following data in MongoDB. Based alone on an id that I have available how can I retrieve all other entries where the player matches the player for my current id.
For example : find who the player for id 12 is, search all other entries that match that player name and return a list of all of them.
[
{_id: '62ecdf342f1193134043964c', id: '12', player: 'David Beckham', team: 'Manchester United'},
{_id: '62ecdf342f1193134043965c', id: '17', player: 'Cristiano Rolando', team: 'Manchester United'},
{_id: '62ecdf342f1193134043966c', id: '22', player: 'Cristiano Rolando', team: 'Juventus'},
{_id: '62ecdf342f1193134043967c', id: '42', player: 'David Beckham', team: 'Real Madrid'},
]
This is the code that I'm using to retrieve the one single entry that matches a specific id and then I'd also like to get the related entries.
export async function getStaticProps({ params }) {
const { db } = await connectToDatabase();
const jerseyA = await db
.collection("Jerseys")
.find({ id: params.jersey })
.sort()
.toArray();
const jersey = JSON.parse(JSON.stringify(jerseyA))[0];
return { props: { jersey } };
}
Now that you know the name, do another fetch like .find({player: jersey.player})
I'm not sure of the output format you want, but here's one way to return all documents that match the "player" name of the given "id".
db.Jerseys.aggregate([
{
"$match": {
// your id goes here
"id": "17"
}
},
{
"$lookup": {
"from": "Jerseys",
"localField": "player",
"foreignField": "player",
"as": "docs"
}
},
{"$unwind": "$docs"},
{"$replaceWith": "$docs"}
])
Example output:
[
{
"_id": "62ecdf342f1193134043965c",
"id": "17",
"player": "Cristiano Rolando",
"team": "Manchester United"
},
{
"_id": "62ecdf342f1193134043966c",
"id": "22",
"player": "Cristiano Rolando",
"team": "Juventus"
}
]
Try it on mongoplayground.net.
Just an addition to #rickhg12hs solution, to ignore the first record. You can use the following query to ignore the first record (where the id also matched) and the others.
db.Jerseys.aggregate([
{
"$match": {
"id": "12"
}
},
{
"$lookup": {
"from": "Jerseys",
"localField": "player",
"foreignField": "player",
"as": "docs"
}
},
{
"$unwind": "$docs"
},
{
"$replaceWith": "$docs"
},
{
"$match": {
"id": {
"$not": {
"$eq": "12"
}
}
}
}
])
A possible javascript translation of it, should be,
export async function getStaticProps({ params }) {
const { db } = await connectToDatabase();
const { jersey: id } = params;
const jerseyA = await db
.collection("Jerseys")
.aggregate([
{
"$match": {
id
}
},
{
"$lookup": {
"from": "Jerseys",
"localField": "player",
"foreignField": "player",
"as": "docs"
}
},
{
"$unwind": "$docs"
},
{
"$replaceWith": "$docs"
},
{
"$match": {
"id": {
"$not": {
"$eq": id
}
}
}
}
]).toArray();
const jersey = JSON.parse(JSON.stringify(jerseyA))[0];
return { props: { jersey } };
}

How to match documents of same collection in a lookup where an array field has the highest number of matching elements?

I have a products collection with a schema as follows:
{
brand: String,
category: String,
title: String,
description: String,
product_variants: [...],
tags: [{label:String},{value:String}],
}
I am performing a lookup in the same collection to add a field consisting of related products for each product. I need to get related products by matching documents that have similar elements in the tags array. So far I have the got the following lookup pipeline:
{
$lookup: {
from: 'products',
let: { "tags": "$tags.label" },
pipeline: [{
$match: {'product_variants.status': {$eq: 'Active'}}
},
{
'$match': {
'$expr': {
'$in': ['$tags.label', '$$tags']
}
},
},
{
$match: { 'product_variants': {
$elemMatch: {
'variant_details': {
$elemMatch: {
'inventory': {
$elemMatch: {
quantity: {$gt:0},
}
}
}
}
}
}}
}, {
$project: {
product_variants: {
"$filter": {
"input": {
"$map": {
"input": "$product_variants",
"as": "variants",
"in": {
"variant_code": "$$variants.variant_code",
"images": {$slice: ["$$variants.images", 1]},
"slug": "$$variants.slug"
}
}
},
"as": "related_products",
"cond":{$gt: [{$size: "$$related_products.images"}, 0]}
}
}
}
}, { $limit: 3 } ],
"as": "related_products"
}
},
I have tried using $all operator but it is not working either.

Get number of followers for specific page in Mongodb

let's say I have a collection called pages as
{
_id: "pageid",
name: "Mongodb"
},
{
_id: "pageid2",
name: "Nodejs"
}
and user collection as follows
{
_id : "userid1",
following: ["pageid"],
...
},
{
_id : "userid2",
following: ["pageid", "pageid2"],
...
}
how could I make a query to retrieve the pages information along with the number of users follow each page in mongodb, expected result as follows
[
{
_id: "pageid",
name: "MongoDB",
followers: 2
},
{
_id: "pageid2",
name: "Nodejs",
followers: 1
},
]
You can use $lookup and $size to count total followers,
db.pages.aggregate([
{
$lookup: {
from: "user",
localField: "_id",
foreignField: "following",
as: "followers"
}
},
{
$addFields: {
followers: { $size: "$followers" }
}
}
])
Playground

How to compare the elements of an array from the same object in MongoDB?

I've a document collection with the below data
[{
_id:ObjectId(),
status:"created",
users:[{
email:"xyz#gmail.com",
role:"admin"
},
{
email:"abc#gmail.com",
role:"admin"
},
{
email:"abc#gmail.com",
role:"user"
}]
},
{
_id:ObjectId(),
status:"created",
users:[{
email:"pqr#gmail.com",
role:"admin"
},
{
email:"abc#gmail.com",
role:"admin"
},
{
email:"pqr#gmail.com",
role:"user"
}]
}]
I want to fetch the count of documents which have the user with both "admin" and "user" roles and the status is "created". Now I'm trying to match the email id and also the role.
db.documents.aggregate([
{"$match":{
"status":"created",
"$expr":{
"$eq":[
{"$size":{"$setIntersection":["$users.email_id","$users.email_id"]}},
0
]
}
}},
])
Is there any way to compare the elements of the array from the same object?
I want to fetch the count of documents which have the user with both
"admin" and "user" roles and the status is "created". Now I'm trying
to match the email id and also the role.
You can use an aggregation or a find query as follows:
var MATCH_EMAIL = "abc#gmail.com"
db.collection.aggregate( [
{
$match: {
$and: [ { status: "created" } , { "users": { $elemMatch: { role: "admin", email: MATCH_EMAIL } } } ],
"users": { $elemMatch: { role: "user", email: MATCH_EMAIL } }
}
},
{
$count: "Count"
}
] )
// -- or --
db.collection.find( {
$and: [ { status: "created" } , { "users": { $elemMatch: { role: "admin", email: MATCH_EMAIL } } } ],
"users": { $elemMatch: { role: "user", email: MATCH_EMAIL } }
} ).count()
Note the query filter is same in both cases. The aggregation returns a result document like { "Count" : 1 }, but the find query returns just the count value, like 1.
[ EDIT ADD ]
Updated code to have the same email for "user" and "admin" riles.
db.collection.aggregate( [
{
$match: {
$and: [ { status: "created" } , { "users.role": "admin" } ],
"users.role": "user"
}
},
{
$unwind: "$users"
},
{
$group: {
_id: { _id: "$_id", email: "$users.email" },
roles: { $push: "$users.role" }
}
},
{
$match: {
$expr: { $gt: [ { $size: "$roles" }, 1 ] }
}
},
{
$count: "Count with user+admin roles and same email"
}
] )

Lookup VS Lookup with pipeline MongoDB (Performace & How it internally works)

I'm making a blog and have an query about which would give me better performace, simple lookup or lookup with pipeline because sometime simple lookup gave me fast result and sometime pipleline lookup. So, I am bit confused now which one to use or where to use. Suppose I have 2 collection, user and comment collection.
// Users Collection
{
_id: "MONGO_OBJECT_ID",
userName: "Web Alchemist"
}
// Comments Collection
{
_id: "MONGO_OBJECT_ID",
userId: "USER_MONGO_OBJECT_ID",
isActive: "YES", // YES or NO
comment: "xyz"
}
Now I want to Lookup from users collection to comments, which one would be better for this. I made two query which giving me same result.
[
{
$match: { _id: ObjectId("5d68c019c7d56410cc33b01a") }
},
{
$lookup: {
from: "comments",
as: "comments",
localField: "_id",
foreignField: "userId"
}
},
{
$unwind: "$comments"
},
{
$match: {
"comments.isActive": "YES"
}
},
{ $limit: 5},
{
_id: 1, userName: 1, comments: { _id: "$comments._id", comment: "$comments.comment"}
},
{
$group: {
_id: "$_id",
userName: { '$first': '$userName' },
comments: { $addToSet: "comments"}
}
}
]
OR
[
{
$match: { _id: ObjectId("5d68c019c7d56410cc33b01a") }
},
{
$lookup: {
from: "comments",
as: "comments",
let: { userId: "$_id" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ['$userId', '$$userId'] },
{ $eq: ['$isActive', 'YES'] }
]
}
}
},
{ limit: 5 },
{
$project: { _id: 1, comment: 1 }
}
]
}
}
]

Resources