How can I implement this model - node.js

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

Related

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

Product.find({category: req.params.category}) doesn't work

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!

How to generate a slug everytime an article is created and saved in the database?

So, I have this articleSchema in which I want to create a unique slug.
const mongoose = require("mongoose")
const Schema = mongoose.Schema
var URLSlug = require('mongoose-slug-generator')
const articleSchema = new Schema({
title: { type: String, required: true },
description: { type: String, required: true },
userId: { type: Schema.Types.ObjectId, ref: "User" },
slug: { type: "String", slug: "title", unique: true }
}, { timestamps: true })
articleSchema.pre("save", function(next) {
this.slug = this.title.split(" ").join("-")
next()
})
articleSchema.plugin(URLSlug("title", {field: "Slug"}))
const Article = mongoose.model("Article", articleSchema)
module.exports = Article
Here is the articleController
newArticle: (req, res) => {
Article.create(req.body, (err, newArticle) => {
if (err) {
return res.status(404).json({ error: "No article found" })
} else {
return res.status(200).json({ article: newArticle })
}
})
}
I don't know, when I check this in postman it's saying no article found, let alone the slug! Also, I'm getting this error:
schema.eachPath is not a function
According to the mongoose-slug-generator you need to apply plugin on mongoose, but in your code it is applied to the schema.
So if you try with this code, it will work:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
var URLSlug = require("mongoose-slug-generator");
mongoose.plugin(URLSlug);
const articleSchema = new Schema(
{
title: { type: String, required: true },
description: { type: String, required: true },
userId: { type: Schema.Types.ObjectId, ref: "User" },
slug: { type: String, slug: "title"}
},
{ timestamps: true }
);
articleSchema.pre("save", function(next) {
this.slug = this.title.split(" ").join("-");
next();
});
const Article = mongoose.model("Article", articleSchema);
module.exports = Article;
If we send a req.body like this:
{
"title": "metal head dev",
"userId": "5e20954dc6e29d1b182761c9",
"description": "description"
}
The saved document will be like this (as you see slug is generated correctly):
{
"_id": "5e23378672f10f0dc01cae39",
"title": "metal head dev",
"description": "description",
"createdAt": "2020-01-18T16:51:18.445Z",
"updatedAt": "2020-01-18T16:51:18.445Z",
"slug": "metal-head-dev",
"__v": 0
}
By the way mongoose-slug-generator seems pretty old, there is a more popular and well maintained slugify package.

Mongoose - model methods not found in middleware

It's possible that i'm just burned out, but I have the following models:
user
const mongoose = require('mongoose');
const validate = require('mongoose-validator');
const Post = require('./post');
let UserSchema = mongoose.Schema({
firstName: { type: String, required: true },
lastName: { type: String, required: true },
email: {
type: String, required: true, lowercase: true, trim: true, unique: true, index: true,
validate: [validate({ validator: 'isEmail', message: 'Invalid Email!' })]
},
posts: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Post' }]
})
module.exports = mongoose.model('User', UserSchema);
posts
const _ = require('lodash');
const mongoose = require('mongoose');
const User = require('./user');
let PostSchema = mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
title: { type: String, required: true },
body: { type: String, require: true }
})
PostSchema.post('save', async function (next) {
await User.update({ _id: this.user }, { $push: { posts: this._id } })
return next();
})
module.exports = mongoose.model('Post', PostSchema);
When trying to add a new post, the post save hook runs, but I get the error User.update is not a function (same goes for findOneAndUpdate, findOne, etc).
I can call user.update from the rest of the app without issues, so not sure whats happening here. Both models are in the same directory.
What you missed is that post middleware has the first argument as the "document" and not the next handler:
user.js
const { Schema } = mongoose = require('mongoose');
const userSchema = new Schema({
firstName: String,
lastName: String,
posts: [{ type: Schema.Types.ObjectId, ref: 'Post' }]
});
post.js
const { Schema } = mongoose = require('mongoose');
const User = require('./user');
const postSchema = new Schema({
user: { type: Schema.Types.ObjectId, ref: 'User' },
title: String,
body: String
});
// note that first argument is the "document" as in "post" once it was created
postSchema.post('save', async function(doc, next) {
await User.update({ _id: doc.user._id },{ $push: { posts: doc._id } });
next();
});
index.js
const { Schema } = mongoose = require('mongoose');
const User = require('./user');
const Post = require('./post');
const uri = 'mongodb://localhost/posttest';
mongoose.set('debug', true);
mongoose.Promise = global.Promise;
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const conn = await mongoose.connect(uri);
await Promise.all(Object.entries(conn.models).map(([k,m]) => m.remove()));
let user = await User.create({ firstName: 'Ted', lastName: 'Logan' });
let post = new Post({ user: user._id, title: 'Hi', body: 'Whoa!' });
post = await post.save();
mongoose.disconnect();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
Returns:
Mongoose: users.remove({}, {})
Mongoose: posts.remove({}, {})
Mongoose: users.insertOne({ posts: [], _id: ObjectId("5b0217001b5a55208150cc9b"), firstName: 'Ted', lastName: 'Logan', __v: 0 })
Mongoose: posts.insertOne({ _id: ObjectId("5b0217001b5a55208150cc9c"), user: ObjectId("5b0217001b5a55208150cc9b"), title: 'Hi', body: 'Whoa!', __v: 0 })
Mongoose: users.update({ _id: ObjectId("5b0217001b5a55208150cc9b") }, { '$push': { posts: ObjectId("5b0217001b5a55208150cc9c") } }, {})
Showing that the update fires with the correct detail.
In good design you really should avoid this and simply drop the posts array from the User model. You can always either use a virtual instead:
userSchema.virtual('posts', {
ref: 'Post',
localField: '_id',
foreignField: 'user'
})
Or just get the data via $lookup:
User.aggregate([
{ "$match": { "_id": userId } }
{ "$lookup": {
"from": Post.collection.name,
"localField": "_id",
"foreignField": "user",
"as": "posts"
}}
])
Storing and maintaining arrays of related ObjectId values "on the parent" is kind of an "anti-pattern" and leads to unnecessary overhead such as writing in two places where you only need "one".
Also in general you should be opting for embedding "first", and only considering "referencing" if and when the usage pattern of the application actually demands it. Simply copying the same patterns of an RDBMS with a database engine that was not designed for that is not the best way to utilize it.

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