I have identical products that are sold in different cities at different prices.
Product.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ProductSchema = new Schema({
name: String,
img: String,
translations: {
Spanish: { name: String},
}
});
module.exports = mongoose.model("products", ProductSchema)
City.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CitySchema = new Schema({
name: String,
products_cost: [{
product_id: { type: Schema.Types.ObjectId, ref: 'products' },
price: { type: Number, default: 0 }
}],
translations: {
Spanish: { name: String },
}
});
module.exports = mongoose.model('cities', CitySchema);
Set defaults records for products:
const Product = require('../models/Product');
const defaultValuesProducts = () => {
try {
Product.insertMany([
{
name: "Agate",
img: "img/Agate.png",
translations: {
Spanish: { name: "Ágata" },
}
},
{
name: "Gold",
img: "img/Gold.png",
translations: {
Spanish: { name: "Oro" },
}
},
]);
} catch (error) {
console.log(error);
}
}
module.exports = {defaultValuesProducts};
And I'm trying to link products and cities. Set default records for cities:
const Product = require('../models/Product');
const Cities = require('../models/City');
const defaultValuesCities = () => {
try {
const products = Product.find();
Cities.insertMany([
{
name: "Amsterdam",
products_cost: [
{
product_id: products[0]
},
{
product_id: products[1]
}
],
shipRoute: 8,
translations: {
Spanish: { name: "Ámsterdam" },
}
}
]);
} catch (error) {
console.log(error);
}
};
module.exports = { defaultValuesCities };
In DB i see all added records:
router.get('', async (req, res) => {
try {
const cities = await City.find();
//const cities = await City.find().populate(products); - ReferenceError: products is not defined
res.render('index', { cities });
}
catch (error) {
console.log(error);
}
});
<ol>
<% cities.forEach(city=> { %>
<li>
<%=city.name%>
<%=city.translations.Spanish.name%>
<ol>
<% city.products_cost.forEach(element=> { %>
<li>
<%=element.product_id.name%>
</li>
<% }) %>
</ol>
</li>
<% }) %>
</ol>
Please tell me where I made a mistake. And how can I access the DBRef object directly?
There are several problems with your code.
Product.js
with the following:// Change name of Model from 'products' to 'Product'
module.exports = mongoose.model("Product", ProductSchema)
City.js
with the following:// Change name the ref from 'products' to 'Product'
product_id: { type: Schema.Types.ObjectId, ref: 'Product' },
That's because:
The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural, lowercased version of your model name.
defaultValuesCities()
function has not added the relevant data to your products_cost
array. Yes, you have two new objects in there but you do not have the product_id
, all you have the automatically generated _id
and the default 0
value for price
. Both your Product.find();
and Cities.insertMany()
functions are asynchronous operations so your your products won't be returned in time for the values to be accessed in the Cities.insertMany()
part of your code. Update like so:const defaultValuesCities = async () => { //< Mark function as async
try {
const products = await Product.find(); //< Mark function call with await
await Cities.insertMany([ //< Again, mark function call with await
{
name: "Amsterdam",
products_cost: [
{
product_id: products[0]._id //< You need to insert the _id ObjectId
},
{
product_id: products[1]._id //< You need to insert the _id ObjectId
}
],
shipRoute: 8,
translations: {
Spanish: { name: "Ámsterdam" },
}
}
]);
} catch (error) {
console.log(error);
}
};
product_id
rerference is nested inside an array of products_cost
you will need to define that path to the populate()
method. Update your route like so:try {
// You need to use dot notation to access 'products_cost.product_id'
const cities = await City.find().populate({ path: 'products_cost.product_id', model: Product });
res.render('index', { cities });
} catch (error) {
console.log(error);
}