Mongodb 2 level lookup - node.js

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)})

Related

How to find sum in Mongo aggregate method with match in different collection in an array of object?

I am trying to find the sum by matching array of object using mongoose. I have 2 collection such as
const accountSchema = new mongoose.Schema({
groupId: {
type: Number,
required: true
},
account_no: {
type: String,
required: true
},
account_name: {
type: String,
required: true
},
opening_balance: {
type: Number,
default: 0
}
})
And second collection as:
const mongoose = require('mongoose')
const AutoIncrement = require('mongoose-sequence')(mongoose);
const accountJournalSchema = new mongoose.Schema({
journal_no: {
type: Number
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'Users',
required: [true, 'User ID is required.'],
},
groupId: {
type: Number,
required: true
},
date: {
type: Date,
required: true
},
receipt: [
{
account_no: {
type: mongoose.Schema.ObjectId,
ref: 'Accounts',
required: true
},
debit: {
type: Number,
default: 0
},
credit: {
type: Number,
default: 0
},
}
]
})
And my aggregate method is:
await Accounts.aggregate([
{
$match: {
$and: [
{ groupId: {$eq: parseInt(req.params.group_id)} },
{ 'Account_jour.groupId': { $eq: parseInt(req.params.group_id) } }
]
}
},
{ unwind: '$Account_jour' },
{
$lookup: {
from : 'account_journals',
localField: '_id',
foreignField: 'receipt.account_no',
as: 'Account_jour'
}
}
])
I am getting error from the above statement:
Arguments must be aggregate pipeline operators
And after solving the issue I also want to find the sum of debit and credit.
Thank you!!
Try this:
await Accounts.aggregate([
{
$match: {
$and: [{ groupId: { $eq: parseInt(req.params.group_id) } }, { "Account_jour.groupId": { $eq: parseInt(req.params.group_id) } }]
}
},
{ $unwind: '$Account_jour' },
{
$lookup: {
from : 'account_journals',
localField: '_id',
foreignField: 'receipt.account_no',
as: 'Account_jour'
}
}
])

Nodejs mongoose get results from multiple collections in one query

I'm very new to Nodejs and MongoDB, I have 3 collections one for chapter,one for lecture and one for asset
every course have chapters, every chapter has array of lectures and every lecture have assets
so I want to get chapters by courseId which already done, and inside chapter to get its lectures and in every lecture to get its assets
Course:
const mongoose = require('mongoose');
var Double = require('#mongoosejs/double');
const courseSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
requirements: {
type: String,
},
code: {
type: String,
required: true,
},
coverPhoto: {
type: String,
required: false,
},
description: {
type: String
},
instructor:{
type:mongoose.Schema.Types.ObjectId,
ref:'User',
required:true
},
category:{
type:mongoose.Schema.Types.ObjectId,
ref:'Category',
required:true
},
learns: [{
type: String
}],
subCategory:{
type:mongoose.Schema.Types.ObjectId,
ref:'SubCategory',
required:true
},
isCoaching: {
type: Boolean,
default: false,
},
isFree: {
type: Boolean,
default: false,
},
price: {
type: Double,
default: 0,
},
rating: {
type: Double,
default: 0,
},
isPublished: {
type: Boolean,
default: false,
},
dateCreated: {
type:Date,
default:Date.now,
},
});
exports.Course = mongoose.model('Course', courseSchema);
exports.courseSchema = courseSchema;
Chapter:
const mongoose = require('mongoose');
const chapterSchema = new mongoose.Schema({
course:{
type:mongoose.Schema.Types.ObjectId,
ref:'Course',
required:true
},
title: {
type: String,
required: true,
},
sort_order: {
type: Number,
default: 1,
},
is_published: {
type: Boolean,
default: true,
},
});
exports.Chapter = mongoose.model('Chapter', chapterSchema);
exports.chapterSchema = chapterSchema;
Lecture:
const mongoose = require('mongoose');
const lectureSchema = new mongoose.Schema({
chapter:{
type:mongoose.Schema.Types.ObjectId,
ref:'Chapter',
required:true
},
title: {
type: String,
required: true,
},
sort_order: {
type: Number,
default: 1,
},
is_published: {
type: Boolean,
default: true,
},
});
exports.Lecture = mongoose.model('Lecture', lectureSchema);
exports.lectureSchema = lectureSchema;
Asset:
const mongoose = require('mongoose');
const assetSchema = new mongoose.Schema({
lecture:{
type:mongoose.Schema.Types.ObjectId,
ref:'Lecture',
required:true
},
title: {
type: String,
required:true
},
asset_type: {
type: String
},
description: {
type: String,
require:true
},
file_url: {
type: String,
require:true
},
page_number: {
type: Number,
default:1
},
time_estimation: {
type: String,
require:true
},
is_external: {
type: Boolean,
default: false,
},
is_published: {
type: Boolean,
default: true,
},
});
exports.Asset = mongoose.model('Asset', assetSchema);
exports.assetSchema = assetSchema;
Get Chapters of a course
router.get(`/`, async (req, res) => {
let course_filter = {};
if (req.query.course) {
course_filter = {course:req.query.course};
}
const chapterList = await Chapter.find(course_filter).populate('lecture').sort('sort_order');
if (!chapterList) {
res.status(500).json({ success: false });
}
res.send(chapterList);
});
using simple aggregation:
const chapterList = await Chapter.aggregate([{
$lookup: {
from: 'lectures',
localField: '_id',
foreignField: 'chapter',
as: 'lectures'
}}]);
You have to nest the populate in another populate:
router.get(`/`, async (req, res) => {
let course_filter = {};
if (req.query.course) {
course_filter = { course: req.query.course };
}
const chapterList = await Chapter.find(course_filter)
.populate({ path: 'lectures', populate: { path: 'assets' } })
.sort('sort_order');
if (!chapterList) {
res.status(500).json({ success: false });
}
res.send(chapterList);
});
You have to ensure that you have set a virtual for 'assets' prop in lectureSchema accordingly. I assume you have also done it for your 'Chapter' schema.
If not, you have do add the following:
virtual for Chapter schema:
chapterSchema.virtual('lectures', {
ref: Lecture.collection.collectionName,
localField: '_id',
foreignField: 'chapter'
});
virtual for Lecture schema:
lectureSchema.virtual('assets', {
ref: Asset.collection.collectionName,
localField: '_id',
foreignField: 'lecture'
});

Display data from another collection in MongoDB

I have two separate collection in MongoDB 1-> Post, 2-> Comment.
Post-schema:
const postSchema = new mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
media: {
type: [mongoose.Schema.Types.Mixed],
trim: true,
required: true,
},
text: String,
mentions: {
type: Array,
default: [],
},
hashTags: {
type: ["String"],
},
likes: {
type: Array,
default: [],
},
postStatus: {
type: "String",
default: "public",
enum: ["public", "private"],
},
deletedAt: {
type: "Date",
default: null,
},
},
{ timestamps: true }
);
comment schema:
const commentSchema = new mongoose.Schema(
{
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
postId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Post",
required: true,
},
commentText: String,
},
{ timestamps: true }
);
Now I want to display all the comments of requested post. Post table does not have anything that links to Comment that is why I can not use populate(). but Comment table have postId to connect.
Here's what I have tried:
exports.getPostById = async (req, res) => {
try {
let post = await Post.findById(req.params.id);
if (!post) return res.status(404).json({ message: "No Post found" });
let comment = await Comment.find({ postId: { $in: {post: req.params.id} } });//Wrong query
return res.status(200).send(post);
} catch (error) {
res.status(500).json({ message: error.message });
}
};
Use lookup in an aggregation query.
please refer to this link.
https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/
example:
db.post.aggregate([{
$lookup: {
from: ‘comment’,
localField: ‘user_id’,
foreignField: ‘user_id’,
as: ‘whatever_name_you_want’
}}]);
Try this:
Transform the id string from params to a ObjectId:
let postObjectId = mongoose.Types.ObjectId(req.params.id);
Query using the variable defined above:
let comments = await Comment.find({ postId: postObjectId});

Mongoose find problem - TypeError: Cannot read property 'find' of undefined

I'm having a issue with a mongoose find() query, which I cannot figure out. the error I receive is "TypeError: Cannot read property 'find' of undefined" which I suspect is an export/import problem. Any help would be greatly appreciated.
here is my scheme model file:
const mongoose = require('mongoose');
const RoleSchema = new mongoose.Schema({
pageGroup: {
type: String,
required: true,
},
level: {
type: String,
required: true,
}
})
const OfficeSchema = new mongoose.Schema({
officeId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Office",
required: true,
},
roleId: {
type: [mongoose.Schema.Types.ObjectId],
required: false,
},
})
const InstanceSchema = new mongoose.Schema({
instanceId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Instance",
required: true,
},
offices: {
type: [OfficeSchema],
required: false,
},
})
const UserSchema = new mongoose.Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
},
email: {
type: String,
required: false
},
password: {
type: String,
required: false
},
access: {
type: [InstanceSchema],
required: false,
},
permissions: {
type: [RoleSchema],
required: false,
},
activationToken: {
type: String,
required: false,
},
roleId: { // new
type: mongoose.Schema.Types.ObjectId,
// index: true,
ref: 'Role',
// default: null
},
employeeId: {
type: String,
required: false
},
instanceId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Instance',
required: true
},
officeId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Office',
required: true
},
},
{
toJSON: { virtuals: true },
toObject: { virtuals: true },
})
UserSchema.virtual('instances', {
ref: 'Instance',
localField: 'access.instanceId',
foreignField: '_id',
});
UserSchema.virtual('instances.offices', {
ref: 'Office',
localField: 'access.offices.officeId',
foreignField: '_id',
});
UserSchema.virtual('office', {
ref: 'Office',
localField: 'officeId',
foreignField: '_id',
justOne: true,
});
UserSchema.virtual('name').get(function() {
return this.firstName + " " + this.lastName
});
const User = mongoose.model('User', UserSchema);
module.exports = { User }
here is my function in my controller file:
const { User } = require('./user.model');
async getEmployees(){
const employees = await User.find({
instanceId: this._id,
}, '-password -activationToken -__v -activated')
.populate('office')
.sort([['firstName', 1]])
.exec()
return employees
},
The error points to User being undefined, which can happen when your project has cyclic dependencies (where file A.js depends on file B.js, which in turn depends on file A.js again, either directly or indirectly through another file).
A quick fix is to delay loading the User model until the moment it's actually needed, by moving the require() into getEmployees():
async getEmployees(){
const { User } = require('./user.model');
const employees = await User.find({
instanceId: this._id,
}, '-password -activationToken -__v -activated')
.populate('office')
.sort([['firstName', 1]])
.exec()
return employees
}
But ideally, you should get rid of the cyclic dependency altogether.
I had the same problem on my project. You can fix it by replacing
const { User } = require('./user.model');
of your controller by :
const User = require('./user.model');
Just by removing the brackets made it for me. So I guess you should use destructuring.

Mongo populate not working and not retrieve data model

I have a problem in populate method in mongodb it can't retrieve data from model. Can anyone help me solve that problem?
This is the code
router.get('/', auth, async (req, res) => {
try {
const user = req.user._id;
const wishlist = await Wishlist.find({ user, isLiked: true })
.populate({
path: 'Products',
select: 'title',
})
.sort('-updated');
res.status(200).json({
wishlist,
});
} catch (error) {
res.status(400).json({
error: 'Your request could not be processed. Please try again.',
});
}
});
When I navigate to http://localhost:3000/wishlist/, this is the response I get:
{
"wishlist": [
{
"product": "60cb5eb82cc7091ae2e31c88",
"user": "60cb6c46291247466fe08f92",
"isLiked": true,
"_id": "60d1a656567e08bf89571209",
"updated": "2021-06-22T10:09:25.295Z",
"created": "2021-06-22T08:59:02.434Z",
"__v": 0
}
]
}
The model of products
const mongoose = require('mongoose');
const { Schema } = mongoose;
const ProductSchema = mongoose.Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
category:{
type: Schema.Types.ObjectId,
ref: 'Categories',
default: null
},
photo: { type: String, required: true },
createdAt: {
type: Date,
default: new Date(),
},
updateAt: Date,
price: {
type: String,
required: true,
},
quantity: {
type: Number
}
,
isActive: {
type: Boolean,
default: true
},
user: {
type: Schema.Types.ObjectId,
ref: "User",
}
});
module.exports = mongoose.model('Product', ProductSchema);
The model for wishlist:
const Mongoose = require('mongoose');
const { Schema } = Mongoose;
// Wishlist Schema
const WishlistSchema = new Schema({
product: {
type: Schema.Types.ObjectId,
ref: 'Product',
default: null,
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
default: null,
},
isLiked: {
type: Boolean,
},
updated: {
type: Date,
default: Date.now,
},
created: {
type: Date,
default: Date.now,
},
});
module.exports = Mongoose.model('Wishlist', WishlistSchema);
Can anyone help me please to find the solution for that problem?

Resources