Mongoose $lookup object returns empty array - node.js

I have tried other similar kind of questions available but nothing seems to work for me.
I have two collections:
leads:
const mongoose = require("mongoose");
const id = mongoose.Schema.Types.ObjectId;
const leadsSchema = mongoose.Schema(
{
_id: id,
userId: { type: id, ref: "User", required: true },
leadName: String,
leads: [
{
_id: id,
name: String,
status: { type: String, required: false, default: "New" },
leadActivity: { type: String, required: false, default: "No Campaign Set" },
headline: { type: String, required: false },
location: { type: String, required: false },
leadType: { type: id, ref: "LeadsCategory", required: true },
}
],
campaignAssociated: {type: id, ref: "campaign"},
},
{
timestamps: true
}
);
module.exports = mongoose.model("lead", leadsSchema);
leadCategory
const mongoose = require("mongoose");
const leadsCategorySchema = mongoose.Schema(
{
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: false,
},
leadsData: [{ type: Array, ref: "lead" }],
},
{ timestamps: true }
);
module.exports = mongoose.model("LeadsCategory", leadsCategorySchema);
I am trying to reference/populate the name of the lead from leadscategory schema into the leads
exports.get_single_lead_info = (req, res) => {
const { userId } = req.user;
const { leadid } = req.body;
let idToSearch = mongoose.Types.ObjectId(leadid);
Lead.aggregate([
{
$lookup: {from: 'leadscategories', localField: 'leadType', foreignField: 'name', as: 'type as'}
},
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
"leads._id": idToSearch,
},
},
])
.exec(function (err, result) {
if (err) {
return res.status(400).json({ message: "Unable to fetch data", err });
}
if (!result.length) {
res.status(404).json("No result found");
} else {
res.status(200).json({ message: "Lead info found", result });
}
});
};
But it outputs me the lookup result as an empty array everytime:
{
"message": "Lead info found",
"result": [
{
"_id": "5ece11cbac50c434dc4b7f2c",
"leadName": "python",
"leads": {
"status": "New",
"leadActivity": "Campaign Set",
"name": "Hailey",
"headline": "Machine Learning | Python",
"location": "New Delhi Area, India",
"_id": "5ece11cbac50c434dc4b7f29",
"leadType": "5ebce0f81947df2fd4eb1060"
},
"userId": "5eba83d37d4f5533581a7d58",
"createdAt": "2020-05-27T07:07:55.231Z",
"updatedAt": "2020-05-27T10:47:42.098Z",
"__v": 0,
"type as": [] //<--- Need lead type name associated inside this
}
]
}
Input: "leadid": "5ece11cbac50c434dc4b7f29"
Any help appreciated.

[
{
$match: {
userId: mongoose.Types.ObjectId(userId),
},
},
{
$unwind: "$leads",
},
{
$match: {
'leads._id': idToSearch,
},
},
{
$lookup: {
from: 'leadscategories',
localField: 'leads.leadType',
foreignField: '_id',
as: 'type as'
}
},
]

Related

MissingSchemaError: Schema hasn't been registered for model images

I keep getting the same error that my imageSchema hasn't been registered for ImageModel when I try to populate the posts from UserModel. But I can't figure out what's the issue. I checked the image Schema file and I don't see anything wrong. Or am I missing something else?
User model
const userSchema = new mongoose.Schema(
{
name: {
type: String,
required: true
},
username: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
min: 6,
max: 30
},
created: {
type: Date,
required: true,
default: Date.now
},
},
{
timestamps: true,
toJSON: {
virtuals: true
}
},
);
userSchema.virtual("posts", {
ref: "ImageModel",
foreignField: 'userId',
localField: '_id'
});
module.exports = mongoose.model('users', userSchema);
Image model
const imageSchema = new mongoose.Schema({
caption: {
type: String,
},
timeCreated: {
type: Date,
default: () => Date.now(),
},
img: {
type: String,
default: 'placeholder.jpg',
},
});
module.exports = mongoose.model("imagesPosts", imageSchema);
model routes
const UserModel = require("../models/User");
const ImageModel = require("../models/Image");
This is the code I'm working on to populate the posts from the User model, but I'm not sure if I'm doing it correctly or not.
const userId = req.user.id;
try {
const result = await UserModel.findById(userId).populate("posts");
console.log("\n\nPopulate result: " + result + "\n\n");
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong, check logs");
}
**Desired output: **
{
"_id": "5e3a885ec511414a3c37a78c",
"username": "Johm",
"email": "john#head.dev",
"password": "123123",
"__v": 0,
"posts": [
{
"_id": "5e3a88e2c511414a3c37a78d",
"caption": "caption one",
"img": "1661309774553spaghetti.jpg",
"userId": "5e3a885ec511414a3c37a78c",
"created": "2020-02-05T09:20:49.754Z",
"__v": 0
},
{
"_id": "5e3a88f1c511414a3c37a78e",
"caption": "caption two",
"img": "1661309774553spaghetti.jpg",
"userId": "5e3a885ec511414a3c37a78c",
"created": "2020-02-05T09:20:49.754Z",
"__v": 0
}
],
}
Declare your posts field as a ref in userSchema:
const userSchema = new mongoose.Schema(
{
posts: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'imagesPosts',
},
],
...
);
You should be able to populate it with:
await UserModel.findById(userId).populate("posts").exec();

Getting items that referenced an object id

// PRODUCT MODEL
const productSchema = new mongoose.Schema(
{
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
name: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
default: 0,
},
colors: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Color",
},
],
sizes: {
type: Array,
default: [
{ id: 1, size: "XS" },
{ id: 2, size: "S" },
{ id: 3, size: "M" },
{ id: 4, size: "L" },
{ id: 5, size: "XL" },
{ id: 6, size: "XXL" },
],
},
},
{ timestamps: true }
);
const Product = mongoose.model("Product", productSchema);
export default Product;
// CATEGORY MODEL
const CategorySchema = new mongoose.Schema(
{
name: String,
},
{ timestamps: true, toJSON: true, toObject: true }
);
CategorySchema.virtual("items").get(async function () {
return await CategorySchema.aggregate([
{
$lookup: {
from: "Product",
localField: "category",
foreignField: "_id",
as: "items",
},
},
]);
});
const Category = mongoose.model("Category", CategorySchema);
export default Category;
Hello, please i'm trying to get list of items that referenced each categoryid on the product model, i want to add the result as a virtual field called "count" from the category model and not the product model. i'm getting this error "TypeError: Cannot use 'in' operator to search for 'transform' in true".
i want to get the result the way it was done in this example from mongodb doc about inventory and orders but i'm getting "items: {}". docs.mongodb.com/manual/reference/operator/aggregation/lookup
router.get(
"/counts",
catchAsync(async (req, res) => {
const stats = await Category.aggregate([
{
$lookup: {
from: "products",
localField: "_id",
foreignField: "category",
as: "count",
},
},
{
$project: {
name: 1,
image: 1,
count: {
$cond: {
if: { $isArray: "$count" },
then: { $size: "$count" },
else: "NA",
},
},
},
},
]);
if (!stats) {
return res.status(404).json("No data found");
}
res.status(200).json(stats);
})
);
For anyone that is trying to achieve the result i wanted, i used Category.aggregate on the category route instead of creating a virtual field from the category model.

Populate field in an array of objects MongoDB and Mongoose

I'm building a comment system for my website. I've created this schema for the article and it's comments:
let articleSchema = mongoose.Schema({
language: {
type: String,
default: "english"
},
slug: {
type: String,
required: true
},
image: {
type: String,
required: true
},
title: {
type: String,
required: true
},
tags: {
type: [String],
required: true
},
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true
},
date: {
type: Date,
default: Date.now()
},
description: {
type: String,
required: false
},
body: {
type: String,
required: true
},
translation: [
{
language: {
type: String,
required: true
},
title: {
type: String,
required: true
},
tags: {
type: [String],
required: true
},
description: {
type: String,
required: false
},
body: {
type: String,
required: true
}
}
],
comments: [{
author: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
date: {
type: Date,
default: Date.now()
},
body: {
type: String,
required: true
}
}]
});
Now what I want to do is to populate every comment author field.
Let's say this is the article with a comment:
{
"comments": [
{
"author": "5f71d3b575f9f800943903af",
"date": "some date here",
"body": "comment body"
}
]
}
Now what I want to get after I populated all of this is this:
{
"comments": [
{
"author": {
"username": "Username",
"avatar": "fox"
},
"date": "some date here",
"body": "comment body"
}
]
}
How can I do that?
Here's my current code:
router.get('/article/:id', async (req, res) => {
const { id: articleId } = req.params;
const lang = req.i18n.language;
const langName = new Intl.DisplayNames(['en'], { type: "language" }).of(lang).toLowerCase();
try {
console.log(langName);
if (req.i18n.language === "en") {
var article = await Article.findById(articleId)
.populate([
{path:'comments.author', select:'+photo +username'},
{path:'author', select:'+photo +username'}
])
.select("title image tags author date description body comments");
} else {
var article = await Article
.aggregate([
{
$match: {
"_id": ObjectId(articleId)
}
}, {
$unwind: "$translation"
}, {
$match: {
"translation.language": langName
}
}, {
$group: {
_id: "$_id",
image: { $first: "$image" },
title: { $first: "$translation.title" },
tags: { $first: "$translation.tags" },
author: { $first: "$author" },
date: { $first: "$date" },
description: { $first: "$translation.description" },
body: { $first: "$translation.body" },
comments: { $first: "$comments" }
}
},
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author"
}
},
{
$unwind: {
path: "$author"
}
},
{ ///////////////////// Here I need to populate the comment author. How can I do it?
$lookup: {
from: "users",
localField: "comments.author",
foreignField: "_id",
as: "comments.author"
}
},
{
$unwind: {
path: "$comments.author"
}
},
{
$project: {
image: 1,
title: 1,
tags: 1,
date: 1,
description: 1,
body: 1,
// comments: 1,
"comments.date": 1,
"comments.body": 1,
"comments.author.username": 1,
"comments.author.avatar": 1,
"author.username": 1,
"author.avatar": 1
}
}
], function(err, result) {
console.log(err)
return result;
});
console.log(article[0].comments[0]);
console.log(article);
}
if (!article) throw new Error();
} catch {
return res.status(404).render('errors/404');
}
res.render('articles/article', {
article: article[0]
});
});
So after some digging I came up with this solution:
router.get('/article/:id', async (req, res) => {
const { id: articleId } = req.params;
const lang = req.i18n.language;
const langName = new Intl.DisplayNames(['en'], { type: "language" }).of(lang).toLowerCase();
try {
if (req.i18n.language === "en") {
var article = await Article.findById(articleId)
.populate([
{path:'comments.author', select:'+photo +username'},
{path:'author', select:'+photo +username'}
])
.select("title image tags author date description body comments");
} else {
var article = await Article
.aggregate([
{
$match: {
"_id": ObjectId(articleId)
}
}, {
$unwind: "$translation"
}, {
$match: {
"translation.language": langName
}
},
{
$lookup: {
from: "users",
localField: "author",
foreignField: "_id",
as: "author"
}
},
{
$unwind: {
path: "$author"
}
},
{
$unwind: {
path: "$comments"
}
},
{
$lookup: {
from: "users",
localField: "comments.author",
foreignField: "_id",
as: "comments.author"
}
},
{
$group: {
_id: "$_id",
root: { $mergeObjects: '$$ROOT' },
image: { $first: "$image" },
title: { $first: "$translation.title" },
tags: { $first: "$translation.tags" },
author: { $first: "$author" },
date: { $first: "$date" },
description: { $first: "$translation.description" },
body: { $first: "$translation.body" },
comments: { $push: "$comments" }
}
},
{
$project: {
image: 1,
title: 1,
tags: 1,
date: 1,
description: 1,
body: 1,
"comments.date": 1,
"comments.body": 1,
"comments.author.username": 1,
"comments.author.avatar": 1,
"author.username": 1,
"author.avatar": 1
}
}
]);
}
if (!article) throw new Error();
} catch {
return res.status(404).render('errors/404');
}
res.render('articles/article', {
article: article[0]
});
});

fetch 3 schema value using mongoose node JS

I have 3 collection schema CategorySchema, SubCategorySchema, ProductSchema like below.
var CategorySchema = new mongoose.Schema({
catgory_name: {
type: String,
required: [true, "Catgory name is required"]
},
modified_date: {
type: Date
}
});
module.exports = mongoose.model("Category", CategorySchema);
var SubCategorySchema = new Schema({
subcatgory_name: {
type: String,
required: [true, "subcategory name is required"]
},
category_id: {
type: Schema.Types.ObjectId,
ref: "Category",
required: [true, "category id is required"]
},
modified_date: {
type: Date
},
is_active: {
type: Boolean,
default: 1
}
});
module.exports = mongoose.model("SubCategories", SubCategorySchema);
const ProductSchema = new Schema({
product_name: {
type: String,
required: [true, "Product name is required"]
},
product_image: {
type: String,
required: [true, "Product image is required"]
},
category_id: {
type: Schema.Types.ObjectId,
ref: "Category",
required: [true, "category is required"]
},
subcategory_id: {
type: Schema.Types.ObjectId,
ref: "Subcategory",
required: [true, "Subcategory is required"]
},
modified_date: {
type: Date
},
is_active: {
type: Boolean,
default: 1
}
});
module.exports = mongoose.model("Products", ProductSchema);
Here i want to take all the active products (is_active = 1) with the corresponding categories and active subcategories (is_active = 1). No need to check is_active condition for categories but need to check active condition for subcategories and products
I tried with the below code in node JS controller
router.get("/list", (req, res, next) => {
products
.find({ is_active: true })
.populate("category_id")
.populate("subcategory_id", null, SubCategory, {
match: { is_active: true }
})
//.where("subcategory_id", !null)
.then(products => res.json({ status: 200, data: products }))
.catch(err => res.json(err));
});
But even subcategories are inactive it returns the product data
You can query using mongodb aggregation framework still using mongoose.
router.get("/list", (req, res, next) => {
products
.aggregate([
{
$match: {
is_active: true
}
},
{
$lookup: {
from: "subcategories",
localField: "subcategory_id",
foreignField: "_id",
as: "subcategories"
}
},
{
$unwind: "$subcategories"
},
{
$match: {
"subcategories.is_active": true
}
},
{
$lookup: {
from: "categories",
localField: "category_id",
foreignField: "_id",
as: "category"
}
},
{
$addFields: {
category: {
$arrayElemAt: ["$category", 0]
}
}
}
])
.then(products => res.json({ status: 200, data: products }))
.catch(err => res.status(500).json(err));
});
Playground
Let's have these sample documents:
db={
"products": [
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": true,
"_id": "5e159ca1bd95457404b22bc3",
"product_name": "Product1 Name",
"product_image": "Product1 Image",
"category_id": "5e159b77a746036404b5f0ae",
"subcategory_id": "5e159befbd95457404b22bc2"
},
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": false,
"_id": "5e159cb8bd95457404b22bc4",
"product_name": "Product2 Name",
"product_image": "Product2 Image",
"category_id": "5e159b77a746036404b5f0ae",
"subcategory_id": "5e159befbd95457404b22bc2"
},
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": true,
"_id": "5e159d3abd95457404b22bc6",
"product_name": "Product3 Name",
"product_image": "Product3 Image",
"category_id": "5e159b77a746036404b5f0ae",
"subcategory_id": "5e159ce0bd95457404b22bc5"
}
],
"categories": [
{
"modified_date": "2020-01-08T09:04:18.003Z",
"_id": "5e159b77a746036404b5f0ae",
"catgory_name": "Main Category 1"
}
],
"subcategories": [
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": true,
"_id": "5e159befbd95457404b22bc2",
"subcatgory_name": "Sub Category 1",
"category_id": "5e159b77a746036404b5f0ae"
},
{
"modified_date": "2020-01-08T09:06:51.544Z",
"is_active": false,
"_id": "5e159ce0bd95457404b22bc5",
"subcatgory_name": "Sub Category 2",
"category_id": "5e159b77a746036404b5f0ae"
}
]
}
The result will be:
[
{
"_id": "5e159ca1bd95457404b22bc3",
"category": {
"_id": "5e159b77a746036404b5f0ae",
"catgory_name": "Main Category 1",
"modified_date": "2020-01-08T09:04:18.003Z"
},
"category_id": "5e159b77a746036404b5f0ae",
"is_active": true,
"modified_date": "2020-01-08T09:06:51.544Z",
"product_image": "Product1 Image",
"product_name": "Product1 Name",
"subcategories": {
"_id": "5e159befbd95457404b22bc2",
"category_id": "5e159b77a746036404b5f0ae",
"is_active": true,
"modified_date": "2020-01-08T09:06:51.544Z",
"subcatgory_name": "Sub Category 1"
},
"subcategory_id": "5e159befbd95457404b22bc2"
}
]
As you see, even the Product 3 is active, it hasn't been retrieved because its subcategory 5e159ce0bd95457404b22bc5 is not active.

Unable to push object into array in Mongodb collection but Id is inserted

I am using comment array in my schema as fallows. I want to push comments data into that comment array using nodejs api
var Schema = mongoose.Schema;
var myfeeds = new Schema({
title: {
type: String,
required: true
},
feed: {
type: String,
required: true
},
createdBy: {
type: String,
required: true,
unique: true
},
createdDate: {
type: Date,
required: true,
default: Date.now()
},
comment: [
{
commentBy: {
type: String
},
commentText: {
type: String
},
createdDate: {
type: Date
}
}
],
likes: [
{
likesCount: {
type: Number,
required: false
},
likeBy: {
type: String,
required: false
}
}
]
});
I want to push object to this comment array. so, for that I did in this way please tell me if anything wrong in this
let _id = req.body.id;
let commentBy = req.body.commentedBy;
let commentedText = req.body.commentedText;
let commentedDate = req.body.commentedDate;
let data = {
commentBy: commentBy,
commentText: commentedText,
createdDate: commentedDate
};
MyFeeds.findByIdAndUpdate(
{ _id: _id },
{
$push: {
comment: data
}
}
)
.then((result) => {
res.status(200).json({
status: result
});
})
.catch((err) => {
res.status(500).json({
status: 'invalid',
err: err
});
});
but only id are inserted into that comment array but not the required content
"comment": [
{
"_id": "5badfd092b73fa14f4f0aa7c"
},
{
"_id": "5badfd102b73fa14f4f0aa7d"
},
{
"_id": "5badfd142b73fa14f4f0aa7e"
},
{
"_id": "5badfd31500fb11bb06b4c8a"
},
{
"_id": "5badfd35500fb11bb06b4c8b"
},
{
"_id": "5badff3d439a151190d62961"
}
],

Resources