How can I query on a referenced field in MongoDB? - node.js

I have two collections, users and posts. The relevant parts of a typical document looks like this:
user
{
"_id": "user1",
"name": "Joe",
"age": 20
}
posts
{
"content": "Yo what's up!",
"created": "2018-02-05T05:00:00.000Z",
"author": "user1"
}
I would like to create a query on the posts collection that returns the following:
{
"content": "Yo what's up!",
"created": "2018-02-05T05:00:00.000Z",
"author": {
"name": "Joe",
"age": 20
}
Is there any way to do this in raw MongoDB?
I'm using the MongoDB Node.js client.

Using aggregation with lookup operator.
db.posts.aggregate([
{"$lookup":{
"from":"users",
"localField":"author",
"foreignField":"_id",
"as":"author"
}},
{"$addFields":{
"author": {"$arrayElemAt":["$author",0]}
}}])

db.posts.aggregate(
// Pipeline
[
// Stage 1
{
$lookup: {
"from": "user",
"localField": "author",
"foreignField": "_id",
"as": "authorobj"
}
},
// Stage 2
{
$project: {
content: 1,
created: 1,
author: {
'name': {
"$arrayElemAt": ["$authorobj.name", 0]
},
'age': {
"$arrayElemAt": ["$authorobj.age", 0]
}
},
}
},
]
);

Related

how to join two documents using $lookup mongodb

I have been trying to join two collection in MongoDb using the aggregate function but it seems it's not working for me, When I run api using lookup it show the me empty collection [],
I have tried with the following.
db.student.aggregate([
{
"$match": {
_id: "63c4c245188267e988d690e2"
},
},
{
"$lookup": {
"from": "wall",
"localField": "_user",
"foreignField": "_id",
"as": "wallpost"
}
}
])
Result:
Here is the result i m getting after lookup :(
[
{
"_id": "63c4c245188267e988d690e2",
"hereFor": [
{
"mainTag": "study",
"subtag": [
"studybuddy",
"findtutor"
]
}
],
"lastName": "Kumar",
"name": "Kamal",
"profilePicture": [
"https://airustudentimages.s3.ca-central-1.amazonaws.com/1673588594404-ba7777ef676f439f86aa612e8be67fd9"
],
"wallpost": []
}
]
Collections
Collection i m using in the query.
Student
student: [
{
"_id": "63c4c245188267e988d690e2",
"name": "Kamal",
"lastName": "Kumar",
"profilePicture": [
"https://airustudentimages.s3.ca-central-1.amazonaws.com/1673588594404-ba7777ef676f439f86aa612e8be67fd9"
],
"hereFor": [
{
"mainTag": "study",
"subtag": [
"studybuddy",
"findtutor"
]
}
],
},
{
"_id": "63c3965c201a1d738ab6867e",
"name": "Suraksha",
"lastName": "Singh",
"profilePicture": [
"https://airustudentimages.s3.ca-central-1.amazonaws.com/1673762449670-a8bdf9b9b0faf3ad84e0a5bc76e32fb8"
],
"hereFor": [
{
"mainTag": "study",
"subtag": [
"studybuddy",
"findtutor"
]
}
],
}
],
Wall
"wall": [
{
"_id": "63c4c92a188267e988d690e3",
"_user": "63c3965c201a1d738ab6867e",
"isSponsered": false,
"description": "Hello test case ",
"tag": {
"mainTag": "hostels"
},
"createdAt": "1673766717308",
"updatedAt": "1673766717308",
},
{
"_id": "63c4cc2b188267e988d690e5",
"_user": "63c3965c201a1d738ab6867e",
"isSponsered": false,
"description": "Hello test case 22 ",
"tag": {
"mainTag": "hostels"
},
"createdAt": "1673766717308",
"updatedAt": "1673766717308",
},
],
Have a look at https://mongoplayground.net/p/2moDXi3lygL
Your query is on Student collection. So the localField should be _id and and the foreignField should be _user (from wall collection).
Then it works fine.
db.student.aggregate([
{
"$match": {
_id: "63c3965c201a1d738ab6867e"
},
},
{
"$lookup": {
"from": "wall",
"localField": "_id",
"foreignField": "_user",
"as": "wallpost"
}
}
])
https://mongoplayground.net/p/r1JeQIlM7AA
You mix up the localField and foreignField.
From Equality Match with a Single Join Condition:
{
$lookup:
{
from: <collection to join>,
localField: <field from the input documents>,
foreignField: <field from the documents of the "from" collection>,
as: <output array field>
}
}
It should be:
{
"$lookup": {
"from": "wall",
"localField": "_id",
"foreignField": "_user",
"as": "wallpost"
}
}
Demo # Mongo Playgound

MongoDB filter documents if they have associated children

Let's say we have two collections, users and posts:
{
"users": [
{
"_id": 1,
"name": "John"
},
{
"_id": 2,
"name": "Jack"
},
{
"_id": 3,
"name": "Mike"
}
],
"posts": [
{
"_id": 1,
"text": "Post 1",
"userId": 1
},
{
"_id": 2,
"text": "Post 2",
"userId": 1
},
{
"_id": 3,
"text": "Post 3",
"userId": 2
}
]
}
Now, the question is as follows:
How can I write a query to filter out users (using aggregation pipeline or something else) depending on whether they have posts? For example, my query should only return an array of users with _id 1 and 2, not 3. Because, there are no posts with userId: 3;
$lookup join posts collection
$match is return post is not empty
$project to remove post field if its not needed
db.users.aggregate([
{
"$lookup": {
"from": "posts",
"localField": "_id",
"foreignField": "userId",
"as": "post"
}
},
{ $match: { post: { $ne: [] } } },
{ $project: { post: 0 } }
])
Playground

MongoDB aggregate and get mismatched results

Hi everyone i have 2 collections named "members" and "offers" in mongoDB. When a member send an offer to another, my web service saves it to offers collection.
"members" collection is like:
[
{
"_id": "5ee00pp0ebfd4432145233344",
"Fname": "John",
"Lname": "Lastname",
"Email": "JohnLastName#gmail.com",
},
{
"_id": "yyyy44p0ebfd4432145233355",
"Fname": "Ashley",
"Lname": "Lastname",
"Email": "AshleyLastName#gmail.com",
},
{
"_id": "yyyy44p0ebfd4432145233355",
"Fname": "Sue",
"Lname": "Lastname",
"Email": "SueLastName#gmail.com",
}
]
when John send an offer to Ashley
"offers" collection is like:
[
{
"_id": "5eea6e62881835271415fd25",
"OfferMail": "JohnLastName#gmail.com",
"Email": "AshleyLastName#gmail.com",
}
]
Now my question is: How can i get all members except Ashley?
Use the $not operator. For example
db.members.find({"Email" : {"$not" : "AshleyLastName#gmail.com"}})
db.members.aggregate([
{
"$lookup": {
"from": "offers",
"localField": "Email",
"foreignField": "Email",
"as": "docuentInB"
}
},
{
$match: {
$expr: { $eq: [{ $size: "$docuentInB" }, 0 ] }
}
},
{
$project: {
docuentInB: 0
}
}
])````

Joining two collections in mongodb

I have separate collections for 'comments','products' and 'users'. The 'comments' collection contains text, product_id and user_id. When a product is fetched I want the details of product along with details of the user in the result.
I have created schema using mongoose odm. I am using aggregate function to populate the product with comments using $lookup.
Product.aggregate([
{
$match:{
_id:mongoose.Types.ObjectId(id)
}
},
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "product",
as: "comments"
}
},
{
$match:{
"comments.product":mongoose.Types.ObjectId(id)
}
},
{
$lookup: {
from: "users",
localField: "comments.user._id",
foreignField: "user",
as: "comments.user"
}
}
])
expected result is
[
{
"_id": "5cc9441feed4c258881c99cd",
"title": "Batman",
"imageUrl": "images\\1556694047310_Batman.jpg",
"price": 555,
"description": "The dark knight",
"user": "5cbca36d4acc5d538c209014",
"__v": 2,
"comments": [
{
"_id": "5cc947125c69600d58c1be05",
"date": "2019-05-01T07:12:42.229Z",
"text": "This product is very nice",
"user":{
"_id": "5cbca36d4acc5d538c209014",
"name": "Clark Kent"
}
},
{
"_id": "5cc96eb4b2834d43f8a24470",
"date": "2019-05-01T09:46:34.774Z",
"text": "Anyone can be Batman",
"user":{
"_id": "5cbca5504acc5d538c209015",
"name": "Bruce Wayne"
},
}
}
]
actual result is
[
{
"_id": "5cc9441feed4c258881c99cd",
"title": "Batman",
"imageUrl": "images\\1556694047310_Batman.jpg",
"price": 555,
"description": "The dark knight",
"user": "5cbca36d4acc5d538c209014",
"__v": 2,
"comments": {
"user": [
{
"_id": "5cbca5504acc5d538c209015",
"name": "Bruce Wayne",
"email": "batman#gotham.com",
"password": "$2a$12$L.t/nBXq/xlic25Y0a884uGxjlimuNH/tcmWLg.sNkcjJ/C40Q14m",
"contactNumber": 9999999999,
"address": "Somewhere in Gotham",
"__v": 0
},
{
"_id": "5cbca7334acc5d538c209016",
"name": "Superman",
"email": "superman#metro.com",
"password": "$2a$12$mrogzC1Am86b0DnvTzosm.qfu38Ue7RqSNcnVSoCR55PtmLddeZv.",
"contactNumber": 9999999999,
"address": "Somewhere in metropolis",
"__v": 0
},
{
"_id": "5cbca7e54acc5d538c209017",
"name": "Wonder Woman",
"email": "ww#amazon.com",
"password": "$2a$12$Vt9XZUyOTULvel5zNAsMLeoMi3HlaGJJZN7OH2XkWuoAiZtDIGaMq",
"contactNumber": 9999999999,
"address": "Somewhere in Amazon",
"__v": 0
},
{
"_id": "5cbe192934ae2944c8704a5a",
"name": "Barry Allen",
"email": "barry#flash.com",
"password": "$2a$12$k73Wp1HTMv/MhUV3BOok3OSh.nnLq3vWG1Qz9ZTO7iB7saFlxhLjW",
"contactNumber": 9999999999,
"address": "Somewhere in Central City",
"__v": 0
}
]
}
}
]
Your $lookup query of users is overwriting the comments array. Its not working as you think it'll.
You need to unwind the comments array and then run that $lookup of users and then group by the products.
Edit: I have updated the query with $group by code too. Also you can playa around with the query here:
https://mongoplayground.net/p/2EA-Glz8Hrm
Product.aggregate([
{
$match: {
_id: "5cc9441feed4c258881c99cd"
}
},
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "product",
as: "comments"
}
},
{
$unwind: "$comments"
},
{
$lookup: {
from: "users",
localField: "comments.user",
foreignField: "_id",
as: "comments.user"
}
},
{
$unwind: "$comments.user"
},
{
$group: {
_id: "$_id",
// add other fields you want to include
comments: {
$addToSet: "$comments"
}
}
},
])
As suggested by Hamza, I made following changes to my query
Product.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(id)
}
},
{
$lookup: {
from: "comments",
localField: "_id", //field from input document
foreignField: "product", // field from documents of the 'from' collection
as: "comments"
}
},
{
$unwind: "$comments"
},
{
$lookup: {
from: "users",
localField: "comments.user", //field from input document
foreignField: "_id", // field from documents of the 'from' collection
as: "comments.user"
}
},
{
$unwind: "$comments.user"
},
{
$group: {
_id: "$_id",
title: { $first: "$title" }, // $first returns the first expression of the document it encounters, ex. first title
price: { $first: "$price" },
imageUrl: { $first: "$imageUrl" },
description: { $first: "$description" },
rating: { $first: "$rating" },
comments: {
$addToSet: "$comments" // group comments and create an array
}
}
},
{
$project: {
_id: 1,
title: 1,
price: 1,
imageUrl: 1,
description: 1,
rating: 1,
comments: {
_id: 1,
text: 1,
date: 1,
user: {
_id: 1,
name: 1
}
}
}
}
])
With this I got the desired result.

How to use $lookup on embedded document field in mogodb

Please have a look, your help will be appriciated
var user = new Schema({
name: String,
});
var Comments = new Schema({
title : String
, body : String
,user_id : {type: Schema.Types.ObjectId, ref: 'user' }
, date : Date
});
var blog = new Schema({
author : String
, title : String
, body : String
, date : Date
, user_id :{type: Schema.Types.ObjectId, ref: 'user' }
, comments : [Comments]
});
db.blogs.aggregate([
{ $match : { "_id" : ObjectId("57e3b7f4409d80a508d52769") } },
{ $lookup: {from: "users", localField: "user_id", foreignField: "_id", as: "User"} },
])
this returns
[
{
"_id": "57e3b7f4409d80a508d52769",
"author": "Tariq",
"title": "MyfirstPost",
"body": "This is my first post",
"user_id": "57e3b763f7bc810c08f9467a",
"comments": [
{
"title": "hi",
"body": "again i am commenting on this",
"user_id": "57e3b763f7bc810c08f9467a",
"_id": "57e3c153409d80a508d5276b"
},
{
"title": "hi",
"body": "this is seond comment",
"user_id": "57e3b763f7bc810c08f9467a",
"_id": "57e3c8632ebca0ee0afb2ac6"
}
],
"__v": 0,
"User": [
{
"_id": "57e3b763f7bc810c08f9467a",
"name": "Tariq",
"username": "teekay",
"password": "123456",
"__v": 0
}
]
}
]
this return result by comparing blog table is and user table _id which is fine .. but I want to get userdetail with each comment by using user_id of “comments.user_id” blog collection and “_id” of collection
should be something like this
"_id": "57e3b7f4409d80a508d52769",
"author": "Tariq",
"title": "MyfirstPost",
"body": "This is my first post",
"user_id": "57e3b763f7bc810c08f9467a",
"comments": [
{
"title": "hi",
"body": "again i am commenting on this",
"user_id": "57e3b763f7bc810c08f9467a",
"_id": "57e3c153409d80a508d5276b",
"User": [
{
"_id": "57e3b763f7bc810c08f9467a",
"name": "Tariq",
"username": "teekay",
"password": "123456",
"__v": 0
}
]
},
You can run an aggregation operation of the pipeline:
db.blogs.aggregate([
{ "$unwind": "$comments" },
{
"$lookup": {
"from": "users",
"localField": "comments.user_id",
"foreignField": "_id",
"as": "comments.user"
}
},
{ "$unwind": "$comments.user" },
{
"$group": {
"_id": "$_id",
"author": { "$first": "$author" },
"title": { "$first": "$title" },
"body": { "$first": "$body" },
"comments": { "$push": "$comments" },
"user_id": { "$first": "$user_id" }
}
},
{
"$lookup": {
"from": "users",
"localField": "user_id",
"foreignField": "_id",
"as": "user"
}
},
{ "$unwind": "$user" },
])

Resources