Mongoose / Express findByIdAndUpdate does not work - node.js

I tried methods from Stackoverflow and many other sites, but I could not change the data in my database. I am sending the ID of the product in my database to the specified API. However, nothing is returning. The data is also not updated with the data in the new JSON file I entered. I'm testing it using Postman.
This is my API code from my server.js file.
app.put("/api/product/update", (req, res) => {
Product.findByIdAndUpdate(req.params.id, req.body, function (err, doc) {
res.send(doc);
});
Here is my model file:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const productSchema = mongoose.Schema({
name: {
required: true,
type: String,
unique: 1,
//maxlength
},
material: {
type: Array,
default: []
},
price: {
type: Number,
required: true,
maxlength: 255,
},
productType: {
type: Schema.Types.ObjectId,
ref: 'Prodtype',
default: "Other",
},
inStock: {
type: Boolean,
default: true,
}
},{timestamps:true});
const Product = mongoose.model('Product',productSchema);
module.exports = { Product }
Postman: PUT localhost:3000/api/product/update?id=6029812bee921b0c7fb65abf

Based on the information you've provided I'm assuming you're using express as your nodejs server framework.
If you want to access your request body, you need to add a body-parser as a middleware.
$ npm i body-parser
const bodyParser = require('body-parser')
app.use(bodyParser)
app.put("/api/product/update", (req, res) => {
Product.findByIdAndUpdate(req.params.id, req.body, function (err, doc) {
res.send(doc);
});
In addition I would recommend you to not directly write everything you're receiving on the endpoint to your database but to implement some sort of validation.

I solved my problem. When I changed my code as follows, I used req.query instead of req.body.
app.put("/api/product/update",(req, res) => {
Product.findByIdAndUpdate(
{ _id: req.query.id },
{ name: req.body.name, price: req.body.price, inStock: req.body.inStock },
function (err, doc) {
console.log(doc);
if (err) {
console.log(err.message);
} else {
res.send(doc);
}
}
);
});

Related

I'm unable to push my array to mongodb document using node.js

In my previous html code when I submit it sends a post to /comment/:id then the website crashes and outputs MongoError: Unsupported projection option: $push: { comment: { content: "gfdghd" } } in my console. I don't know how to solve it and I hope I can get some help on the issue as I'm a starter with web development.
I want this to work by pushing the array which includes the req.body into a certain mongodb array default collection where it finds the parent post _id. If you need me to elaborate please ask, thanks.
This is my code:
app.js
const Post = require("./models/Post");
mongoose
.connect("secret", {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: true,
})
.then(() => {
console.log("connected to mongodb cloud! :)");
})
.catch((err) => {
console.log(err);
});
app
.post("/comment/:id", authenticateUser, async (req, res) => {
const content = req.body;
// checks for missing fields
if (!content){
return res.send("Please enter all the required credentials!");
}
//This is where I tried to match and then push it to mongodb
Post.update({"_id": ObjectId(req.params.id) }, {
$push: {
comment: content,
}
}, function (error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
});
})
Post Mongoose Schema
Post.js
const mongoose = require("mongoose");
const PostSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
postedAt: {
type: String,
default: new Date().toString()
},
postedBy: {
type: String,
},
warned: {
type: String,
},
comment: [String]
});
module.exports = new mongoose.model("Post", PostSchema);
Everything else works but the array functionality.
I think there are a few mistakes, you didn't await the request and you put "_id" when querying instead of _id.
Another way you could do it too would be using findByIdAndUpdate method.
await Post.findByIdAndUpdate(req.params.id, {
$push: {
comment: content,
},
function(error, success) {
if (error) {
console.log(error);
} else {
console.log(success);
}
},
});

Call a related collection via populate

I try to call a related list of logs for a certain user via Mongoose populate. Who can help me with finishing the response?
These are the schemes:
const logSchema = new Schema({
logTitle: String,
createdOn:
{ type: Date, 'default': Date.now },
postedBy: {
type: mongoose.Schema.Types.ObjectId, ref: 'User'}
});
const userSchema = new Schema({
firstName: {
type: String,
required: true
},
lastName: {
type: String,
required: true
}
logs: { type: mongoose.Schema.Types.ObjectId, ref: 'logs' }
});
mongoose.model('User', userSchema);
mongoose.model('logs', logSchema);
Inspired by the Mongoose documentary (see above) and other questions in relation to this subject I think I got pretty far in making a nice get. request for this user. I miss the expierence to 'translate it' to Express.
const userReadLogs = function (req, res) {
if (req.params && req.params.userid) {
User1
.findById(req.params.userid)
.populate('logs')
.exec((err, user) => {
if (!user) { }); // shortened
return;
} else if (err) {
return; // shortened
}
response = { //question
log: {
user: user.logs
}
};
res
.status(200)
.json(response);
});
} else { }); //
}
};
The response in Postman etc would be something like this:
{
"log": {5a57b2e6f633ce1148350e29: logTitle1,
6a57b2e6f633ce1148350e32: newsPaper44,
51757b2e6f633ce1148350e29: logTitle3
}
First off, logs will not be a list of logs; it will be an object. If you want multiple logs for each user, you will need to store is as an array: logs: [{ type: mongoose.Schema.Types.ObjectId, ref: 'logs' }]
From the Mongoose docs: "Populated paths are no longer set to their original _id , their value is replaced with the mongoose document returned from the database by performing a separate query before returning the results." In other words, in your query user.logs will be the logs document for each user. It will contain all the properties, in your case logTitle, createdOn, and postedBy.
Sending user.logs as json from the server is as easy as: res.json(user.logs). So your query can look like this:
const userReadLogs = function (req, res) {
if (req.params && req.params.userid) {
User1
.findById(req.params.userid)
.populate('logs')
.exec((err, user) => {
if (!user) { }); // shortened
return;
} else if (err) {
return; // shortened
}
res.status(200).json(user.logs)
});
} else { }); //
}
};
I hope this makes it a little bit clearer!

Mongodb and Mongoose error: E11000 duplicate key error index

So I've been working on a project and I finished most of it, but then this error popped up, saying there is something that is undefined, here is the error:
E11000 duplicate key error index: build-a-voting-app.polls.$votedIp_1 dup key: { : undefined }
Here is my code for my create new mongo schema file (polls.model.js)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const pollSchema = new Schema({
title: { type: String, unique: true, required: true },
choices: [
{
title: { type: String, required: true },
count: { type: Number, default: 0 }
}
],
votedIp: [{ type: String, unique: true }],
createdAt: {type:Date, default:Date.now()},
createdBy: String
});
const Poll = mongoose.model('polls', pollSchema);
module.exports = Poll;
Here is the function where I add the inputs
function submitVote(field, res, ip) {
Poll.findOneAndUpdate(
{ choices: { $elemMatch: { title: field } } },
{ $inc: { 'choices.$.count': 1 }, $addToSet: { 'votedIp': ip } },
{ new: true },
function (err, poll) {
if (err) throw err;
res.json({ updated: poll });
}
);
}
Here is how I first created it
var newPoll = new Poll({
title: req.body.title,
choices: choicesArr,
createdBy: req.session.user.username || req.session.user
}).save(function (err, poll) {
if (err) throw err
res.redirect('/mypolls')
});
If you want to see the full code please go to https://github.com/ElisaLuo/Freecodecamp-Build-A-Voting-App
I'm using the ip addresses for checking if the user has voted or not (I'm building a voting app), but right now, I cannot even create a new schema / poll. Does anyone know why the error happens and how I can solve it?
#Elisa l - you may want to read this - mongoose enforce unique attribute on subdocument property
However, I did manage to test with mongoose-mock and the behavior is as expected - test results below (please do check the two versions of votedIp in the test code snippets)
and as described in the MongoDb document referenced in the above link. Mongoose does not enforce the unique integrity, MongoDb does.
With the mocha test below (inserted as snippets, not to run the code but just for better readability, please ignore the messy look of the comments in the code but the permutation and combination had to be worked out!), I did manage to create the mongoose schema by adding a create method in "Poll". please note the change in the schema - votedIp: { type: String, unique: true }, you can change it to array in the test code.
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var pollSchema = new Schema({
title: { type: String, unique: true, required: true },
choices: [
{
title: { type: String, required: true },
count: { type: Number, default: 0 }
}
],
votedIp: { type: String, unique: true },
createdAt: {type:Date, default:Date.now()},
createdBy: String
});
// Below code added by RJv (ie. me :)
var NewPoll = new mongoose.Schema(pollSchema);
NewPoll.statics.create = function (params, callback) {
var newUpdate = new NewPoll(params);
newUpdate.save(function(err, result) {
callback(err, result);
});
return newUpdate;
};
var Poll = mongoose.model('Model', NewPoll);
module.exports = Poll;
var expect = require('chai').expect,mongooseMock = require('mongoose-mock'),proxyquire=require('proxyquire'),
sinon = require('sinon'), chai=require('chai'),sinonChai = require("sinon-chai");chai.use(sinonChai);
var Poll;
before(function(done){
Poll = proxyquire('./Poll', {'mongoose': mongooseMock});
done();
})
describe('model', function() {
/* beforeEach(function (done) {
Poll = proxyquire('./Poll', {'mongoose': mongooseMock});
done();
});*/
it("should be called once",function(done){
setTimeout(done, 15000);
var callback = sinon.spy();
var poll1 = Poll.create({ "title": 'jv', "choices": [{"title":"jv#gmail.com","count":"1"}],
"votedIp":"9.1.2.1","createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
// Below to pass data for votedIp as an array as described in the original schema by Elisa
//"votedIp":[{"add1":"9.","add2":"1.","add3":"2.","add4":"1"}],"createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
//expect(poll1.votedIp[0].add1+poll1.votedIp[0].add2+poll1.votedIp[0].add3+poll1.votedIp[0].add4).equals("9.1.2.1");
expect(poll1.save).calledOnce;
console.log(JSON.stringify(poll1));
expect(poll1.votedIp).equals("9.1.2.1");
done();
});
it('should expect same ip to get added', function(done) {
this.timeout(5000);
setTimeout(done, 15000);
var callback = sinon.spy();//mock(new Poll({ title: 'jv', choices: [{title:"jv#gmail.com","count":"1"}], votedIp:[{ad1:"9.",add2:"1.",add3:"2.",add4:"1"}],createdAt:"25/07/2017",createdBy:"Lisa"}));
var poll = Poll.create({ "title": 'jv', "choices": [{"title":"jv#gmail.com","count":"1"}],
"votedIp":"9.1.2.1","createdAt":"23/07/2017","createdBy":"Lisa"}, callback);
// var poll = Poll.create({ "title": 'jv', "choices": [{"title":"jv#gmail.com","count":"1"}],
// Below to pass data for votedIp as an array as described in the original schema by Elisa
// "votedIp":[{"add1":"9.","add2":"1.","add3":"2.","add4":"1"}],"createdAt":"25/07/2017","createdBy":"Lisa"}, callback);
// expect(poll.votedIp[0].add1+poll.votedIp[0].add2+poll.votedIp[0].add3+poll.votedIp[0].add4).equals("9.1.2.1");
expect(poll.save).calledOnce;
expect(poll.votedIp).equals("9.1.2.1");
//assert(spy.calledOnce);
done();
});
});
Are you calling submitVote multiple times in quick succession? You might be running into https://jira.mongodb.org/browse/SERVER-14322.
The suggested fix for this is to check the error and if one of the calls fails retry it.
https://docs.mongodb.com/manual/reference/method/db.collection.update/#use-unique-indexes

Adding slug to req params url in express

I'm using the 'slug' npm library to give-strings-dashes-for-url-cleanliness. The library works when I console.log() a string, and it's required properly into all of the relevant controllers.
However, I can't figure out how implement slug() properly to format my URLs. The problem I'm having is that a product name might be "Foo Bar Baz Quux", but I can't seem to find the right implementation for slug() without disrupting the connection between the app.js route and the findOne query via mongoose.
app.js
app.get('/market/:product_name', marketController.getProduct);
controller.js
exports.getProduct = function(req, res, next) {
var product_name = req.params.product_name;
// var slugProduct = slug(product_name);
Product.findOne({'name': product_name}, function(err, product) {
if (err) return next(err);
return res.send(product.data);
});
}
Perhaps you could "slugify" a product by defining a slug property while setting up your "Product Schema". I can confirm it works if using mongoose with along mongoose-slug-generator plugin, see link... You could set it up as follows:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const slug = require('mongoose-slug-generator');
// * mongoose slug generator options
const options = {
separator: '-',
lang: 'en',
truncate: 120
};
// * Init mongoose slug generator plugin
mongoose.plugin(slug, options);
const ProductSchema = new Schema({
name: {
type: String,
trim: true
},
slug: {
type: String,
slug: 'name', // genarating slug from multiple properties is allowed ['name', 'brand']
unique: true
},
price: {
type: Number,
required: true
},
brand: {
type: String,
default: 'Apple'
}
});
module.exports = mongoose.model('Product', ProductSchema);
exports.getProduct = function(req, res, next) {
const { product_name } = req.params;
Product.findOne({ slug: product_name }, function(err, product) {
if (err) return next(err);
if (!product) {
return res.status(404).json({
message: 'Product data not found.'
);
}
res.status(200).send(product);
});
}
app.get('/market/:product_name', marketController.getProduct)
// request
const response = await axios.get('/api/market/my-awesome-product')
// response.data
{
"_id": "60fh4d37ac1a1c6f58d6a5f4",
"name": "My Awesome Product",
"slug": "my-awesome-product",
"price": 85.9,
"brand": "Apple"
}

Elasticsearch returns IndexMissingException

I'm trying to implement a search engine in node.js using elasticSearch + mongoose which is elmongo. Whenever i try to run a search api i get
"error": "IndexMissingException[[ads] missing]"
Here's the code
advertisingSchema.js
var mongoose = require('mongoose');
var elmongo = require('elmongo');
var Schema = mongoose.Schema;
var AdSchema = new Schema({
title: String,
description: String,
category: String,
phoneNumber: { type: Number, unique: true},
photos: [{ type: String }],
created: { type: Date, default: Date.now},
price: Number,
password: String
});
AdSchema.plugin(elmongo);
module.exports = mongoose.model('Ad', AdSchema
);
api.js
var Ad = require('../models/advertising');
module.exports = function(app, express) {
var api = express.Router();
Ad.sync(function(err) {
console.log("Check the number sync");
})
api.post('/search', function(req, res) {
Ad.search(req.body, function(err, result){
if(err) {
res.send(err);
return;
}
res.json(result);
});
});
return api;
}
I've done everything correctly, but its just doesn't want to return the search result.
As the error suggest there is no index named 'ads' in your cluster. Index is automatically created unless you have set the property "action.auto_create_index" to false in your elasticsearch configuration. You can create the index programmatically or by running a curl request.
Refer to create index api.

Resources