Populate mongoose subdoc in different location - mongodb

I'm populating a subdocument using my users model. Right now all the info that is populated is being placed under user_list[x]._id so for instance user_list[0]._id.displayName but I'd like to have the data available like this: user_list[0].displayName
Is it possible to tell mongoose to extend user_list[x] with the population data for user_list[x]._id data rather than placing it with the _id property itself?
/// model
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
deepPopulate = require('mongoose-deep-populate')(mongoose);
var user_list_schema = new Schema({
_id:{
type: Schema.ObjectId,
ref: 'User'
},
soft_delete:{
type:Boolean
}
});
var BusinessSchema = new Schema({
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
user_list:[user_list_schema],
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Business', BusinessSchema);
///controller
Business.findById(id).populate('user', 'displayName').populate('user_list._id').exec(function (err, business) {
if (err) {
return next(err);
} else if (!business) {
return res.status(404).send({
message: 'No business with that identifier has been found'
});
}
req.business = business;
next();
});

Related

How can I see the products per each category with mongoose

this is my schema for storing products using mongoose as below.
const mongoose = require("mongoose");
const mongoosePaginate = require("mongoose-paginate-v2");
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: {
type: String,
required: true,
},
category: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
productImage: {
type: String,
required: true,
},
description: {
type: String,
},
createdAt: {
type: Date,
default: new Date(),
},
deletedAt: {
type: Date,
},
});
productSchema.plugin(mongoosePaginate);
const productModel = mongoose.model("Product", productSchema, "Product");
module.exports = productModel;
and this how I have the schema for storing categories that products are related to
const mongoose = require("mongoose");
const categorySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
product: { type: mongoose.Schema.Types.ObjectId, ref: "Product" },
});
const categoryModel = mongoose.model("Category", categorySchema, "Category");
module.exports = categoryModel;
What I don´t know is how to populate my controller.
getAll: async (req, res) => {
const limitPage = parseInt(req.query.limit, 10) || 10;
const pageChange = parseInt(req.query.page, 10) || 1;
Product.paginate({}, { limit: limitPage, page: pageChange })
.then((result) => {
return res.status(200).json({
message: "GET request to all getAllProducts",
dataCount: result.length,
result: result,
});
})
.catch((err) => {
console.log(err);
res.status(500).json({
error: err,
});
});
},
Please help, I don´t understand why it not being populated and how to see the categories displayed with the categorie they belong to.
You should probably include populate in your query like so:
...
Product.paginate({}, { limit: limitPage, page: pageChange }).populate('category')
...
Note: Are you sure you want to have a 1-1 relation between products and categories. Because this is what you achieve if you set the relation like you did on both schemas. If yes, you should find a way to ensure that this 1-1 relation is enforced each time you save or update objects.

Saving a document in Mongoose, reference id is not stored in the second document

When I save a new "experience" document with the model Experience, the experience _id is not saved into the document of the user. So my "experiences" array in the user document remains empty. Why?
const mongoose = require('mongoose');
const ExperienceSchema = mongoose.Schema({
name: String,
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
reviews: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Review' }],
categories: [{ type: String }],
});
module.exports = mongoose.model('Experience', ExperienceSchema);
==============================================
const mongoose = require('mongoose');
const UserSchema = mongoose.Schema({
name: String,
experiences: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Experience' }],
});
module.exports = mongoose.model('User', UserSchema);
=============================================
// Update experience to database
router.post('/:id', (req, res, next) => {
const idexp = req.params.id;
const newExperience = {
name: req.body.name,
user: req.user._id,
};
Experience.findOneAndUpdate({ _id: idexp }, newExperience, (err, result) => {
if (err) {
return res.render(`/${idexp}/edit`, { errors: newExperience.errors });
}
return res.redirect(`/experiences/${idexp}`);
});
});
The experiences is the sub-document of user schema. So, when you save experiences, the user will not be saved. However, when you save user, the experience should be saved.
Refer this subdocs documentation
Here is the solution... I needed to use $push to update the user document with the experience id before rendering the site.
Experience.findOneAndUpdate({ _id: idexp }, newExperience, (err, result) => {
if (err) {
return res.render('experiences/edit', { errors: newExperience.errors });
}
User.findByIdAndUpdate({ _id: req.session.passport.user._id }, { $push: { experiences: idexp } }, (err) => {
if (err) {
next(err);
} else {
return res.redirect(`/experiences/${idexp}`);
}
});
});

Mongoose Populate a field

I have two mongoose schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var itemSchema = new Schema({
name: {type: String, required: true, max: 25, trim: true},
price: {type: Number, required: true, trim: true, default: 0},
tax: {
type: Schema.Types.ObjectId,
ref: "Store"
}
});
module.exports = mongoose.model('Item', itemSchema);
The second Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var storeSchema = new Schema({
name: {type: String, required: true, trim: true},
taxes: [
{
name: String,
rate: Number
}
]
});
module.exports = mongoose.model('Store', storeSchema);
What I want to do is populate the itemSchema tax object with the storeSchema taxes array of object. every time I pushed a new tax object to the taxes array mongoose created an ObjectId. I stored that ObjectId in my itemSchema Tax. I want to use that _id to retrieve the store taxes that matches the itemSchema _id.
I have tried this so far, but I get no in the tax attribute.
Item.find().populate("tax", "taxes").exec(function (err, docs) {
if (err) return console.error(err);
console.log(items);
});
Try this query
Item.find().populate({
path: 'tax',
select: 'taxes'
}).exec(function (err, docs) {
if (err) {
console.error(err);
} else {
console.log(docs);
}
});
Item.find().populate(path:"tax", model: )
Mention your item model file name... don't use "" or '' for the file name, simply add the file name.
Use Item.find().populate("Store", "taxes") instead of Item.find().populate("tax", "taxes").

Mongoose populate array

I can't get mongoose to populate an array of objects.
The schema is as follows:
var topOrganisationsForCategorySchema = new mongoose.Schema({
category: String,
topOrganisations: [{
organisation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'organisation'
},
model: mongoose.Schema.Types.Mixed
}]
});
module.exports = mongoose.model('topOrganisationsForCategory', topOrganisationsForCategorySchema);
I would like all of the objects in this collection populated with an array of organisations.
Here is what i have tried
TopOrganisationsForCategory
.find()
.exec(function(err, organisation) {
var options = {
path: 'topOrganisations.organisation',
model: 'organisation'
};
if (err) return res.json(500);
Organisation.populate(organisation, options, function(err, org) {
res.json(org);
});
});
var organisationSchema = new mongoose.Schema({
name: String,
aliases: [String],
categories: [String],
id: {
type: String,
unique: true
},
idType: String
});
organisationSchema.index({
name: 'text'
});
module.exports = mongoose.model('organisation', organisationSchema);
You're close but a couple notes:
The following code assumes you also have a schema/model declaration for Oranisation.
I am not sure if the model property is meant as an option (which would be invalid) or actually is a property of topOrganisations.
So, I left model in as it shouldn't cause any issues but be aware that if you were using it as an option it is not doing what you might think it is.
// Assuming this schema exists
var organisationSchema = new mongoose.Schema({...});
var topOrganisationsForCategorySchema = new mongoose.Schema({
category: String,
topOrganisations: [{
organisation: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Organisation' // Model name convention is to begin with a capital letter
}
// Is `model` supposed to be for the ref above? If so, that is declared in the
// Organisation model
model: mongoose.Schema.Types.Mixed
}]
});
// Assuming these model definitions exist
var Organisation = mongoose.model('Organisation', organisationSchema);
var TopOrganisationsForCategory = mongoose.model('TopOrganisationsForCategory', TopOrganisationsForCategorySchema);
// Assuming there are documents in the organisations collection
TopOrganisationsForCategory
.find()
// Because the `ref` is specified in the schema, Mongoose knows which
// collection to use to perform the population
.populate('topOrganisations.organisation')
.exec(function(err, orgs) {
if (err) {
return res.json(500);
}
res.json(orgs);
});

Does Mongoose Actually Validate the Existence of An Object Id?

I like the validation that comes with Mongoose. We are trying to figure out whether we want to use it, and put up with the overhead. Does anyone know if providing a reference to the parent collection when creating a mongoose schema, (in the child schema, specify the object id of the parent object as a field,) does this then mean that every time you try to save the document it checks the parent collection for the existence of the refereneced object id?
I'm doing it with middleware, performing a search of the element on validation:
ExampleSchema = new mongoose.Schema({
parentId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Example'
}
});
ExampleModel = mongoose.model('Example', ExampleSchema);
ExampleSchema.path('parentId').validate(function (value, respond) {
ExampleModel.findOne({_id: value}, function (err, doc) {
if (err || !doc) {
respond(false);
} else {
respond(true);
}
});
}, 'Example non existent');
I'm using mongoose-id-validator. Works good
var mongoose = require('mongoose');
var idValidator = require('mongoose-id-validator');
var ReferencedModel = new mongoose.Schema({name: String});
var MySchema = new mongoose.Schema({
referencedObj : { type: mongoose.Schema.Types.ObjectId, ref: 'ReferencedModel'},
referencedObjArray: [{ type: mongoose.Schema.Types.ObjectId, ref: 'ReferencedModel' }]
});
MySchema.plugin(idValidator);
No, an ObjectId field that's defined in your schema as a reference to another collection is not checked as existing in the referenced collection on a save. You could do it in Mongoose middleware, if needed.
I found this thread very helpful and this is what I came up with:
This Middleware (I think its one anyway please let me know if not) I wrote checks the referenced model for the id provided in the field.
const mongoose = require('mongoose');
module.exports = (value, respond, modelName) => {
return modelName
.countDocuments({ _id: value })
.exec()
.then(function(count) {
return count > 0;
})
.catch(function(err) {
throw err;
});
};
Example model:
const mongoose = require('mongoose');
const uniqueValidator = require('mongoose-unique-validator');
const Schema = mongoose.Schema;
const User = require('./User');
const Cart = require('./Cart');
const refIsValid = require('../middleware/refIsValid');
const orderSchema = new Schema({
name: { type: String, default: Date.now, unique: true },
customerRef: { type: Schema.Types.ObjectId, required: true },
cartRef: { type: Schema.Types.ObjectId, ref: 'Cart', required: true },
total: { type: Number, default: 0 },
city: { type: String, required: true },
street: { type: String, required: true },
deliveryDate: { type: Date, required: true },
dateCreated: { type: Date, default: Date.now() },
ccLastDigits: { type: String, required: true },
});
orderSchema.path('customerRef').validate((value, respond) => {
return refIsValid(value, respond, User);
}, 'Invalid customerRef.');
orderSchema.path('cartRef').validate((value, respond) => {
return refIsValid(value, respond, Cart);
}, 'Invalid cartRef.');
orderSchema.path('ccLastDigits').validate(function(field) {
return field && field.length === 4;
}, 'Invalid ccLastDigits: must be 4 characters');
orderSchema.plugin(uniqueValidator);
module.exports = mongoose.model('order', orderSchema);
I'm a very new dev so any feedback is greatly valued!
You can try https://www.npmjs.com/package/lackey-mongoose-ref-validator (I'm the developer)
It also prevents deletion if the reference is used on another document.
var mongooseRefValidator = require('lackey-mongoose-ref-validator');
mongoSchema.plugin(mongooseRefValidator, {
onDeleteRestrict: ['tags']
});
It's an early version, so some bugs are expected. Just fill in a ticket if you find any.
I know this is an old thread but I had the same problem and I came up with a more "modern" solution.
I'm not an expert myself, hope I'm not misleading anyone, but this seems to work:
for example, in a simple "notes" schema, which contains a user field:
const noteSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
text: String
});
here's the middleware that checks if the userId exists:
noteSchema.path('user').validate(async (value) => {
return await User.findById(value);
}, 'User does not exist');