Populated nested schema in Mongoose - node.js

This is my Schema, I have a CampaignStat Object which has a nested Product Schema, but at the same time Product is another model.
const ProductsSchema = new Schema({
_id: {
type: mongoose.Schema.ObjectId,
ref: 'Product'
},
clickedAt: Date,
deviceInfo: {
type: Schema.Types.Mixed
}}, { _id: false })
const CampaignStatSchema = new Schema({
campaign: {
type: mongoose.Schema.ObjectId,
ref: 'Campaign'
},
subscriber: {
type: mongoose.Schema.ObjectId,
ref: 'Subscriber'
},
openedAt: Date,
products: [ProductsSchema]
}, {
timestamps: { createdAt: true, updatedAt: false }
})
An this is how is being stored in MongoDB
"_id" : ObjectId("5f18d01ecab4eb6175cc0f83"),
"subscriber" : ObjectId("5ed6890da2c99843280a0058"),
"campaign" : ObjectId("5ed937829ff2a1f99de55150"),
"products" : [
{
"_id" : ObjectId("5f160ca2014be4e159f32fcd")
},
{
"_id" : ObjectId("5f160ca2014be4e159f3302e")
},
{
"_id" : ObjectId("5f160ca2014be4e159f33036")
},
{
"_id" : ObjectId("5f160ca2014be4e159f3311a")
},
{
"_id" : ObjectId("5f160ca2014be4e159f33159")
}
],
"createdAt" : ISODate("2020-07-22T23:47:42.228Z"),
"__v" : 0
I need to populate Products, I'm doing this but I cannot get the Product collection attributes
const CampaignStats = await this.find({ campaign: "5ed937829ff2a1f99de55150" })
.populate('subscriber')
.populate({
path: 'products',
model: 'Product'
})

In case somebody gets into the same problem, I changed this (_id for product):
const ProductsSchema = new Schema({
product: {
type: mongoose.Schema.ObjectId,
ref: 'Product'
},
clickedAt: Date,
deviceInfo: {
type: Schema.Types.Mixed
}}, { _id: false })
And my query is like this:
this.find(query)
.populate({
path: 'products',
populate: {
path: 'product',
model: 'Product'
}
})

Related

How to populate nested model in mongoose

I have dataSubmitted model:
module.exports = (mongoose, mongoosePaginate) => {
const schema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
company: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
}, {
timestamps: true
})
schema.plugin(mongoosePaginate)
const dataSubmitted = mongoose.model('dataSubmitted', schema)
return dataSubmitted
}
And i have the other model is called "profile" for user profile. I use user._id to releation with "profile" model.
My profile model:
module.exports = (mongoose, mongoosePaginate) => {
const schema = mongoose.Schema({
name: String,
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
}, {
timestamps: true
})
schema.plugin(mongoosePaginate)
const profile = mongoose.model('profile', schema)
return profile
}
The result json from select dataSubmitted, is like this:
totalItems": 90,
"data": [
{
"_id": "63546cb05bfb8607c005da6a",
"user": "6353a1aebaf28d7aa066a963",
"company": "6022487bbe148f3a9b132122",
"createdAt": "2022-10-22T22:20:32.296Z",
"updatedAt": "2022-10-25T07:09:52.363Z",
"__v": 0
},
]
My question is how can i populate the user id with "profile" model?
My current code:
const options = {
populate: [
{
path: 'user',
populate: {
path: 'user',
model: 'profile'
}
}
],
sort: ({ createdAt: -1 })
}
but its return "user": null if i add nested populate like that.
If you need to reference the profile model from the dataSubmitted you should change your schema declaration to:
const dataSubmittedSchema = mongoose.Schema(
{
profile: {
type: mongoose.Schema.Types.ObjectId,
ref: 'profile',
},
company: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
},
},
{
timestamps: true,
}
);
Then, you should be able to populate with:
dataSubmitted.find({}).populate({
path: 'profile',
populate: {
path: 'user'
}
})

How to push items in to an array which was referenced in collections schema in mongoose, nodejs

i want to create an item and push it into items array which was referenced in my collections schema. What i am sending in the payload is item properties and collection id. But i am getting CastError. Please help
COLLECTION SCHEMA
const collectionSchema = new Schema(
{
name: { type: String },
description: { type: String },
owner: { type: Schema.Types.ObjectId, ref: "User" },
items: [{ type: Schema.Types.ObjectId, require: true, ref: "Item" }],
},
{ timestamps: true }
);
export default model("Collection", collectionSchema);
ITEM SCHEMA
import mongoose from "mongoose";
const { Schema, model } = mongoose;
const itemSchema = new Schema(
{
name: { type: String },
description: { type: String },
owner: { type: Schema.Types.ObjectId, ref: "User" },
likes: [{ type: Schema.Types.ObjectId, ref: "User" }],
collections: { type: Schema.Types.ObjectId, ref: "Collection" },
},
{ timestamps: true }
);
export default model("Item", itemSchema);
CREATE ITEM ROUTE
itemRouter.post("/", JWTAuthMiddleware, async (req, res, next) => {
const item = new ItemModal(req.body)
const collection = await CollectionModal.findByIdAndUpdate(
req.user._id,
{
$push : { items: [{ ...req.body, id: item._id }] },
},
{ new: true }
);
res.send(collection);
});
What i am getting is an CASTERROR

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?

using "or" operator when using .populate on a query?

I have my invoices model which has a "sales" field ,which is array of sales. In my sales model I have item field which either can come from Vehicles schema or AfterSale schema.
export const Sales = new Schema<ISale>(
{
customer: { type: Schema.Types.ObjectId, ref: "Customer", required: true },
category: { type: String, enum: enu, required: true },
item: {
type: Schema.Types.ObjectId,
required: true,
ref: (doc) => doc.category,
},
total: { type: Number, required: true },
discount: { type: Number },
purchaseDate: { type: Date, required: true },
branch: { type: Schema.Types.ObjectId, ref: "Branch", required: true },
},
{ timestamps: true },
);
I want to populate the sales.item on my invoice schema, but the problem is , since item can reference to multiple schemas, I can only populate one of them.
async findAll(req: any, query: SharedPreferences): Promise<InvoiceClass[]> {
const invoices = this.invoiceModel
.find(req.searchObj)
.sort({ [query.sort]: query.orderBy === "desc" ? -1 : 1 })
.populate([
"customer",
"sales",
{
path: "sales",
populate: [
{
path: "customer",
model: "Customer",
},
{
path: "branch",
model: "Branch",
},
{
path: "item",
model: "Vehicle",
},
{
path: "item",
model: "AfterSale", //in this case only AfterSale will be populated since
// its last and has same path
}
],
},
]);
my perfect solution would have been using the or operator , but that doesnt work
async findAll(req: any, query: SharedPreferences): Promise<InvoiceClass[]> {
const invoices = this.invoiceModel
.find(req.searchObj)
.sort({ [query.sort]: query.orderBy === "desc" ? -1 : 1 })
.populate([
"customer",
"sales",
{
path: "sales",
populate: [
{
path: "customer",
model: "Customer",
},
{
path: "branch",
model: "Branch",
},
{
path: "item",
model: { $or: ["Vehicle", "AfterSale"] },
},
],
},
]);
any reference/help will be appreciated, thanks.

Mongo aggregate $match and $group in $lookup result

I have node text
I have 3 models lead, user and company
I want to get all customers for selected company id using lead model
what I try was,
using $lookup and $group I got unique customer with details but after adding $match I got empty array
I hope someone can get my on the right track..
****This is my Aggregation code ****
const customers = await Lead.aggregate([
{
$match: { moving_company: companyId },
},
{
$group: {
_id: {
"customer": "$customer",
},
}
},
{
$lookup: {
from: "users",
localField: "_id.customer",
foreignField: "_id",
as: "myCustomResut",
},
},
]);
This is my Company model
const schemaOptions: mongoose.SchemaOptions = {
_id: true,
id: false,
timestamps: true,
skipVersioning: true,
strict: false,
toJSON: {
getters: true,
virtuals: true,
},
};
export const CompanySchema = new mongoose.Schema(
{
name: {
type: Schema.Types.String,
},
},
schemaOptions
);
const Company = mongoose.model<ICompany>("Company", CompanySchema);
export default Company;
This my lead model
const schemaOptions: mongoose.SchemaOptions = {
_id: true,
id: false,
timestamps: true,
skipVersioning: true,
strict: false,
toJSON: {
getters: true,
virtuals: true,
},
};
export const LeadSchema = new mongoose.Schema(
{
status: {
type: Schema.Types.String,
},
customer: {
type: Schema.Types.ObjectId,
ref: Customer.modelName
},
assigned_sales_agent: {
type: Schema.Types.String,
ref: Agent.modelName
},
moving_company: {
type: Schema.Types.ObjectId,
ref:Company.modelName
},
selected_categories: [
{
type: SelectedCategorySchema,
},
],
},
schemaOptions
);
const Lead = mongoose.model<ILead>("Lead", LeadSchema);
export default Lead;
This is my customer model
export const customerSchema = new mongoose.Schema(
{
address: {
type: Schema.Types.ObjectId,
ref: Addresses,
},
},
UserSchemaOptions
);
export const Customer = User.discriminator<ICustomer>(
"Customer",
customerSchema,
Role.CUSTOMER
);
export default Customer;

Resources