Mongo. Return related collections on parent - node.js

Assuming such a mongoose database schema,
const teacherSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
})
const courseSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
teacher: { type: 'ObjectId', ref: 'Teacher', required: true },
})
const studentSchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
course: { type: 'ObjectId', ref: 'Course', required: true },
})
const hobbySchema = new mongoose.Schema({
name: {type: String, required: true, trim: true},
student: { type: 'ObjectId', ref: 'Student', required: true },
})
How can I query and return data from a specific teacher, while nesting courses they teach, students taking the course and their hobbies?
NOTE: I find teacher using url params (id)
{
_id: "61040a6dec6d054128fa8eae",
name: "John Doe",
others: ...,
courses: [
{
content: ...,
students: [
{
content: ....,
hobbies: [{
content:...
}
]
},
{...other students taking the course}
]
},
{...other subjects by John Doe}
]
}

All you need is mongo aggregate to link and obtain data from different schemas.
First, introduce a relationship linking all schemas, just like how you doing with the ref, I suggest have a
usersSchema and introduce a userType field that identifies the type of user either student or teacher, so you can do away with teacherSchema and studentSchema
UserSchema.aggregate([
{
$match: { id: teacherId }
},
{
$lookup: {
from: "users",
localField: "students",
foreignField: "_id",
as: "students",
},
},
{
$lookup: {
from: "hobbies",
localField: "hobbyId",
foreignField: "_id",
as: "hobbies",
},
},
{
$lookup: {
from: "course",
localField: "courseId",
foreignField: "_id",
as: "courses",
},
},
{
$sort: {
createdAt: -1,
},
},
{
$unwind: "$users",
},
])
.then((data) => {
return data
})

Related

Fetch parent records based on child field record field mongodb

I have User Schema
var UserSchema = mongoose.Schema(
{
email: String,
roles: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Roles',
required: true,
},
],
});
Roles Schema
var RolesSchema = mongoose.Schema({
name: {
type: String,
unique: true,
required: [true, 'A Role must have a name'],
},
});
I want to fetch all users which role name includes Admin in one query.
const t = await User.find({
roles: {
$elemMatch: {
name: 'Admin',
},
},
});
This Always returns empty array
Another I tried
const t = await User.aggregate([
{
$lookup: {
from: 'Roles',
localField: 'roles',
foreignField: '_id',
as: 'roles',
},
},
{
$match: { 'roles.name': 'Admin' },
},
]);
This also returns an empty array. I want all users which have role name = Admin

how to filter a query base on a field of object after $lookup in mongoose

I want to get comments of a post-blog sorted by top-like(rate)
Comment Model:
const CommentSchema = new Schema({
content: {type: String, maxLength: 100, required: true},
user: {type: Schema.Types.ObjectId, ref: 'User', required: true },
blog: {type: Schema.Types.ObjectId, ref: 'Blog', required: true},
reactions: [{type: Schema.Types.ObjectId, ref: 'Reaction'}],
},{ timestamps: true})
const ReactionSchema = new Schema({
user: {type: Schema.Types.ObjectId, ref: 'User', required: true},
comment: {type: Schema.Types.ObjectId, ref: 'Comment', required: true},
date: {type: Schema.Types.Date, default: Date.now, required: true}
})
my query:
this.model.Reaction.aggregate([
{
$group: {
_id: "$comment",
num: { $sum: 1 }
}
},
{ $sort: { num: -1 } },
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "_id",
as: "Com",
}
},
{ $unwind: "$Com" },
{
$match: {"Com.blog": '629af177e03ab4cfe845a9a4'}
},
{
$project: {
content: "$Com.content",
num: 1,
}
},
])
but $match after $lookup isn't accept! I try to use "$filter" also, but it get a "array " and a have a Object!
my query work without $match. but a need to filter comments base on blog-id
Can you try this,
this.model.Reaction.aggregate([
{
$group: {
_id: "$comment",
num: { $sum: 1 }
}
},
{ $sort: { num: -1 } },
{
$lookup: {
from: "comments",
localField: "_id",
foreignField: "_id",
as: "Com",
}
},
{ $unwind: "$Com" },
{
$match: {"Com.blog": mongoose.Types.ObjectId('629af177e03ab4cfe845a9a4')}
},
{
$project: {
content: "$Com.content",
num: 1,
}
},
])

Group and populate related schema in mongoose

I have these 2 schemas
const UserSchema = mongoose.Schema({
username: {
type: String,
maxlength: 50,
required: false
},
//other fields...
}
});
module.exports = mongoose.models.User || mongoose.model('User', UserSchema);
const PictureSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
title: {
type: String,
minLength: 3,
maxlength: 80,
required: true
},
category: {
type: String,
required: false
},
// other fields
}, {timestamps: true} );
module.exports.Picture = mongoose.models.Picture || mongoose.model('Picture', PictureSchema);
I am doing an aggregate query
let aggPictures = await Picture.aggregate([
{$sort: {
createdAt: -1
}
},
{$group: {
_id: '$category' ,
pictures: { $push: "$$ROOT" },
}
},
{$project: {
pictures: {
$slice:["$pictures", 1]
}
}}
])
;
That one works but I am strugling to populate the user. What I tried is to add this right after $project
{$lookup: {
from: 'Users',
localField: 'user',
foreignField: 'username',
as: 'name'
}
},
{
$unwind: '$user'
}
still no luck. any suggestion?

how to sort after lookup and project aggregation in mongodb?

I have 2 Collections one for users and other for posts(Posts colllection have _id of users as postedBy).
In users collection each user is having friends array which have _id of users in it.I want to get all the Posts of My friends and mine post in sorted order(sorted By CreatedAt).
This is my Userschema in which i am having friends array of mongoose object type ref to user collection,
here i'm storing users id who is friend.
`//UserSchema
const userSchema = new Schema({
profileImg : {
type: String,
},
name: {
type: String,
required: [true, 'Please Enter Your Name!']
},
about: {
type: String,
},
email: {
type: String,
required: [true, 'Please Enter Email!'],
unique: [true, 'Already Registered!'],
match: [/\S+#\S+\.\S+/, 'is invalid!']
},
password: {
type: String,
required: [true, 'Please Enter Your Password!'],
},
friends: [{
type: mongoose.Types.ObjectId,
ref: 'USER'
}],
address: {
line1: {
type: String,
required: [true, 'Please Enter Your Address!']
},
line2: {
type: String
},
city: {
type: String,
required: [true, 'Please Enter Your City!']
},
state: {
type: String,
required: [true, 'Please Enter Your State!']
},
}
}, { timestamps: true })
This is my Post Schema where userId is ref to users collection and here the _id of user who is uploading post is saved.
//POST SCHEMA
const postSchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "USER",
required: true
},
postImage: {
type: String,
required: [true, 'Please Upload the Image!']
},
caption: {
type: String
},
likes: [likeSchema],
comments: [commentSchema]
}, { timestamps: true })
`
What I am Doing:
1st I am finding the user through _id
2nd from found user's friend array ,lookup in posts collection to get post of friends
3rd Now to get owns post again look up in post collection with own _id
4th concat the both array obtain from friend post and user post as Posts
Now here after step 4 i want to sort the Posts by createdAt but its not working..
How to sort it?
const posts = await User.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(req.user_id)
}
},
{
$lookup: {
from: "posts",
localField: "friends",
foreignField: "userId",
as: "friendposts"
}
},
{
$lookup: {
from: "posts",
localField: "_id",
foreignField: "userId",
as: "userposts"
}
},
{
$project: {
"Posts": {
$concatArrays: ["$friendposts", "$userposts"]
},
_id: 0
}
}
])
you can use 1 lookup instead of 2 .
for sorting you have 3 ways
sort in the code level (using sort function)
use $unwind $sort and group (if mongo db version is less than 5.2)
use $sortArray (applicable for mongodb version 5.2+)
if using 2nd method.
User.aggregate([
{
'$match': {
'_id': mongoose.Types.ObjectId(req.user_id)
}
}, {
'$addFields': {
'users': {
'$concatArrays': [
'$friends', [
mongoose.Types.ObjectId(req.user_id)
]
]
}
}
}, {
'$lookup': {
'from': 'posts',
'localField': 'users',
'foreignField': 'userId',
'as': 'posts'
}
}, {
'$unwind': {
'path': '$posts'
}
}, {
'$sort': {
'posts.createdAt': -1
}
}, {
'$group': {
'_id': '$_id',
'posts': {
'$push': '$posts'
},
'name': {
'$first': '$name'
}
}
}
])
you can add any other field needed in final response like wise i added name .

Mongodb 2 level lookup

I'm a nodejs beginner and cannot debug my problem
I have 3 models.
Cargo model
const mongoose = require("mongoose");
const cargoSchema = mongoose.Schema({
description: { type: String, required: true },
cargoType: { type: String, enum:['irt', 'prt', 'adr', 'pr', 'npr', 'it', 'pt', 'izt', 'hsr', 'tpd', 'zt', 'vt', 'tpv', 'st', 'pv', 'other']},
price: { type: String, required: true },
date: { type: Date },
creator: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true }
});
module.exports = mongoose.model("Cargo", cargoSchema);
Signups model
const mongoose = require("mongoose");
const signupSchema = mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
cargoId: { type: mongoose.Schema.Types.ObjectId, ref: "Cargo", required: true },
truckId: { type: mongoose.Schema.Types.ObjectId, ref: "Truck", required: true },
approved: { type: Boolean ,required: true },
finished: { type: Boolean ,required: true },
});
module.exports = mongoose.model("Signup", signupSchema);
and User model
const mongoose = require("mongoose");
const uniqueValidator = require("mongoose-unique-validator");
const userSchema = mongoose.Schema({
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
isEmailVerified: { type: Boolean },
registrationStep: { type: Number, enum: [0,1,2,3]},
regDate: { type: Date },
companyName: { type: String },
oib: { type: String },
telephone: { type: String },
address: { type: String },
city: { type: String },
countryCode: { type: String },
postalCode: { type: String },
userType: { type: String, enum:['firms','drivers','both']},
approved: { type: Boolean },
isAdmin: { type: Boolean }
});
userSchema.plugin(uniqueValidator);
module.exports = mongoose.model("User", userSchema);
I want to get data from Cargos for a specific user and also get data from Cargo signups and specific user for that signup
This is my controller
exports.getCargosByUserId = (req, res, next) => {
const ObjectId = mongoose.Types.ObjectId;
const cargoQuery = Cargo.aggregate([
{ $match: {creator: ObjectId(req.params.id)}},
{
$lookup: {
from: Load.collection.name,
localField: "_id",
foreignField: "cargoId",
as: "loads"
}
},
{
$lookup: {
from: UnLoad.collection.name,
localField: "_id",
foreignField: "cargoId",
as: "unLoads"
}
},
{
$lookup: {
from: Signup.collection.name,
localField: "_id",
foreignField: "cargoId",
pipeline: [{
$lookup: {
from: User.collection.name,
localField: "_id",
foreignField: "userId",
as: "signupUser"
}
}],
as: "signUps"
}
}
]);
cargoQuery
.then(documents => {
fetchedCargos = documents;
res.status(200).json({
message: "Cargo fetched successfully!",
cargos: fetchedCargos,
});
})
.catch(error => {
res.status(500).json({
message: "Fetching cargo failed!"
});
});
};
Nodejs doesn't print any error, so it's hard to debug, but somewhere I have made a mistake. I'm recieving cath block "Fetching cargo failed". Thank you for your help!
I cant comment you but a good help would be to print the error like this so you know more info about it:
.catch(error => {console.log(error)})

Resources