I am trying to send a string and number values upon hitting submit which is to be stored in a database collection (log collection).
I am making use of the MERN stack. I have tried sending it vis Axios, but when I console log the "req.params" in the backend, I get undefined. why am I not getting the typed in value of "order" at the backend? I am sure there is something I am not doing right, but I can figure it out. I need help.
handleSubChange(id, quantity){
// these lines of code below gets the inputted values and then subtracts from the total quantity
if ((parseInt(quantity) - parseInt(this.state.minus))<0) {
alert('The input value is too high.');
}else{
var nums = parseInt(quantity)-parseInt(this.state.minus);
// and just before I send the value of "nums" via Axios, I get the user to type in destination for the item and add the inputted value of "order" on the Axios URL
const order = prompt("Please enter item destination")
axios.get("/api/inventory/add_product/" + id + "/" + nums + "/" + parseInt(quantity) + "/" + order)
.then(res => {
console.log(res);
})
.catch(err => {
console.log(err);
})
}
}
Here is the backend route
router.get("/add_product/:id/:num/:quantity/:order",(req,res) => {
var id = req.params.id;
var quantity = req.params.quantity;
//i then console log req.params.order, i get undefined
console.log(req.params.order)
// console.log('id----', id);
var num_mod = req.params.num;
var modified_count = parseInt(num_mod) - parseInt(quantity);
console.log('num_mod----', num_mod);
Inventory.findByIdAndUpdate(id,{quantity: parseInt(num_mod)},{new:true},function(err,inventory){
if (err){
console.log("err",err);
res.status(500).send(err);
} else {
console.log(inventory.name);
console.log(inventory.order)
const newLog = new Log({
name:inventory.name,
description:inventory.description,
price:parseInt(inventory.price),
quantity:parseInt(inventory.quantity),
modified_quantity: parseInt(modified_count),
order:inventory.order,
});
newLog.save(function(err, Log) {
if (err){
console.log(err);
} else{
console.log('add log success');
res.send(inventory);
}
});
}
});
});
Inventory model
const mongoose= require('mongoose');
//create Schema
const InventorySchema = new mongoose.Schema({
// _id: mongoose.Schema.Types.ObjectId,
name : { type: String, required: true },
description : { type: String, required: true },
price : { type: Number, required: true },
quantity : { type: Number, required: true },
supplier : String,
taxable : Boolean,
order:String,
// Create model from the schema
const Inventory = mongoose.model("Inventory", InventorySchema);
// Export model
module.exports = Inventory;
The issue is your backend route definition, which is missing a / before :order.
router.get("/add_product/:id/:num/:quantity:order",(req,res) => {
// ^ missing /
Change to:
router.get("/add_product/:id/:num/:quantity/:order",(req,res) => {
// ^ correct
Related
exports.addToCart = async(req,res)=>{
const cart = await schema.cart.findOne({username:req.body.username})
if(cart){
return res.status(404).json({
message:"User's cart is already available, append to the same cart"
})
}
else{
const cart = new schema.cart({
cartId : getValueForNextSequence("item_id"),
username : req.body.username,
productsInCart :req.body.productsInCart
});
console.log(cart.cartId);
await cart.save();
res.status(200).json(cart)
}
}
async function getValueForNextSequence(sequenceOfName){
const sequenceDoc = await schema.counter.findOneAndUpdate(
{"_id": sequenceOfName },
{"$inc":{"sequence_value":1}},
);
return sequenceDoc.sequence_value;
}
THis is the schema for counter I added a document with _id as item_id and sequence_value as 0
const counterSch = new mongoose.Schema({
_id :{
type : String
},
sequence_value:{
type : Number
}
})
getValueForNextSequence method is not returning any value I dont know why please help with this issue.Here I have to increment the cartId automatically but its not happening
Thank you for taking out time to read this.
I have two models positions and users. I'm trying to add 'users' to the Array of 'Recruiters' as seen below in positions Model. When I make the put request, Everything goes well but my amended array which includes the new userids fail to save and give the following error.
'Cast to [ObjectId] failed for value "[3]" at path "recruiters"'
PositionsModel.js
const positionsSchema = new Schema(
{
title: {
type: String,
required: [true, 'Position title is required'],
},
description: {
type: String
},
recruiters: [{
type: Schema.Types.ObjectId,
ref: "users"
}]
},
{ timestamps: true }
);
usersModel.js
const usersSchema = new Schema(
{
name: {
type: String,
required: [true, "Name must be provided"],
},
email: {
type: String
},
password: {
type: String,
}
},
{ timestamps: true }
);
Controller.js (Problem Here)
I'm making a put request to add recruiter to the Array in Positions Model and sending two parameters. Id (this is the position id) and Recruiter (this is the userId)
exports.addRemove = async (req, res, next) => {
try {
const {id, recruiter} = req.params;
//Get Current Position Details
const position = await positionsModel.findById(id)
// Update Position
const newList = position.recruiters.push(recruiter) //this works, It adds the id to array
const newData = {
recruiters: newList
}
//At this point if you console log position.recruiters. You will see the newly added item in the array
const uptPosition = await positionsModel
.findByIdAndUpdate(id, newData, {
new: true,
runValidators: true,
})
.exec(); // this fails with error
if(!uptPosition) {
return res.status(400).json("Position failed to update");
}
//Success Response
return res.status(200).json('Position Updated');
} catch (err) {
console.log({ Error: err.message });
return;
}
Current List of Recruiter Id's in the array
The Current Recruiter Array already has two userIds. The third one gets added successfully to the newList variable, but it doesn't get saved in the database. You can see the error below as it points to the third element that was just added in the controller
'Cast to [ObjectId] failed for value "[3]" at path "recruiters"'
The push() method adds one or more elements to the end of an array and returns the new length of the array.
You did:
const newList = position.recruiters.push(recruiter);
Then newList will be the new length of recruiters array(in your case is 3). You can fix this by changing your code to:
position.recruiters.push(recruiter);
position.markModified('recruiters'); // mark recruiters as having pending change
position.save();
I am trying to update an array within my object. However, every time I send the post call, the index in the array changes.
I have tried using $set and manually updating the array... but the index on the array keeps changing.
Here is the model:
const MappedImageSchema = new Schema({
imageUrl: {type: String, required: true},
name: {type: String, required: true},
areas:[
{
name: {type: String},
shape: {type: String},
coords:[{type: Number}],
}
]
});
module.exports = MappedImage = mongoose.model('mappedImages', MappedImageSchema)
Here is the code that performs the update:
// #route POST api/maps/:id/areas
// #desc add an area to a map (by map id)
// #access Private
router.post('/:id/areas/:id_area', passport.authenticate('jwt', { session: false }),
(req, res) => {
MappedImage.findById(req.params.id)
.then(map => {
// get all of the areas from the map...
var allAreas = map.areas;
// now get the index of the area we are going to update
const areaIndex = map.areas.map(item => item._id.toString()).indexOf(req.params.id_area);
// update the information
var coords = req.body.coords.split(',');
const updatedArea = {
name: req.body.name,
shape: req.body.shape,
coords: coords,
};
// set the updated information in the correct map area
allAreas[areaIndex] = updatedArea;
var query = {_id: req.params.id}; // this is the MAP id
var update = {$set: {areas:allAreas}}; // update the areas
var options = {new: true};
MappedImage.findOneAndUpdate(query, update, options)
.then(map => res.json(map))
.catch(err => res.status(404).json({ mapnotfound: err }));
})
.catch(err => res.status(404).json({ mapnotfound: 'Map not found while updating area' }));
}
);
Here is the data BEFORE the call
{
"_id": "5c5c69dda40e872258b4531d",
"imageUrl": "url test",
"name": "name test",
"areas": [
{
"coords": [1,2,3,4,5,6],
"_id": "5c5c8db2f904932dd8d4c560", <---- _id changes every time !
"name": "area name",
"shape": "poly"
}
],
"__v": 3
}
Here is the Postman call I make:
The result of the call is the name gets changed... but so does the index... making the next call fail with "no area found with that index".
What is perplexing about this problem is the _id for the map does not get updated when I run this code:
router.post('/:id', passport.authenticate('jwt', { session: false }),
(req, res) => {
var query = {_id: req.params.id};
var update = {imageUrl: req.body.imageUrl, name: req.body.name};
var options = {new: true};
MappedImage.findOneAndUpdate(query, update, options)
.then(map => res.json(map))
.catch(err => res.status(404).json({ mapnotfound: err }));
});
Update 1
I tried using the areas index and updating just that area... but the _id changes with this code as well:
... same code all the way down to here
allAreas[areaIndex] = updatedArea;
// but instead of calling 'findOneAndUpdate'... call map save
map.save().then(map => res.json(map));
Update 2
I can't get this code to work as areas._id and areas.$ are undefined ?
var query = {_id: req.params.id, areas._id: id_area}; // this is the MAP id
var update = {$set: {areas.$: updatedArea}}; // update the area
Update 3
So, putting the _id in the updatedArea fixes this issue... but it "feels" wrong to do so: ( per eol answer )
const updatedArea = {
_id: req.params.id_area,
name: req.body.name,
shape: req.body.shape,
coords: coords,
};
Update 4
eol - thanks for the verification on the mongoDB side... If that solves the DB id problem... I just need to know why my query is failing. I tried this and all I see in the terminal output is "creating query"... I never see the "query" and it's definition... so something is wrong and I don't know how to figure out what. Here is what I have now:
console.log('creating query');
var query = {"_id": req.params.id, "areas._id": id_area};
console.log('query');
console.log(query);
Update 5
Figured it out why the query not being output, id_area is not defined... but req.params.id_area is !
console.log('creating query');
var query = {"_id": req.params.id, "areas._id": req.params.id_area};
console.log('query');
Update 6
Code is in... but it is still not working. A picture is worth a 1000 words... so here are two:
This one shows the areas ID is still changing:
Here is the code I have now:
console.log('Update area');
console.log('changing area ' + req.params.id_area);
//console.log(req.body);
const { errors, isValid } = mapValidators.validateAreaInput(req.body);
// Check Validation
if(!isValid){
return res.status(400).json(errors);
}
MappedImage.findById(req.params.id)
.then(map => {
// Check to see if area exists
if (
map.areas.filter(
area => area._id.toString() === req.params.id_area
).length === 0
) {
return res.status(404).json({ areanotfound: 'Area does not exist' });
}
console.log('area exists');
// get all of the areas from the map...
var allAreas = map.areas;
console.log('all areas');
console.log(allAreas);
// now get the index of the area we are going to update
const areaIndex = map.areas.map(item => item._id.toString()).indexOf(req.params.id_area);
console.log('area index');
console.log(areaIndex);
// update the information
var coords = req.body.coords.split(',');
const updatedArea = {
name: req.body.name,
shape: req.body.shape,
preFillColor: req.body.preFillColor,
fillColor: req.body.fillColor,
coords: coords,
};
console.log('updated area');
console.log(updatedArea);
// set the updated information in the maps areas
allAreas[areaIndex] = updatedArea;
console.log('creating query');
var query = {"_id": req.params.id, "areas._id": req.params.id_area};
console.log('query');
console.log(query);
var update = {$set: {"areas.$": updatedArea}};
console.log('update');
console.log(update);
var options = {new: true};
MappedImage.findOneAndUpdate(query, update, options)
.then(map => res.json(map))
.catch(err => res.status(404).json({ mapnotfound: err }));
})
.catch(err => res.status(404).json({ mapnotfound: 'Map not found while updating area' }));
Here is the terminal output:
You could try setting the _id property in the updatedArea object with the value of the area that you'd like to update. This would prevent creating a new id while using the $set operator. Something like this:
// now get the index of the area we are going to update
const areaIndex = map.areas.map(item => item._id.toString()).indexOf(req.params.id_area);
// update the information
var coords = req.body.coords.split(',');
const updatedArea = {
_id: id_area,
name: req.body.name,
shape: req.body.shape,
coords: coords,
};
...
Note that with the above solution you're always setting a new array, which is why new id's are generated.
You could also try updating the specific element in the array using the $ operator:
var query = {"_id": req.params.id, "areas._id": id_area}; // this is the MAP id
var update = {$set: {"areas.$": updatedArea}}; // update the area
See the screenshots below for an example (executing the commands in the mongodb-shell) where I'm trying to only update the second array element (i.e. with _id 5c5c8db2f904932dd8d4c561)
I am new to NodeJs and MongoDB, i want to insert row with auto increment primary key 'id'. also defined a function called getNextSequence on mongo server.
this is working perfect on Mongodb server
> db.user.insert({
"id" : getNextSequence('user_id'),
"username" : "test",
"email" : "test#test.com",
"password" : "test123"
})
now i want to insert from NodeJs.I have tried this but not working
db.collection('user').insertOne({
id : "getNextSequence('user_id')",
username : query.name,
email: query.email,
password: query.pass
}, function(err, result) {
assert.equal(err, null);
console.log("row insterted ");
callback();
});
Assuming that getNextSequence is a server-script function (i.e. a method you defined and saved via db.system.js.save), it is not callable outside of the server. One way to go is to use eval, which forces the server to evaluate a string as a js code, even though it is not a good practice. Here is an example:
db.eval('getNextSequence(\'user_id\')', function(err, result) {
db.collection('users').insert({
"id" : result,
"username" : "test",
"email" : "test#test.com",
"password" : "test123"
});
});
Another way is to follow the mongo tutorial and to implement the getNextSequence directly in NodeJS. The syntax is pretty much the same:
function getNextSequence(db, name, callback) {
db.collection("counters").findAndModify( { _id: name }, null, { $inc: { seq: 1 } }, function(err, result){
if(err) callback(err, result);
callback(err, result.value.seq);
} );
}
You then use it in your nodeJS code like:
getNextSequence(db, "user_id", function(err, result){
if(!err){
db.collection('users').insert({
"_id": result,
// ...
});
}
});
Note: of course, you need to have set the counters collection as explained in the docs.
You can also use "mongoose-auto-increment".
The code has just 4 lines
var mongoose = require('mongoose');
var autoIncrement = require('mongoose-auto-increment');
autoIncrement.initialize(mongoose.connection);
userSchema.plugin(autoIncrement.plugin, 'user');
example :
npm i mongoose-auto-increment
connections.js :
const mongoose = require('mongoose');
require("dotenv").config;
const uri = process.env.MONGOURL;
mongoose.connect(uri, { useNewUrlParser: true }, (err) => {
if (!err) { console.log('MongoDB Connection Succeeded.') }
else { console.log('Error in DB connection : ' + err) }
});
require('../schema/userSchema');
userSchema.js :
var mongoose = require('mongoose'); // 1. require mongoose
var autoIncrement = require('mongoose-auto-increment'); // 2. require mongoose-auto-increment
var userSchema = new mongoose.Schema({
name: { type: String },
password: { type: String },
email: { type: String, unique: true, required: 'This field is required.' },
});
autoIncrement.initialize(mongoose.connection); // 3. initialize autoIncrement
userSchema.plugin(autoIncrement.plugin, 'user'); // 4. use autoIncrement
mongoose.model('user', userSchema);
To accomplish this, we will create a function that will keep trying to save the document untill it will have been saved with incremented _id
async function retryUntilSave(db, task) {
try {
const index = await db.collection('tasks').find().count() + 1;
const result = await db.collection('tasks').insertOne(Object.assign(task, { _id: index }))
} catch (error) {
if (error.message.includes("_id_ dup key")) {
console.log("ID already exists!")
console.log("Retrying...");
retryUntilSave(db, task)
} else {
console.log(error.message);
}
}
}
We can use task._id: index instead of Object.assign()
finally you can test this by making some concurrent requests
for (let index = 0; index < 20; index++) {
setTimeout(async () => {
await retryUntilSave(db, { title: "Some Task" })
}, 1000);
}
This function will handle easily if two or more tasks submitted at the same time because mogod throws error when we try to insert a document with duplicate _id, then we will retry saving the document again with incremented _id and this process will run until we save the document successfully !
You can also use "mongodb-autoincrement" module of node js. For example:
var autoIncrement = require("mongodb-autoincrement");
exports.yourMethod = function(newData, callback) {
autoIncrement.getNextSequence(db, your-collection-name, function (err, autoIndex) {
newData.id = autoIndex;
//save your code with this autogenerated id
});
}
You can use the below package on a model schema to auto-increment your collection field.
mongoose-auto-increment //you can download it from npm
Here I am not focusing on how to connect MongoDB. I just focus on how you can integrate auto increment in your model/collection/table.
const mongoose = require("mongoose"); //
const autoIncrement = require("mongoose-auto-increment");
const post_schema = new mongoose.Schema({
title: {
type: String,
required: true,
min: 3,
max: 225,
},
slug: {
type: String,
required: true,
},
});
autoIncrement.initialize(mongoose.connection);
post_schema.plugin(autoIncrement.plugin, {
model: "post", // collection or table name in which you want to apply auto increment
field: "_id", // field of model which you want to auto increment
startAt: 1, // start your auto increment value from 1
incrementBy: 1, // incremented by 1
});
module.exports = mongoose.model("post", post_schema);
I'm trying to add a subdocument to a parent schema with Mongoose and MongoDB however I'm being thrown the following error:
TypeError: User is not a constructor
This is based off Mongoose's documentation on subdocuments and I think everything is the same. How can I debug this further?
Router
// Add a destination to the DB
router.post('/add', function(req, res, next) {
let airport = req.body.destination
let month = req.body.month
let id = (req.user.id)
User.findById(id , function (err, User) {
if (err) return handleError(err)
function addToCart (airport, month, id) {
var user = new User ({
destinations: [(
airport = '',
month = ''
)]
})
dog.destinations[0].airport = airport
dog.destinations[0].month = month
dog.save(callback)
res.status(200).send('added')
}
addToCart()
})
console.log(airport)
})
Schema
var destinationSchema = new Schema({
airport: String,
month: String
})
// Define the scheme
var User = new Schema ({
firstName: {
type: String,
index: true
},
lastName: {
type: String,
index: true
},
email: {
type: String,
index: true
},
homeAirport: {
type: String,
index: true
},
destinations: [destinationSchema]
})
User.plugin(passportLocalMongoose)
module.exports = mongoose.model('User', User)
JavaScript is case sensitive about the variable names. You have User model and the User result with the same name.
Your code will work with the following change :
User.findById(id , function (err, user) {
/* ^ use small `u` */
if (err) return handleError(err)
/* rest of your code */
Also keep in mind that further in your code you are declaring another variable named user. You will need to change that to something different.