Express/Mongoose route always updates the same item - javascript

Im having a problem where if I try to update or create a new item, it only creates 1 item and then just updates that item no matter what i do, is there anything wrong with this route?
// #route POST api/item
// #desc Create/Edit item
// #access Private
router.post(
"/",
passport.authenticate("jwt", { session: false }),
(req, res) => {
const itemFields = {};
const { errors, isValid } = validateItemInput(req.body);
// Check Validation
if (!isValid) {
// If any errors, send 400 with errors object
return res.status(400).json(errors);
}
if (req.body.name) itemFields.name = req.body.name;
if (req.body.component) itemFields.component = req.body.component;
if (req.body.parameter) itemFields.parameter = req.body.parameter;
if (req.body.severity) itemFields.severity = req.body.severity;
if (req.body.description) itemFields.description = req.body.description;
if (req.body.recomendation)
itemFields.recomendation = req.body.recomendation;
if (req.body.resources) itemFields.resources = req.body.resources;
Item.findOne({ item: req.params._id }).then(item => {
if (item) {
// Update
Item.findOneAndUpdate(
{ item: req.params._id },
{ $set: itemFields },
{ new: true }
).then(item => res.json(item));
} else {
// Create
// Save Item
new Item(itemFields).save().then(item => res.json(item));
}
});
}
);

you are setting item to req.param.id which sets the content of you new item to older item if you give you older item's id so change it
Item.findOneAndUpdate(
{ item: req.params._id },
{ $set: itemFields },
{ new: true }
).then(item => res.json(item));
} else {
// Create
// Save Item
new Item(itemFields).save().then(item => res.json(item));
}

Looks like you do not have any param in the route.Inorder to use a param like req.params._id, your route should be defined as
router.post("/:_id",() =>{
//access req.params._id here
res.end()
});
to pass the value for req.params._id , you have to hit the following POST url
yoursite.com/21, where 21 is the req.params._id
https://expressjs.com/en/api.html

Related

How can I submit an array of item numbers to query a mongoDB collection and return all requested docs as array of objects?

My query is an array of numbers that I want to pull from a collection. I can map over the array and pull the needed docs, but it will not update the original array (also used spread operator) or save and/or return a new array. I found that Promise.all something about aggregate might be solutions, but I am not really sure how to implement. The mongoDB connections are all good and the client request is an array, but an async map is not practical. How can this be accomplished in a single request?
const Purchase = require('../models/purchase.model')
//Get user purchases
const getPurchases = async(req, res) => {
let purchases = req.body.purchases;
console.log(purchases)
purchases.map(async(p, index) => {
console.log(purchases)
const purchase = await Purchase.findOne({
trans_no: p
})
const purchaseObj = {
date: purchase.date,
total: purchase.total,
merchant_name: purchase.merchant_name,
merchant_id: purchase.merchant_id,
mop: purchase.mop,
items: purchase.items,
trans_no: purchase.trans_no
}
console.log(purchaseObj)
purchases[index] = purchaseObj
})
if (purchases.length > 0) {
return res.json({
status: 'ok',
purchases
})
} else {
return res.json({ status: 'error', purchase: false })
}
}
module.exports = { getPurchases }
You can do it in one request using Purchase.find() and the $in keyword. This method will return an array of purchases:
Purchase.find({
'trans_no': { $in: purchases }
}, function(err, docs){
console.log(docs);
});
Your code will be something like:
const Purchase = require('../models/purchase.model')
//Get user purchases
const getPurchases = async(req, res) => {
let purchases = req.body.purchases;
console.log(purchases)
const dbPurchases = await Purchase.find({
'trans_no': { $in: purchases }
});
dbPurchases.map((purchase, index) => {
const purchaseObj = {
date: purchase.date,
total: purchase.total,
merchant_name: purchase.merchant_name,
merchant_id: purchase.merchant_id,
mop: purchase.mop,
items: purchase.items,
trans_no: purchase.trans_no
}
console.log(purchaseObj)
purchases[index] = purchaseObj
})
if (purchases.length > 0) {
return res.json({
status: 'ok',
purchases
})
} else {
return res.json({ status: 'error', purchase: false })
}
}
module.exports = { getPurchases }
You can simplify this code and add a try/catch to make it more robust :)

.filter() not filtering the items from the array

I have this array of data in Javascript:
// console.log(currentMembers.members)
// *************************
// CURRENT MEMBERS
// *************************
// [
// new ObjectId("62385d8caee17d13a1762b39"),
// new ObjectId("6238a480170aff10d16ccd86"),
// new ObjectId("6238a480170aff10d16ccd86"),
// new ObjectId("6238a608170aff10d16ccd89")
// ]
I want to remove from the array one value that matches the variable "memberToRemove". So .filter() should be enough to perform this but it doesn't and I'm lost.
try {
const newListofMembers = currentMembers.members.filter(
member => member._id !== memberToRemove
);
const updatedMembers = await Group.findOneAndUpdate(
{ _id: groupId },
{ members: newListofMembers }
);
console.log('Users successfully updated.');
return res.status(200).json({ success: true, members: newListofMembers });
} catch (err) {
console.log(err);
next(err);
}
When I perform this action nothing happens, when I console.log(newListOfMembers) the filter doesn't seem to work at all, it ignores the member => member !== memberToRemove. The member to remove is 6238a608170aff10d16ccd89.
Simplest, you can change you filter into this member => String(member._id) === memberToRemove
But the right way is
const _ = require('lodash');
const { Types } = require('mongoose');
const newListofMembers = currentMembers.members.filter(
member => !_.isEqual(member._id, Types.ObjectId(memberToRemove))
);
You can replace member => !_.isEqual(member._id, Types.ObjectId(memberToRemove)) with member => !_.isEqual(member, Types.ObjectId(memberToRemove)) or !item.equals(memberToRemove)

How to push data with Mongoose to a nested array in MongoDB

I'm trying to push data to a nested array in mongodb. I'm using mongoose as well.
This is just mock code to see if i can get it working:
User model:
import mongoose from "mongoose";
const CoinSchema = new mongoose.Schema({
coinID: { type: String },
});
const CoinsSchema = new mongoose.Schema({
coin: [CoinSchema],
});
const WatchlistSchema = new mongoose.Schema({
watchlistName: { type: String },
coins: [CoinsSchema],
});
const NameSchema = new mongoose.Schema({
firstName: { type: String },
lastName: { type: String },
username: { type: String },
});
const UserSchema = new mongoose.Schema({
name: [NameSchema],
watchlists: [WatchlistSchema],
test: String,
});
const User = mongoose.model("User", UserSchema);
export default User;
route:
fastify.put("/:id", async (request, reply) => {
try {
const { id } = request.params;
const newCoin = request.body;
const updatedUser = await User.findByIdAndUpdate(id, {
$push: { "watchlists[0].coins[0].coin": newCoin },
});
await updatedUser.save();
// console.dir(updatedUser, { depth: null });
reply.status(201).send(updatedUser);
} catch (error) {
reply.status(500).send("could not add to list");
}
});
request.body // "coinID": "test"
I've tried a lot of different ways to push this data but still no luck. I still get 201 status codes in my terminal which indicates something has been pushed to the DB, but when I check nothing new is there.
Whats the correct way to target nested arrays and push data to them?
It's not perfect but you could get the user document, update the user's watchlist, and then save the updated watchlist like so:
fastify.put("/:id", async (request, reply) => {
try {
const { id } = request.params;
const newCoin = request.body;
// get the user
let user = await User.findById(id);
// push the new coin to the User's watchlist
user.watchlists[0].coins[0].coin.push(newCoin);
//update the user document
const updatedUser = await User.findOneAndUpdate({ _id: id },
{
watchlists: user.watchlists,
},
{
new: true,
useFindAndModify: false
}
);
reply.status(201).send(updatedUser);
} catch (error) {
reply.status(500).send("could not add to list");
}
});

Populate and exepopulate didn’t work in mongoose.js and Node.js project

I have this route, but it didn't give a result which I had expected. It gave the 200 code as a response.
And a console.log statement before populate is working, but after it is not. What did I miss to do or how should I be working with populate?
For more info :
In user model
userSchema.virtual('wishes',{
ref:'Wishes',
localField:"_id",
foreignField:"owner"
})
In wishes model :
owner:{
type:monoogse.Schema.Types.ObjectId,
required:true,
ref:'users'
}
The route :-
router.get("/wishlist", auth, async(req, res) => {
const sort = {}, match = {};
if(req.query.sortBy)
{
const parts = req.query.sortBy.split(':');
sort[parts[0]] = parts[1] === 'dec' ? -1 : 1;
}
try
{
console.log("likes")
// const tasks = await Task.find({owner: req.user._id});
await req.user.populate(
{
path: 'wishes',
// match,
// options:{
// limit: parseInt(req.query.limit),
// skip: parseInt(req.query.skip),
// sort
// }
}).execPopulate();
console.log(req.user.likes, "likes")
console.log("likes2")
res.send(req.user.likes)
}
catch(e)
{
res.send(e)
}
})

Express & Nodejs : How to call 'next()' only after have created schemas

i'm build an event app, and in my 'Event' schema i've an array of 'Tag's schemas, so each event can have one or more tags.
Event:
var EventSchema = new Schema({
...
tags: [{
type: Schema.Types.ObjectId,
ref: 'Tag'
}],
...
}
And Tag:
var TagSchema = new Schema({
name:{
type: String,
require: true
},
times:{
type: Number,
default: 0
}
});
When a user wants to create an event it sends a json to the /POST in the event middleware with all the information regarding the event and an array composed by
//json sent by client to server
{tags:[{name:tag1},{name:tag2}]
Since two events can't have the same name, in a specific middleware i check if some users has already created the tag or we need to actually store one.
// add the tags
addTags(req, res, next) {
var myBody = req.body;
if (myBody.tags) {
const len = myBody.tags.length
if (len > 0) {
// we need to search and store a tag if is has not already created
for (let i = 0; i < len; i++) {
let currentTag = myBody.tags[i]
// find the currentTag in the DB
Tag.findOne({
name: currentTag.name
}, (err, find) =>{
if (err) return next(err)
// if we not find it
else if (!find) {
// create new one
let newTag = new Tag({
name: myBody.tags[i].name
})
utils.saveModel(newTag, next, (saved) => {
// store it back the ref
req.Event.tags.push(saved._id)
})
} else {
// store the ref
req.Event.tags.push(find._id)
}
})
}
console.log('tags added!.');
next()
}
} else {
next()
}
},
My problem is, how can i call the 'next' only after i've checked all the tags? Is it possible? Thank you
You can use Promise.all to wait for an array of promises to be fulfilled.
Code is untested but should give you the outline of a Promise solution.
mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
// Promise to add a new tag
function addTag(req, currentTag) {
let newTag = new Tag({
name: currentTag.name
})
return newTag.save()
.then( (saved) => {
// Store it back the ref
return req.Event.tags.push(saved._id)
})
}
// Promise to find a tag or add it.
function findTagOrAdd(req, currentTag) {
return Tag.findOne({ name: currentTag.name})
.then( (find) => {
if ( find ) return req.Event.tags.push(find._id);
// Otherwise create new one
return addTag(req, currentTag);
})
}
// Promise to add all tags.
function addTags(req, res, next) {
var myBody = req.body;
if ( ! myBody.tags ) return next();
if ( ! Array.isArray(myBody.tags) ) return next();
if ( myBody.tags.length <= 0 ) return next();
// Promise to find the currentTag in the DB or add it.
var promised_tags = [];
myBody.tags.forEach( (currentTag) => {
promised_tags.push( findTagOrAdd(req, currentTag) )
}
// Wait for all the tags to be found or created.
return Promise.all(promised_tags)
.then( (results) => {
console.log('tags added!.', results);
return next();
})
.catch(next);
}
You probably should use promises, but if you don't want to change your current approach, you can do it the old fashioned way, by counting called callbacks:
function addTags(req, res, next) {
var myBody = req.body
if (!myBody.tags || !myBody.tags.length) {
next()
}
let errorOccured = false
let checkedTags = 0
for (let currentTag of myBody.tags) {
Tag.findOne({ name: currentTag.name }, (err, find) => {
if (errorOccured) {
return
}
if (err) {
errorOccured = true
return next(err)
}
checkedTags += 1
if (!find) {
let newTag = new Tag({ name: currentTag.name })
utils.saveModel(newTag, () => {}, (saved) => {
req.Event.tags.push(saved._id)
if (checkedTags === myBody.tags.length) {
next()
}
})
} else {
req.Event.tags.push(find._id)
if (checkedTags === myBody.tags.length) {
next()
}
}
})
}
}

Categories