I tried to populate array of object IDS but it doesn't work
Mongoose Object
const Schema = mongoose.Schema;
const OrderSchema = new Schema({
products: [{
product: { type: Schema.Types.ObjectId, ref: 'Product'},
quantity: { type: Number, default: 1},
price: { type: Number, default: 0},
}],
});
let Model = mongoose.model('Product', ProductSchema);
module.exports = Model;
The API route for getting all the orders
const Order = require('../models/order');
router.get('/orders', checkJWT, (req, res, next) => {
Order
.find({ owner: req.decoded.user._id })
.populate('products.product')
.exec((err, orders) => {
res.json({
orders: orders
});
});
})
It still showing the ID of the product
"products": [
{
"_id": "5a47b6c35e96e3900fd63984",
"price": 0,
"quantity": 2
}
]
replace
type: Schema.Types.ObjectId;
with
type: mongoose.Schema.Types.ObjectId;
Related
Now in product schema, I have a category attached to it like that
"_id" : ObjectId("5e6dfde62764a11b34ccc9a3"),
"title" : "t-shirt",
"price" : 12,
"category" : "T-shirts",
"description" : "<p>tshirts description goes here</p>\r\n",
"images" : [ ],
"__v" : 0
and my route is like that:
router.get('/category/:category', function(req, res){
var categorySlug = req.params.category;
Category.findOne({slug: categorySlug}, function(err, category){
if(err){
console.log(err);
} else {
Product.find({category: categorySlug}, function(err, products){
if(err){
console.log(err);
} else {
console.log(category)
console.log(products)
res.render('client/cat_product', {products, category, title: products.title});
}
})
}
})
})
The problem is:
when I console.log(category) it displays the category normally but when I console.log(products) it gives me a blank array with no products in it
these are the schema models for the category and the product:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CategorySchema = new Schema({
title: {
type: String,
required: true
},
slug: String,
products: [
{
type: Schema.Types.ObjectId,
ref: 'Product'
}
]
});
const Category = mongoose.model('Category', CategorySchema);
module.exports = Category;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ProductSchema = new Schema({
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
category: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
images: [
{
url: String,
public_id: String
}
]
});
module.exports = mongoose.model('Product', ProductSchema);
I believe this solution may work, if not, please, let me know what you see as output. I was able to test the solution on a simple problem I have created, but I have no idea regarding the details of yours, your question does not leave it clear, and you did not provide further details.
router.get("/category/:category", function(req, res) {
var categorySlug = req.params.category;
res.send(
res.render("client/cat_product", {
products: await Product.find({ category: categorySlug }) ,
category: await Category.findOne({ slug: categorySlug }) ,
title: await await Product.find({ category: categorySlug }).select("title")
})
);
});
If you have several products, coming from Category.findOne, you may need to do something like Category.findOne().populate("products").
Hope that helps!
Please forgive me if this question seems related to the one I already asked before, I feel I didn't really put the question well the first time.
I have created a resource route based on the following data seeded into Mongodb from my application
// seeder.js
"_id": "5d7a514b5d2c12c7449be020",
"issuedBy": "Ola",
"collectedBy": "Ola",
"quantity": "8",
"product": "5d713995b721c3bb38c1f5d0",
My question is this; How do i actually save the product objectId "5d713995b721c3bb38c1f5d0" on the Orders collection
exports.getOrders = asyncHandler(async (req, res, next) => {
if (req.params.productId) {
const orders = await Orders.find({ product: req.params.productId });
return res.status(200).json({
success: true,
count: orders.length,
data: orders
});
} else {
res.json(orders);
}
});
With the above route, I am setting a condition that's based on the presence of a ProductId in the URL that will be matched inside the Orders collection "Orders.find({ product: req.params.productId })"
//Product Schema
const ProductSchema = new mongoose.Schema({
name : String,
description : String,
price : Number,
quantity : Number,
supplier :String
},{timestamps:true});
module.exports = mongoose.model('Product', ProductSchema)
// Orders Schema
const OrderSchema = new mongoose.Schema({
issuedBy : String,
collectedBy: String,
quantity: Number,
product: {
type: mongoose.Schema.ObjectId,
ref: 'Product',
required: true
},
},{timestamps:true});
const Orders = mongoose.model("Orders", OrderSchema);
// Export model
module.exports = Orders;
How do I actually create a route that saves the productId in the Orders collection like these "product": "5d713995b721c3bb38c1f5d0"?
I think an order can have multiple products. So I changed the name of product to producs, and made it array.
Also, for model names it is better to use singular naming convention.
I would set up my schemas and models like this:
Product model:
const mongoose = require("mongoose");
const ProductSchema = new mongoose.Schema(
{
name: String,
description: String,
price: Number,
quantity: Number,
supplier: String
},
{ timestamps: true }
);
module.exports = mongoose.model("Product", ProductSchema);
Order model:
const mongoose = require("mongoose");
const OrderSchema = new mongoose.Schema(
{
issuedBy: String,
collectedBy: String,
quantity: Number,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Product",
required: true
}
]
},
{ timestamps: true }
);
module.exports = mongoose.model("Order", OrderSchema);
You can create an order with products with this code:
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const url = "mongodb://localhost:27017/ord";
const Order = require("./models/order");
const Product = require("./models/product");
const port = 3000;
app.use(express.json());
mongoose
.connect(url, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => {
app.listen(port, () => {
console.log(`App running on port ${port}...`);
});
})
.catch(error => console.log(error));
app.post("/order", async (req, res) => {
let result = await Order.create(req.body);
res.send(result);
});
app.get("/order/:orderId", async (req, res) => {
const result = await Order.findById(req.params.orderId).populate("products");
res.send(result);
});
You can create an order with products to the http://localhost:3000/order with this body: (you must use your existing product ids)
{
"issuedBy": "issuedBy",
"collectedBy": "collectedBy",
"quantity": 123,
"products": ["5ddfb388b14c5b41e0607a5e","5ddfb376b14c5b41e0607a5d"]
}
Response:
{
"products": [
"5ddfb388b14c5b41e0607a5e",
"5ddfb376b14c5b41e0607a5d"
],
"_id": "5ddfb418b14c5b41e0607a5f",
"issuedBy": "issuedBy",
"collectedBy": "collectedBy",
"quantity": 123,
"createdAt": "2019-11-28T11:48:40.500Z",
"updatedAt": "2019-11-28T11:48:40.500Z",
"__v": 0
}
When you want to get this order and its products, you need to send a GET request to the http://localhost:3000/order/5ddfb418b14c5b41e0607a5f the id in the url is the the id of the order we previosly created, so you need to use your order id.
If you want also to be able to add a product to an existing order, you can add this code:
app.post("/order/:orderId/:productId", async (req, res) => {
const result = await Order.findByIdAndUpdate(
req.params.orderId,
{
$push: {
products: req.params.productId
}
},
{ new: true }
);
res.send(result);
});
So the POST url must contain the orderId and productId like this:
http://localhost:3000/order/5ddfb418b14c5b41e0607a5f/5ddfb67c721b885790ec837b
Response:
{
"products": [
"5ddfb388b14c5b41e0607a5e",
"5ddfb376b14c5b41e0607a5d",
"5ddfb67c721b885790ec837b"
],
"_id": "5ddfb418b14c5b41e0607a5f",
"issuedBy": "issuedBy",
"collectedBy": "collectedBy",
"quantity": 123,
"createdAt": "2019-11-28T11:48:40.500Z",
"updatedAt": "2019-11-28T11:59:51.659Z",
"__v": 0
}
how can I implement this
[{
"title": "pranam",
"year": "2016",
"rating": 9,
"actors": [
{
"name": "Amir",
"birthday": "16 Aug 1982",
"country": "Bangladesh"
},
{
"name": "Imran",
"birthday": "15 Aug 1982",
"country": "Bangladesh"
}
]
}]
I had tried this ......
models/actors.js
const Joi = require('joi');
const mongoose = require('mongoose');
const actorSchema = new mongoose.Schema({
name:{
type: String,
required: true,
min: 5,
max:50
},
birthday:{
type: String,
required: true
},
country:{
type: String,
required: true
}
});
models/movies.js
const mongoose = require('mongoose');
const Joi = require('joi');
const actorSchema = require('../models/actors');
const movieSchema = new mongoose.Schema({
title:{
type:String,
required:true,
min: 5,
max: 50
},
year:{
type: String,
required: true,
min:2,
max:4
},
rating:{
type: Number,
required: true,
min:0,
max:10
},
actors: {
type: actorSchema,
required: true
}
});
routes/movies.js
const { Movie, validate} = require('../models/movies');
const { Actor} = require('../models/actors');
const auth = require('../middleware/auth');
const router = express.Router();
router.get('/', auth, async(req, res)=>{
const movies = await Movie
.find({}, { _id:0, __v:0 })
res.send(movies);
});
router.post('/', async(req, res)=>{
const {error} = validate(req.body);
if(error) return res.status(400).send(error.details[0].message)
//May be problem is hare, But I can not solve
const actor = await Actor.findById(req.body.actorId);
if(!actor) return res.status(400).send('Invalid Actors');
let movie = new Movie({
title: req.body.title,
year: req.body.year,
rating: req.body.rating,
actors:[{
name: actor.name,
birthday: actor.birthday,
country: actor.country
}]
});
try {
movie = await movie.save();
res.send(movie)
} catch (ex) {
console.log("Invalid Movie ");
}
});
module.exports =router;
I enter this by POST Method in postman
{ "title": "I hate love Story", "rating": "9", "actorId": [ "5d99ac95f17917117068631b", "5d99ad75c4edd61f98af740b"]
}
this show only first actor data in movies output by GET api call,
how can I show more actors data in movies.
In the context of the question, every other thing looks fine until this point in routes/movies.js:
const actor = await Actor.findById(req.body.actorId);
I don't think that query is correct, firstly, Model.findById() accepts just an id not an array of ids. Secondly, what you want to do here is to fetch all the actors as identified by the ids in the actorId array, this is a valid query for that:
const actors = await Actor.find(
// Filter: fetch all actors whose Id is in the array of ids provided in the request
{ _id: { $in: req.body.actorId } },
// Projection: Include just the name, birthday and country in the response, if any.
{ name: 1, birthday: 1, country: 1 }
);
You can check Model.find() for more info on how to use its query interface.
The query up there should return multiple actors and that is what you need, you can then, instantiate a new movie model with that:
new Movie({
title: req.body.title,
year: req.body.year,
rating: req.body.rating,
actors,
});
I'm new to Mongoose I don't know how to populate on condition.
So this is my model :
const OrderSchema = new Schema({
products: [{ type: Schema.Types.ObjectId, ref: 'Product' }],
remarks: {type: String, lowercase: true}
});
mongoose.model("Order", OrderSchema);
const ProductSchema = new Schema({
reference: {type: String}
status: {type: Schema.Types.ObjectId, ref: 'ProductStatus'}
});
mongoose.model("Product", ProductSchema);
const ProductStatus = new Schema({
name: {type: String}
});
const CountrySchema = new Schema({
name: {type: String}
});
mongoose.model("Country", CountrySchema);
I have a getOrderById methods
export const getOrderById = async (req, res) => {
let id = req.params.id;
try {
await orderModel
.findById(id)
.populate({
path: 'products',
populate: {
path: 'country',
model: 'Country'
}
})
.populate({
path: 'products',
populate: {
path: 'status',
model: 'ProductStatus'
}
})
.exec(function (err, orders) {
if (err) {
res.send(err);
}
res.status(200).json(orders);
});
} catch (error) {
console.log(error);
}
}
And now I would like to show in the order lists all products that have the status Received in France.
First, I guess you also missed reference to the country in the product schema, so assuming these are your corrected schemas:
const OrderSchema = new Schema({
products: [{
type: Schema.Types.ObjectId,
ref: 'Product'
}],
remarks: {
type: String,
lowercase: true
}
});
const Order = mongoose.model("Order", OrderSchema);
const ProductSchema = new Schema({
reference: {
type: String
},
country: {
type: Schema.Types.ObjectId,
ref: 'Country'
},
status: {
type: Schema.Types.ObjectId,
ref: 'ProductStatus'
}
});
const Product = mongoose.model("Product", ProductSchema);
const ProductStatusSchema = new Schema({
name: {
type: String
}
});
const ProductStatus = mongoose.model("ProductStatus", ProductStatusSchema);
const CountrySchema = new Schema({
name: {
type: String
}
});
const Country = mongoose.model("Country", CountrySchema);
As far as I understand you want to only show the products, whose country's name is 'France' and ProductStatus' name is 'Received', these kind of operations are done through Aggregation
Your query may look like this assuming you want to do it one query:
const getOrderById = async (req, res) => {
let id = req.params.id.toString();
const ObjectId = mongoose.Types.ObjectId
try {
const aggregationStages = [{
$match: {
_id: ObjectId(id) //It is important to cast to ObjectId in aggregations
}
}, {
$lookup: {
from: 'products',
let: {
productIds: '$products'
},
pipeline: [{
$match: {
$expr: {
$in: ['$_id', '$$productIds']
}
}
}, {
$lookup: {
from: 'countries',
localField: 'country',
foreignField: '_id',
as: 'country'
}
}, {
$lookup: {
from: 'productstatuses',
localField: 'status',
foreignField: '_id',
as: 'status'
}
}, {
$match: {
'country.name': 'France',
'status.name': 'Received'
}
}],
as: 'products'
}
}];
await orderModel.aggregate(aggregationStages)
.exec(function (err, orders) { // The return is an array btw.
if (err) {
res.send(err);
}
res.status(200).json(orders);
});
} catch (error) {
console.log(error);
}
}
If you feel the aggregation is complicated you may resort to breaking it to smaller simpler queries. Feel free to ask if you need more explanation/modification.
This is my product Schema using mongoose in nodeJs. However I am developing a REST API.
const ImageSchema = new Schema({
path: {
type: String
},
pos: {
type: Number
}
});
const ProductSchema = new Schema({
title: {
type: String,
required: [true, 'Product title is required.']
},
description: {
type: String
},
created_date: {
type: Date ,
required: [true, 'Created Time required.'],
default: Date.now
},
images: [
ImageSchema
]
});
const Product = mongoose.model('product', ProductSchema);
module.exports = Product;
This is how I update a product
router.put('/:id', upload.single('pImg'), function(req, res, next){
var x = req.body;
Product.findByIdAndUpdate({
_id: req.params.id
}, x).then(function(){
Product.findOne({
_id: req.params.id
}).then(function(product){
res.send(product);
});
}).catch(next);
});
My question is how can I push into the images array and also update other fields like title, description at the same time ?
you can use $push and $set in your call.