$addToSet to an array but it gives me null - javascript

so basically I've a wish list and I've bunch of products that I want to add inside the the wish list products array using a put request (I'm using postman btw).
This is the wish list schema, and yes I know that the document's name in the db is "whishlist"....I hate typos
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = mongoose.Schema.Types.ObjectId;
var whishList = new Schema({
title: {type: String, default: "Cool whishlist"},
products:[{type: ObjectId, ref:'Product'}]
});
module.exports = mongoose.model('WhishList', whishList);
This is the products schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var product = new Schema({
title: String,
price: Number,
likes: {type: Number, default: 0}
});
module.exports = mongoose.model('Product', product);
and now this is the code that I am trying to run
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/swag-shop');
var Product = require('./model/product');
var wishList = require('./model/wishlist');
app.put('/wishlist/product/add', function(request, response){
Product.find({_id: request.body.productId}, function(err, product){
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
}else{
wishList.update({_id: request.body.wishlistId},{$addToSet: {products: product._id}}, function(err, wishlist){
if(err){
response.status(500).send({err: "could not add item to wishlist /update/"});
}else{
response.send(wishlist);
}
});
}
});
I really can't see where is the problem I tried deleting the document and posting it again but I had the same problem.
Thanks in advance

The issue is that the result from Product.find() is an array of Mongoose documents if the query matches any documents in the collection instead of a single document which you want.
Thus the expression {$addToSet: {products: product._id}} resolves to {$addToSet: {products: undefined}} because product is an array and product._id is undefined. Take this simple example
var product = [{ '_id': 1 }];
console.log(product._id) // logs undefined
To remedy this problem, you can either access the only element in the array as
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product[0]._id} },
function(err, wishlist) { ... }
);
Or use the findOne() method which returns a single document when querying the product:
Product.findOne({ '_id': request.body.productId }, function(err, product) {
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
} else {
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product._id } },
function(err, wishlist) { ... }
);
}
});
The findById() method is also useful in this case i.e.
Product.findById(request.body.productId, function(err, product) {
if(err) {
response.status(500).send({err: "could not add item to wishlist"});
} else {
wishList.update(
{ '_id': request.body.wishlistId },
{ '$addToSet': { 'products': product._id } },
function(err, wishlist) { ... }
);
}
});

Related

How to Update a specific Item in an array nested in a document

Hi I am building an RESTful API in Node using mongoose to manage data on a practice food delivery site I am building.
I want to setup a patch route that will remove an order Item from my items array nested in my Orders document based on a request from the user identifying the specific item with a name or ID.
I have a patch route which pushes a new order item into the Items Array nested in the Orders document, I want this patch route to also be able to remove a specific Item from the array based on a prop such as name or ID
I have tried using the Update and UpdateOne methods and I think I'm just getting the syntax wrong or something as I keep getting errors.
Server.js:
require("dotenv").config()
const express = require("express");
const mongoose = require("mongoose");
const app = express();
mongoose.connect(process.env.DATABASE_URL)
const db = mongoose.connection
db.on("error", () => console.error(error))
db.once("open", () => console.log("connected to database"))
app.use(express.json())
const subscribersRouter = require("./routes/subscribers")
const suscribersLoginRouter = require ("./routes/login")
const restaurantsRouter = require("./routes/restaurants")
const ordersRouter = require("./routes/orders")
app.use("/subscribers", subscribersRouter)
app.use("/login", suscribersLoginRouter)
app.use("/restaurants", restaurantsRouter)
app.use("/orders", ordersRouter)
app.listen(3000, () => {
console.log("Server has started on port 3000")
});
Order Model:
const mongoose = require("mongoose")
const orderSchema = new mongoose.Schema({
userID: {
type: String,
required: true
},
total: {
type: Number,
required: true
},
items: {
type: Array,
required: true
}
})
module.exports = mongoose.model("order", orderSchema)
Orders Route (you will see here that I have a patch route which pushes a new order item into the Items Array nested in the Orders document, I want this patch route to also be able to remove a specific Item from the array based on a prop such as name or ID, the issue I am have is 1. How to create an if statement that gets the update of the item to be triggered and the the code id use in that if statement to actually update that Item)
const express = require("express")
const router = express.Router()
const Order = require("../models/order")
// Getting All
router.get("/", async (req, res) => {
try {
const orders = await Order.find()
res.json(orders)
} catch (err) {
res.status(500).json({
message: err.message
})
}
})
// Getting One
router.get("/:id", getOrder, (req, res) => {
res.json(res.order)
})
// Creating One
router.post("/", async (req, res) => {
const order = new Order({
userID: req.body.userID,
total: req.body.total,
items: req.body.items
})
try {
console.log(order)
const newOrder = await order.save()
res.status(201).json(newOrder)
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
// Updating One
router.patch("/:id", getOrder, async (req, res) => {
if (req.body.userID != null) {
res.order.userID = req.body.userID
}
if (req.body.total != null) {
res.order.total = req.body.total
}
if (req.body.items != null) {
const currentItems = res.order.items
const newItem = req.body.items
currentItems.push(newItem)
}
try {
const updatedItems = await res.order.save()
res.json(updatedItems)
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
// Deleting One
router.delete("/:id", getOrder, async (req, res) => {
try {
await res.order.remove()
res.json({
message: "Deleted Order"
})
} catch (err) {
res.status(500).json({
message: err.message
})
}
})
async function getOrder(req, res, next) {
let order
try {
order = await Order.findById(req.params.id)
if (order === null) {
return res.status(404).json({
message: "Cannot Find Order"
})
}
} catch (err) {
return res.status(500).json({
message: err.message
})
}
res.order = order
next()
}
module.exports = router
TEST Requests:
# ORDERS
# Get All
GET http://localhost:3000/orders
###
#Get One
GET http://localhost:3000/orders/627fe8e575a8229d0ae81e73
###
#Create One
POST http://localhost:3000/orders
Content-Type: application/json
{
"userID": "627f8b476fa64425928750c9",
"total":50,
"items": [
{
"name": "Burder",
"price": "R20",
"description": "A good Fuggen Waffel"
},
{
"name": "Hotdog",
"price": "R20",
"description": "A good Fuggen Waffel"
},
{
"name": "Bunny Chow",
"price": "R20",
"description": "A good Fuggen Waffel"
},
{
"name": "Pizza",
"price": "R20",
"description": "A good Fuggen Waffel"
}
]
}
###
#Delete One or all
DELETE http://localhost:3000/orders/628202c3b208aebc7f7f8f98
###
# Update on (add Order Item)
PATCH http://localhost:3000/orders/628202c3b208aebc7f7f8f98
Content-Type: application/json
{
"items": {
"name": "gravy",
"price": "R20",
"description": "A good Fuggen Waffel"
}
}
###
I'm not sure I understood you correctly. I understood that you need the PATCH route to also delete an item from the items array by name.
So here is my solution to it:
Because you already fetched the order and you just want to delete a specific item from the items property, you can use filter to do so before saving the order document.
res.order.items = res.order.items.filter(({ name }) => name !== itemNameToRemove);
Like this:
// Updating One
router.patch("/:id", getOrder, async(req, res) => {
const {
userID,
total,
items,
itemNameToRemove
} = req.body;
if (userID != null) {
res.order.userID = userID;
}
if (total != null) {
res.order.total = total;
}
if (items != null) {
const newItem = items;
res.order.items.push(newItem);
if (itemNameToRemove) {
res.order.items = res.order.items.filter(({
name
}) => name !== itemNameToRemove);
}
}
try {
const updatedItems = await res.order.save()
res.json(updatedItems)
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
you can use $pull for this.
Order.update(
{ userID : "userID123" },
{$pull : {"items" : {"name":"gravy"}}}
)
This will delete the object with name as gravy belong to the userID : userID123

NodeJs - how to populate the model view with data from another model

I am practicing Node by making a simple inventory app, in which I have 3 models: Item, Category and Manufacturer. What I want to do is, when displaying an item details, to include the Category and Manufacturer associated with that specific item, however it seems that I can not make it work. Any help would be appreciated.
Here is the Item controller
const Item = require("../models/item");
const Category = require("../models/category");
const Manufacturer = require("../models/manufacturer");
const async = require("async");
exports.index = function (req, res) {
res.render("index", { title: "StrinGuist" });
};
//display list of all items
exports.item_list = function (req, res, next) {
Item.find({}, "name description category in_stock price manufacturer")
.populate("item")
.exec(function (err, list_items) {
if (err) {
return next(err);
}
res.render("item_list", { title: "All items", item_list: list_items });
});
};
//display detail page for a specific item
exports.item_detail = function (req, res, next) {
async.parallel(
{
item: function (callback) {
Item.findById(req.params.id)
.populate("category")
.populate("manufacturer")
.exec(callback);
},
},
function (err, results) {
if (err) {
return next(err);
}
if (results.item == null) {
// No results.
const err = new Error("Item not found");
err.status = 404;
return next(err);
}
// Successful, so render.
res.render("item_detail", {
title: results.item.name,
item: results.item,
});
}
);
};
and here is the view (pug)
extends layout
block content
h1 #{item.name}
div(class='item-detail-content')
p #[strong Price: $] #{item.price}
p #[strong Description: ] #{item.description}
p #[strong In stock: ] #{item.in_stock}
p #[strong Manufacturer: ] #{item.manufacturer.name}
p #[strong Category: ] #{item.category.name}
This throws me an error "Cannot read property 'name' of null", but I am not quite sure what is wrong. I tried also to declare Category and Manufacturer name as 'virtual', but that didn't work either.
For a ORM like mongoose when using mongodb, you can use the populate method with allows you do that.
https://mongoosejs.com/docs/populate.html

mongoose js won't print out query value

I want to create using mongoose js a collection of kitten with this document in it {name: "mike"}.
After creating this document I want to print it's value.
I wrote this code below.
2 problems:
this code doesn't end (meaning when I wrote node file.js the cmd line stays open (stucked) and no return value is return (infinite loop like in a server).
the code doesn't print the value of "mike". just create this doucument...
what am I doing wrong?
thanks
var mongoose = require('mongoose');
var url = 'mongodb://Yotam:Yotam#ds023475.mlab.com:23475/small-talkz';
mongoose.connect(url);
var kittySchema = mongoose.Schema({
name: String
});
var Kitten = mongoose.model('kitten', kittySchema);
Kitten.create({ name: "mike" }, function (err, small) {
if (err) return handleError(err);
});
Kitten.findOne( { } ), function(err, docs){
console.log(docs.name);
};
return 1;
newKitten = { name: "mike" };
Kitten.create(newKitten, function (err, kitty) {
if {
(err) return handleError(err);
} else {
console.log(kitty); //OR console.log(kitty.name);
}
});
Kitten.findOne({name: "mike"}).exec(function(e, kitten) {
if (e) {
console.log(e)
} else {
console.log(kitten.name)
}
});
the problem was {for anyone whose intersted (and thanks for herkou)} that I did not use the exec command..
This works:
Kitten.findOne( { name: "mike"} ).exec( function(err, docs){
console.log(docs.name);
return;
});
update:
also had a probelm with race conditions... the create of the documnet not finished when the query was called. that is why I got undeinfed.
use this new code:
var mongoose = require('mongoose');
var url = 'mongodb://Yotam:Yotam#ds023475.mlab.com:23475/small-talkz';
mongoose.connect(url);
var kittySchema = mongoose.Schema({
name: String,
color:String
});
var Kitten = mongoose.model('Kitten', kittySchema);
var newKitten = { name: "mike", color:"white" };
Kitten.create(newKitten, function (err, kitty) {
if (err) {
return handleError(err);
} else {
call_query();
}
});
var call_query= function(){
var query= Kitten.findOne( { name: "mike"} );
query.exec( function(err, docs){
console.log(docs.color);
return;
});
}
return 1;
now I just need to understand why this script doesn't end.

How to add object to nested Array using Node.js and Mongoose

How can I add object to my nested Array in PartnerSchema?
I separate documents, because in the future there will be more of nested arrays.
This is my schema:
var productSchema = new mongoose.Schema({
name: String
});
var partnerSchema = new mongoose.Schema({
name: String,
products: [
{
type: mongoose.Schema.Types.ObjectId,
ref: 'Product'
}]
});
module.exports = {
Partner: mongoose.model('Partner', partnerSchema),
Product: mongoose.model('Product', productSchema)
}
And this is my backend:
var campSchema = require('../model/camp-schema');
router.post('/addPartner', function (req, res) {
new campSchema.Partner({ name : req.body.name }).save(function (err, response) {
if (err) console.log(err);
res.json(response);
});
});
router.post('/addProduct', function (req, res) {
campSchema.Partner.findByIdAndUpdate({ _id: req.body.partnerId },
{
$push: {
"products": {
name: req.body.dataProduct.name
}
}
}, { safe: true }, function (err, response) {
if (err) throw err;
res.json(response);
});
});
I can add Partner by using /addPartner and it works fine.
Problem is with second function /addProduct I can't add Product to Array in Partner Schema. I have an error: CastError: Cast to undefinded failed for value "[object Object]" at path "products"
Since the products field in Partner model is an array that holds _id refs to the Product model, you are supposed to push an _id to the array, not an object hence Mongoose complains with an error.
You should restructure your code to allow the saving of the Product _id ref to the Partner model:
router.post('/addProduct', function (req, res) {
var product = new campSchema.Product(req.body.dataProduct);
product.save(function (err) {
if (err) return throw err;
campSchema.Partner.findByIdAndUpdate(
req.body.partnerId,
{ "$push": { "products": product._id } },
{ "new": true },
function (err, partner) {
if (err) throw err;
res.json(partner);
}
);
});
});

add query conditions using mongoose model

using Node.js, Mongoose Schema and MongoDB,
the user model Schema is
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
name: { type: String, trim: true }
, address: { type: String }
, birth: { type: Date }
, tlf: { type: String }
, email: { type: String, lowercase: true }
});
module.exports = mongoose.model('User', userSchema);
and the function to query users with conditions (var cond), select (var sel), options (var opts) is
findAllUsers = function (req, res, next) {
var cond = {
name : req.query.nm
};
var sel = 'name address';
var opts = {
skip: req.query.sk, limit: req.query.lim, sort: req.query.srt
};
User.find(cond, sel, opts).lean().exec(function (err, users) {
if (err) next(err);
var body = {};
body.skip = req.query.sk;
body.limit = req.query.lim;
body.users= users;
User.count(cond).exec(function (err, total) {
if (err) next(err);
body.total = total;
res.json(body);
});
});
}
and then, what is the best way to create a conditions with regexp, like, or... ?
The equivalent to "like" in MongoDB is the regex operator and would be implemented like this:
db.collection.find({ "address": { "$regex": "via" } });
Keep in mind though, that just the same as the equivalent like "%via%" statement, this does need to scan every document in the collection (or at best index) in order to match the string that is not "anchored" to the start of the string.

Categories