How do we create a reference to a collection with mongoose - node.js

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
}

Related

Mongoose Could not find path "${filterPath}" in schema by updating a subdocument

In this Items object:
{
"items": [
{
"_id": "63a48f12a9731cfd8a64b0b1",
"item_name": "addidas shoes",
"__v": 0,
"rating": [
{
"_id": "63a48fd51fb70775d216eb87",
"rate": 1,
"user_id": "6398a1a157d6146413b23b43"
}
]
}
]
}
I'm trying to update the rating property if a user_id inside of it already exists, else, add a new object into it.
const addRating = async (req, res) => {
const { rate, user_id, item_id } = req.body;
// item_id = 63a48f12a9731cfd8a64b0b1 user_id = 6398a1a157d6146413b23b43 rate = 6
// Adding ratings to the selected item
const test = await itemDB.item.updateOne(
{ _id: item_id, rating: { user_id: user_id } },
{ $push: { "items.rating.$[i].rate": rate } },
{ arrayFilters: [{ "i.user_id": user_id }], upsert: true }
);
console.log(test);
res.json({ message: "success" });
};
I wanted to change something in the rating property so I set the filter as above but it gives me this error when hitting the endpoint:
\node_modules\mongoose\lib\helpers\update\castArrayFilters.js:74
throw new Error(`Could not find path "${filterPath}" in schema`);
^
Error: Could not find path "items.rating.0.user_id" in schema
This is my Items Schema:
const mongoose = require("mongoose");
const RateSchema = mongoose.Schema({
rate: {
type: Number,
required: true,
},
user_id: {
type: mongoose.ObjectId,
},
item_id: {
type: mongoose.ObjectId,
},
});
const ItemSchema = mongoose.Schema({
item_name: {
type: String,
required: true,
},
rating: {
type: [RateSchema],
},
});
module.exports = mongoose.model("Items", ItemSchema);
It looks like it is not noticing that items is also an array when applying the array filter to rating.
Try using the all-positional operator like:
{ $push: { "items.$[].rating.$[i].rate": rate } }

mongoose save internal documents creating a document that contains a document

Im learning mongoose and i have a question how to save several documents:
// Product.js
const categorySchema = mongoose.Schema(
{ name: String },
{ collection: "categories" }
);
const productSchema = mongoose.Schema(
{ name: String, category: categorySchema },
{ collection: "products" }
);
modules.exports = mongoose.model("Product", productSchema);
The idea is that when I create a product this way
const Product = require("./Product.js")
const product = new Product({name: 'Soccer Ball', category: {name: "Sports"})
await product.save()
i want to get a document in the collection products and also a document in the collection categories
how can it be possible
thanks in advance
PD : Im getting this but category is not save in the collection
{
"msg": "Product created succesfully",
"ok": true,
"product": {
"name": "Soccer ball",
"category": {
"name": "Sports",
"_id": "6275df4c8149967bea21e7c0"
},
"_id": "6275df4c8149967bea21e7bf",
"__v": 0
}
}
You should define your Product's category as a ref attribute:
// Product.js
const categorySchema = mongoose.Schema(
{ name: String },
{ collection: 'categories' }
);
const productSchema = mongoose.Schema(
{
name: String,
category: { type: mongoose.Schema.Types.ObjectId, ref: 'categories' },
},
{ collection: 'products' }
);
modules.exports = {
Category: mongoose.model('Category', categorySchema),
Product: mongoose.model('Product', productSchema),
}
Doing this you will need to assign the _id of the category to the new Product:
const { Category } = require("./Product.js")
const { Product } = require("./Product.js")
// Create category (supposing it is not present)
const category = new Category({ name: "Sports" })
await category.save()
// Add category _id to product
const product = new Product({name: 'Soccer Ball', category: category._id})
await product.save()
Finally, you will be able to retrieve the product by using populate:
const product = await Product.findById(<product_id>).populate('categories').exec()
This query should give the same result than before, but the Category data will be loaded from the reference in the Category collection.

How to use POST request for this type array of embedded documents in nodejs

i created a cart schema. inside cart schema i have a items property which is an array of movie sub-docs.
const mongoose = require("mongoose");
const movieSchema = new mongoose.Schema({
title: {
type: String,
required: true,
trim: true,
minlength: 5,
maxlength: 255,
},
price: {
type: Number,
min: 0,
required: true,
},
dailyRentalRate: {
type: Number,
required: true,
min: 0,
max: 255,
},
quantity: {
type: Number,
default: 0,
},
});
const cartSchema = new mongoose.Schema({
dateCreated: {
type: Date,
default: new Date().getTime(),
},
items: [
{
movie: movieSchema,
},
],
});
const Cart = mongoose.model("Cart", cartSchema);
exports.Cart = Cart;
now i want to use post request so i can create new cart with array of movie
const router = require("express").Router();
const { Cart } = require("../models/shoppingCarts");
const { Movie } = require("../models/movie_model");
router.get("/", async (req, res) => {
const carts = await Cart.find().sort("dateCreated");
res.send(carts);
});
router.post("/", async (req, res) => {
const movie = await Movie.findById(req.body.movie);
if (!movie) return res.status(400).send("Invalid movie Id.");
let cart = new Cart({
items: [
{
movie: {
_id: movie._id,
title: movie.title,
price: movie.price,
dailyRentalRate: movie.dailyRentalRate,
quantity: movie.quantity,
},
},
],
});
await cart.save();
res.send(cart);
});
module.exports = router;
when i use postman to create i gets a error
error: Cannot access 'movie' before initialization ReferenceError: Cannot access 'movie' before initialization
i tried to find solution but i could not.
What I observed from the code above is:
you didn't create and export your Movie model; module.exports = mongoose.model("Movie", movieSchema);
When working with subdocuments in mongoose I think one of the best ways of going about it is saving the ObjectId in your MongoDB collection which can later be retrieved by calling the .populate("model name") method, see below code;
// in your Cart Schema
const cartSchema = new mongoose.Schema({
dateCreated: {
type: Date,
default: new Date().getTime(),
},
items: [
{
type:Schema.Types.ObjectId,
ref: "Movies"
},
],
});
// in your post request
router.post("/", async (req, res) => {
const movie = await Movie.findById(req.body.movie);
if (!movie) return res.status(400).send("Invalid movie Id.");
let cart = new Cart()
//do
cart.items = movie.id
//the movie id will be saved in the database
await cart.save();
res.send(cart);
});
//in your get request
router.get("/", async (req, res) => {
const carts = await Cart.find().populate("movies")
//you should get a detailed response
res.send(carts);
});

How can I implement this model

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,
});

How do I populate array of ids using mongoose?

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;

Resources