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.
Related
I wanna to move sub document to main documents, and return single DTO without any nested document, below is my sample data.js
data.js
const mongoose = require('mongoose');
//city
const citySchema = new mongoose.Schema({
cityName: { type: String, required: true, unique: true },
});
const City = mongoose.model('City', citySchema);
//country
const countrySchema = new mongoose.Schema({
countryName: { type: String, required: true, unique: true },
});
const Country = mongoose.model('Country', countrySchema);
//user
const userSchema = new mongoose.Schema({
username: { type: String, required: true },
city: {
type: mongoose.Schema.Types.ObjectId,
ref: 'City',
required: true,
},
country: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Country',
required: true,
},
createdAt: { type: Date, required: true },
updatedAt: { type: Date, required: true },
});
const User = mongoose.model('User', userSchema);
function getUser(id) {
return User.findById(id)
.populate('city')
.populate('country')
.exec();
};
Current return JSON Response for User:
{
"_id": "6321ac3d14a57c2716f7f4a0",
"name": "David",
"city": {
"_id": "63218ce557336b03540c9ce9",
"cityName": "New York",
"__v": 0
},
"country": {
"_id": "632185bbe499d5505cafdcbc",
"countryName": "USA",
"__v": 0
},
"createdAt": "2022-09-14T10:26:05.000Z",
"__v": 0
}
How do I move the cityName and countryName to main model, and response JSON as below format?
{
"_id": "6321ac3d14a57c2716f7f4a0",
"username": "David",
"cityName": "New York",
"countryName": "USA",
"createdAt": "2022-09-14T10:26:05.000Z",
}
Using aggregation you can try something like this:
db.user.aggregate([
{
"$match": {
_id: "6321ac3d14a57c2716f7f4a0"
}
},
{
"$lookup": {
"from": "city",
"localField": "city",
"foreignField": "_id",
"as": "city"
}
},
{
"$lookup": {
"from": "country",
"localField": "country",
"foreignField": "_id",
"as": "country"
}
},
{
"$addFields": {
"country": {
"$arrayElemAt": [
"$country",
0
]
},
"city": {
"$arrayElemAt": [
"$city",
0
]
}
}
},
{
"$addFields": {
"countryName": "$country.countryName",
"cityName": "$city.cityName"
}
},
{
"$unset": [
"country",
"city"
]
}
])
Here's the playground link.
The other probably simpler way of doing this would be, modify your function like this:
function getUser(id) {
const user = User.findById(id)
.populate('city')
.populate('country')
.exec();
if(user.country) {
user.countryName = user.country.countryName;
}
if(user.city) {
user.cityName = user.city.cityName;
}
delete user.country;
delete user.city;
return user
};
I have a debate collection, which holds all the debates and another collection holds the votes against each debate. So I wanted to retrieve all the debates with a new user flag (isVoted) if I found any user in the vote collection against each debate.
Vote model:
var voteSchema = new Schema({
user: { type: Schema.Types.ObjectId, required: true, ref: 'User' }, // One who votes
debate: { type: Schema.Types.ObjectId, required: true, ref: 'Debate' }
}, { timestamps: true });
Debate Model:
var debateSchema = new Schema({
category: { type: Schema.Types.ObjectId, required: true, ref: 'Category' },
question: { type: String, required: true },
Votes: { type: Number, default: 0 },
}, { timestamps: true });
Query
DebateData.aggregate([
{
$match: query
},
{
$sort : { createdAt : -1 }
},
{
$lookup: {
from: "votes", // must be the PHYSICAL name of the collection
localField: "_id",
foreignField: "debate",
as: "votes"
}
},
{
$addFields: {
'isVoted': {
$cond: { if: { $eq: [ '$votes.user', ObjectId(req.query.userId) ] }, then: 'true', else: 'false' }
}
}
},
{
$project: {
'_id': 1,
'question': 1,
'isVoted': 1,
'createdAt': 1
}
},
]).then(result => {
res.status(200).json({ success: true, status: 200, message: 'Debate videos', data: result});
}).catch(err => {
res.status(500).json({ success: false, status: 500, message: err.message })
});
Expected output:
{
"data": [
{
"_id": "60e81f8299a4809658290d80",
"votes": 10,
"category": [
{
"name": "Hockey"
}
],
"question": "What is football?",
"isVoted": true,
"createdAt": "2021-07-09T10:05:54.498Z"
},
{
"_id": "60e438f1194949add0cc2074",
"votes": 12,
"category": [
{
"name": "Cricket"
}
],
"question": "What is football?",
"isVoted": false,
"createdAt": "2021-07-06T11:05:21.654Z"
}
]
}
Current output:
{
"data": [
{
"_id": "60e81f8299a4809658290d80",
"votes": 10,
"category": [
{
"name": "Hockey"
}
],
"question": "What is football?",
"createdAt": "2021-07-09T10:05:54.498Z"
},
{
"_id": "60e438f1194949add0cc2074",
"votes": 12,
"category": [
{
"name": "Cricket"
}
],
"question": "What is football?",
"createdAt": "2021-07-06T11:05:21.654Z"
}
]
}
Vote Data:
{
data: [
{
"user": "69881f8299a480965829ytr267",
"debate": "60e81f8299a4809658290d80"
}
]
}
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'
}
},
]
I have a Usermodel the schema is as below, I'm trying to populate the subcategories in the deals and the wishes but not able to.
image: { type: String, default: 'NA' },
firstName: { type: String, default: 'first name' },
lastName: { type: String, default: 'last name' },
email: { type: String, lowercase: true, unique: true, trim: true },
password: { type: String, min: 6 },
deals: [
{
_id : false,
category: { type: Schema.Types.ObjectId, ref: 'Category' },
subCategory: [{ type: Schema.Types.ObjectId, ref: 'Subcategory' }]
}
],
wishes: [
{
_id : false,
category: { type: Schema.Types.ObjectId, ref: 'Category' },
subCategory: [{ type: Schema.Types.ObjectId, ref: 'Subcategory' }]
}
]
Here is a sample JSON response
"deals": [
{
"subCategory": [
"5bc419cea25fc606153bdbe4",
"5bc419cea25fc606153bdbe3"
],
"category": "5bc419cea25fc606153bdbe2"
}
],
"wishes": [
{
"subCategory": [
"5bc41a40a25fc606153bdbe5",
"5bc41a40a25fc606153bdbe7"
],
"category": "5bc41a40a25fc606153bdbe5"
}
]
I am able to populate the deals.category but not deals.subcategory
// method to get users with populated deals and wishes
allUsers: async (req, res, next) => {
const users = await User.find({})
.populate('deals.category', 'id name')
.populate('wishes.category', 'id name')
.populate('deals.subCategory');
return respondSuccess(res, null, users);
},
// currently the response i'm getting
"deals": [
{
"subCategory": [],
"category": {
"_id": "5bc419cea25fc606153bdbe2",
"name": "Auto"
}
}
],
"wishes": [
{
"subCategory": [
"5bc41a40a25fc606153bdbe5",
"5bc41a40a25fc606153bdbe7"
],
"category": {
"_id": "5bc41a40a25fc606153bdbe5",
"name": "Baby Sitting"
}
}
]
looking for much-needed help.
thank you
I had a problem with the association of collections.
I spent 2 days and still did not solve the problem, it's new for me.
My models:
// Schema opened cases
const openedSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'user',
required: [true, 'user is required'],
index: true
},
weapon: {
type: Schema.Types.ObjectId,
ref: 'cases.weapons',
required: [true, 'weapon is required'],
index: true
},
sellPrice: {
type: Number,
default: null
},
status: {
type: Number,
default: 0
}
}, {
timestamps: true
});
const opened = mongoose.model('opened', openedSchema);
// list cases
const casesSchema = new Schema({
name: {
type: String,
unique: true,
required: [true, 'name is required']
},
price: {
type: Number,
required: [true, 'price is required']
},
weapons: [ {
weapon: {
type: Schema.Types.ObjectId,
ref: 'weapon',
index: true
}
} ]
}, {
timestamps: false
});
const cases = mongoose.model('cases', casesSchema);
// list weapons
const weaponSchema = new Schema({
name: {
type: String,
unique: true,
required: [true, 'name is required']
},
price: {
type: Number,
required: [true, 'price is required']
},
autoship: {
count: Number,
status: Boolean,
price: Number
}
}, {
timestamps: false
});
const weapon = mongoose.model('weapon', weaponSchema);
That's what documents look like
// cases
{
"_id": {
"$oid": "59653bcfa9ac622e1913e10c"
},
"name": "test case #1",
"price": 256,
"weapons": [
{
"weapon": {
"$oid": "59653bcfa9ac622e1913e10b"
},
"_id": {
"$oid": "59653bcfa9ac622e1913e10d"
}
},
{
"_id": {
"$oid": "59653d3279aeda2fda9fb490"
},
"weapon": {
"$oid": "59653c5d069f562eb0ba4ef3"
}
},
{
"_id": {
"$oid": "59653d38ba04de2fdddc459f"
},
"weapon": {
"$oid": "59653c893a772e2ef7b65a29"
}
}
],
"__v": 0
}
// opened
{
"_id": {
"$oid": "5965d134c8c95972a1a498f5"
},
"updatedAt": {
"$date": "2017-07-12T07:35:16.419Z"
},
"createdAt": {
"$date": "2017-07-12T07:35:16.419Z"
},
"user": {
"$oid": "5965d0d6ea9db872360db98b"
},
"weapon": {
"$oid": "59653bcfa9ac622e1913e10d"
},
"status": 0,
"sellPrice": null,
"__v": 0
}
// weapon
{
"_id": {
"$oid": "59653bcfa9ac622e1913e10b"
},
"name": "AWP | Fever Dream",
"price": 300,
"autoship": {
"status": true,
"price": 167,
"count": 5
},
"__v": 0
}
I need to get a list of open cases with weapons data.
opened -> cases -> weapon
So, I do this:
opened.find()
.populate('cases.weapons')
.then(_opened => {
console.log(_opened);
})
.catch(err => {
logger.error(err);
});
But populate does not work.
Unless I am mistaken, there is no relationship between openedSchema and casesSchema.
It is not opened -> cases -> weapon but opened -> weapon as openedSchema has no field called cases -- which means cases will never be populated.
Based on your schema definition, it should be opened.find().populate('weapon').