Post to a mongoose schema with array of objects - mongodb

I want to post some data to my mongo database.
However the structure of the schema confuses me about the implementation.
This is the schema:
var GraphSchema = new Schema({
nodes: [{id: String}],
links: [{source:String, target: String}]
});
This is what I've tried so far but it doesn't seem to working:
router.post('/graphs', (req, res) => {
const graph = new Graph();
graph.nodes = [{id: req.body.nodes.id}];
graph.links = [{source: req.body.source, target: req.body.target}];
graph.save((err) => {
if(err) return res.status(500).json({ message: 'internal error' })
res.json({ message: 'saved...' })
})
});
For example I want to achieve something like this as a final result:
{
"data": [
{
"nodes": [
{
"id": "root"
},
{
"id": "input"
},
{
"id": "component"
}
],
"links": [
{
"source": "component",
"target": "root"
}
]
}
]
}
I a testing the operation with Postman
I am in a kind of dead end regarding how to proceed so I hope you can hint me something!

in your creation of the object , creat it like this
router.post('/graphs', (req, res) => {
const graph = new Graph({
nodes:[{id:req.body.nodes.id}],
links:[{source: req.body.source, target: req.body.target}]
}); // you need to include your data inside the instance of the model when you create it that was the problem.. It should work fine now
In your code you don't actually create the array that you have defined in your schema. So tally with your schema like above and then save. below
graph.save((err) => {
if(err) {
res.status(500).json({ message: 'internal error' });
throw err;
}else{
res.send({ message: 'saved...' });
}
})
});
this is the way you have currently posted the question.. so the answer is valid for that, but I assume this should be sufficient enough for you to figure out what was the problem ..

Related

How to return the a formatted response from a mongo query/projection?

I'm trying to create an API to validate a promocode. I have minimal experience with mongo and the backend in general so I'm a bit confused in what is the best approach to do what I'm trying to accomplish.
I have this PromoCode form in the client. When a user types a promocode I would like for my backend to
verify if the code exists in one of the docs.
if it exists then return that code, the value for that code and the couponId
if the code doesn't exist then return an error.
My db is structured like this. The user will type one of those codes inside the codes: []
{
"_id": {
"$oid": "603f7a3b52e0233dd23bef79"
},
"couponId": "rate50",
"value": 50,
"codes": ["K3D01XJ50", "2PACYFN50", "COKRHEQ50"]
},
{
"_id": {
"$oid": "603f799d52e0233dd23bef78"
},
"couponId": "rate100",
"value": 100,
"codes": ["rdJ2ZMF100", "GKAAYLP100", "B9QZILN100"]
}
My route is structure like this:
router.post('/promoCode', (req, res, next) => {
const { promoCode } = req.body;
console.log('this is the req.body.promoCode on /promoCode', promoCode)
if (!promoCode) {
throw new Error('A promoCode needs to be passed')
}
promoCodesModel
.validatePromoCode(req.body.promoCode)
.then((response) => {
console.log('response inside /promoCode', response)
res.status(200).json({ data: response })
})
.catch((error) => {
res.status(400).json({ result: 'nok', error: error })
})
})
The validatePromoCode function is the following:
const validatePromoCode = async (code) => {
try {
let promoCode = await PromoCodesModel.find(
{"codes": code},
{_id: 0, codes: { $elemMatch: { $eq: code }} })
console.log('This is the promocode', promoCode)
return promoCode
} catch (err) {
throw new Error (err.stack)
}
}
All this seems to sort of work since I get the following response when the code is typed correctly
{
"data": [
{
"codes": [
"COKRHEQ50"
]
}
]
}
when typed incorrectly I get
{
"data": []
}
What I would like to get back is. (How can I accomplish this ?). Thanks
// when typed correctly
{
"data": { value: 50, couponId: "rate50", code: "COKRHEQ50" }
}
// when typed incorrectly
{
"error": "this is not valid code"
}
TL;DR: I would like to return a formatted query with specific values from a mongo query or an error object if that value does not exist on the document object.
Ok just figured it out
To be able to get the this responsed (what I wanted):
{
"data": [
{
"codes": [
"K3D01XJ50"
],
"couponId": "rate50",
"value": 50
}
]
}
I ended up having to do this on validatePromoCode
onst validatePromoCode = async (code) => {
try {
let promoCode = await PromoCodesModel.find(
{ codes: code },
{ _id: 0, codes: { $elemMatch: { $eq: code } }, couponId: 1, value: 1 },
)
return promoCode
} catch (err) {
throw new Error(err.stack)
}
}
But is there a better way on doing this ? Thanks

Mongoose populate method not populating all fields after updating document

I change the order of an array and then populate doesn't work. It still works on the documents that I did not try to update.
I have a seccion model with preguntas inside of it
SECCION MODEL
const Schema = mongoose.Schema;
const seccionSchema = new Schema({
titulo: { type: String },
preguntas: [
{
type: Schema.Types.ObjectId,
ref: 'Pregunta',
},
],
});
PREGUNTA MODEL
const Schema = mongoose.Schema;
const preguntaSchema = new Schema({
titulo: { type: String, required: true },
});
A "Seccion" document with 2 "preguntas" looks something like this:
{
"preguntas": [
"5fb0a48ccb68c44c227a0432",
"5fb0a48ccb68c44c227a0433"
],
"_id": "5fb0a50fcb68c44c227a0436",
"titulo": "Seccion 2",
"__v": 3
}
What I want to do is change the order in the preguntas array, so that the _ids end up being [33, 32] instead of [32, 33] (last two digits of the _ids). I can update using ".save()" method or ".findOneAndUpdate()". Here is the code using "findOneAndUpdate":
router.put('/:id', async (req, res) => {
const seccionId = req.params.id;
try {
const seccion = await Seccion.findOneAndUpdate(
{ _id: seccionId },
{ preguntas: ['5fb0a48ccb68c44c227a0433', '5fb0a48ccb68c44c227a0432'] },
{ new: true }
);
res.json(seccion);
} catch (err) {
res.json({ message: err });
}
});
The result works (and the order is changed in mongo atlas):
{
"preguntas": [
"5fb0a48ccb68c44c227a0433",
"5fb0a48ccb68c44c227a0432"
],
"_id": "5fb0a50fcb68c44c227a0436",
"titulo": "Seccion 2",
"__v": 4
}
But when I try to use the "populate()" method of mongoose, only one of the "preguntas ids" or none are populated (below only the "pregunta" with id ending in 32 was populated):
{
"preguntas": [
{
"_id": "5fb0a48ccb68c44c227a0432",
"titulo": "Pregunta 3",
"__v": 0
}
],
"_id": "5fb0a50fcb68c44c227a0436",
"titulo": "Seccion 2",
"__v": 4
}
So my guess is that the populate method is not working correctly, but it does work on the other "Seccion" documents where I have not tryed to change the order of the "preguntas" inside the array. Here is the code that uses the "populate" method:
router.get('/:id', async (req, res) => {
const seccionId = req.params.id;
try {
const seccion = await Seccion.findById(seccionId).populate('preguntas');
res.json(seccion);
} catch (err) {
res.json({ message: err });
}
});
It has been really hard to make the populate work, I really appreciate anyone's help
Are you sure that pregunta of id 5fb0a48ccb68c44c227a0433 exists in the preguntas collection?
The way populate works is that it does a second find operation on the preguntas collection with the ids in the array, if a document is not found, it will be omitted from the array.
The issue is probably that the one ending with 33 does not exist in the preguntas collection.

Update single specific field only when using $ in embedded document Mongodb

Here's my snippet of code.
router.post(
"/chapter/officers/edit/:id/:c_id",
async (req, res) => {
try {
const updateChapter = await Council.findOneAndUpdate(
{ "chapters._id": req.params.c_id },
{
"chapters.$.officers": req.body,
}
);
if (!updateChapter) return res.status(404).json({ msg: "Not found" });
res.json("Edit Success");
} catch (error) {
res.status(500).json({ msg: error.message });
}
}
);
I am planning on updating only one specific field at a time BUT when I tried to send this JSON from Postman
{
"grandTriskelion": "sample"
}
The other filled in field values becomes an empty string. Heres an example of my res.json
"_id": "5fc9cbb7ba7e2e2430c9a4d8",
"name": "Maria Aurora",
"code": "MA",
"chapters": [
{
"officers": {
"grandTriskelion": "sample",
"deputyGrandTriskelion": "",
"masterWilderOfTheWhip": ""
},
"_id": "5fca014e49fa3f2910794bb8",
"name": "Maria Aurora Community Based"
}
],
I've hit a roadblock. I'm a beginner at MERN stack.

mongoose When I Use update it updates Nothing with status 200(success)

I use update Query for push some data in array in Mongodb and I use mongoose in nodeJs.Pplease anyone can help out from this.
Model Schema :
var mongoose = require('mongoose')
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt')
var schema = new Schema({
email: { type: String, require: true },
username: { type: String, require: true },
password: { type: String, require: true },
creation_dt: { type: String, require: true },
tasks : []
});
module.exports = mongoose.model('User',schema)
So I use this schema and I want to push data in tasks array and here is my route code for pushing data.
Route For Update Data in Tasks:
router.post("/newTask", isValidUser, (req, res) => {
addToDataBase(req, res);
});
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime,
};
var usr = new User(req.user);
usr.update({ email: req.user.email }, { $push: { tasks: dataa } });
console.log(req.user.email);
try {
doc = await usr.save();
return res.status(201).json(doc);
} catch (err) {
return res.status(501).json(err);
}
}
Here I create a async function and call that function in route but when I post data using postman it response with status code 200(success) but it updates nothing in my database.
Output screenshot:
as you can see in this image task : [].. it updates nothing in that array but status is success
I don't know why is this happening.
You can achieve this task easier using findOneAndUpdate method.
router.put("/users", isValidUser, async (req, res) => {
var data = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime,
};
try {
const user = await User.findOneAndUpdate(
{ email: req.user.email },
{
$push: {
tasks: data,
},
},
{ new: true }
);
if (!user) {
return res.status(404).send("User with email not found");
}
res.send(user);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
Also I strongly suggest using raw / JSON data for request body, that's how most ui libraries (reactjs, angular) send data.
To be able to parse json data, you need to add the following line to your main file before using routes.
app.use(express.json());
TEST
Existing user:
{
"tasks": [],
"_id": "5e8b349dc285884b64b6b167",
"email": "test#gmail.com",
"username": "Kirtan",
"password": "123213",
"creation_dt": "2020-04-06T14:21:40",
"__v": 0
}
Request body:
{
"pName": "pName 1",
"pTitle": "pTitle 1",
"pStartTime": "pStartTime 1",
"pEndTime": "pEndTime 1",
"pSessionTime": "pSessionTime 1"
}
Response:
{
"tasks": [
{
"pName": "pName 1",
"pTitle": "pTitle 1",
"pStartTime": "pStartTime 1",
"pEndTime": "pEndTime 1",
"pSessionTime": "pSessionTime 1"
}
],
"_id": "5e8b349dc285884b64b6b167",
"email": "test#gmail.com",
"username": "Kirtan",
"password": "123213",
"creation_dt": "2020-04-06T14:21:40",
"__v": 0
}
Also as a side note, you had better to create unique indexes on username and email fields. This can be done applying unique: true option in the schema, but better to create these unique indexes at mongodb shell like this:
db.users.createIndex( { "email": 1 }, { unique: true } );
db.users.createIndex( { "username": 1 }, { unique: true } );
It's been awhile since I've done mongoose, but I'm pretty sure <model>.update() also actively updates the record in Mongo.
You use .update() when you want to update an existing record in Mongo, but you are instantiating a new User model (i.e. creating a new user)
try the following code instead for a NEW USER:
router.post('/newTask', isValidUser, (req, res) => {
addToDataBase(req,res)
})
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime
}
// email field is already in `req.user`
var usr = new User({ ...req.user, tasks: [dataa] });
console.log(req.user.email);
try {
await usr.save();
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
Now, if you wanted to update an existing record :
router.post('/newTask', isValidUser, (req, res) => {
addToDataBase(req,res)
})
async function addToDataBase(req, res) {
var dataa = {
pName: req.body.pName,
pTitle: req.body.pTitle,
pStartTime: req.body.pStartTime,
pEndTime: req.body.pEndTime,
pSessionTime: req.body.pSessionTime
}
try {
await usr. updateOne({ email : req.user.email}, { $push: { tasks: dataa } });
return res.status(201).json(doc);
}
catch (err) {
return res.status(501).json(err);
}
}
For more info read: https://mongoosejs.com/docs/documents.html

Updated date field is not updated

I have defined this schema
var docSchema = mongoose.Schema({
name:{type:String,required:true},
}, { timestamps: { createdAt: 'createdAt',updatedAt:'updatedAt' }, collection : 'docs', discriminatorKey : '_type' });
I update the documents using this route
router.post('/:id', auth, function(req,res,next) {
var id = req.params.id;
docA.findByIdAndUpdate(id, req.body, {new: true}, function(err, doc) {
if(err)
res.json(err);
else if(doc==null)
res.status(404).send({
message: "Document not found"
});
else
res.json(doc);
});
});
I noticed updatedAt is not updated when I save some edits to the documents.
Besides this problem, thinking about it, it could be helpful to keep this data in form of array of updated date like:
updatedAt : [
"2016-10-25T12:52:44.967Z",
"2016-11-10T12:52:44.967Z",
"2016-12-01T12:52:44.967Z"
]
SOLUTION(?):According to #chridam suggestions, my current workaround to keep an array of update Dates is:
docSchema.pre(`findOneAndUpdate`, function(next) {
if(!this._update.updateHistory) {
console.log("findOneAndUpdate hook: updateHistory not present")
this._update.updateHistory=[];
}
this._update.updateHistory.push(new Date);
return next();
});
docSchema.pre('save', function (next) {
if(!this.updateHistory) {
console.log("Save hook: updateHistory not present")
this.updateHistory=[];
}
this.updateHistory.push(new Date);
next();
});
This is a known issue, please refer to the original thread on the plugin here, where dunnkers commented:
It's actually impossible to hook middleware onto update,
findByIdAndUpdate, findOneAndUpdate, findOneAndRemove and
findByIdAndRemove in Mongoose at the moment.
This means that no plugin is actually run when using any of these
functions.
Check out the notes section in the Mongoose documentation for
middleware. Issue Automattic/mongoose#964 also describes this.
As a suggested workaround, factoring in your schema changes:
var docSchema = mongoose.Schema({
"name": { "type": String, "required": true },
"updateHistory": [Date]
}, {
"timestamps": {
"createdAt": 'createdAt',
"updatedAt": 'updatedAt'
},
"collection" : 'docs',
"discriminatorKey": '_type'
});
router.post('/:id', auth, function(req,res,next) {
var id = req.params.id;
docA.findByIdAndUpdate(id, req.body, {new: true}, function(err, doc) {
if(err)
res.json(err);
else if(doc==null)
res.status(404).send({
message: "Document not found"
});
else {
doc.updateHistory.push(new Date());
doc.save().then(function(doc){
res.json(doc);
}, function(err) {
// want to handle errors here
})
}
});
});
Another approach would be to attach a hook to the schema:
docSchema.pre("findOneAndUpdate", function() {
this.updatedAt = Date.now();
});