Storing JSON in MongoDB - mongodb

I m trying to store some data in MongoDB, I am not sure of the type of data that I am being provided with as I am getting it from a formbuilder(https://github.com/kevinchappell/formBuilder) that I am using.
I am getting the data from:
document.getElementById('getJSON').addEventListener('click', function() {
var ans = formBuilder.actions.getData('json', true);
//console.log(ans);
//var ans2 = JSON.parse(ans);
alert(ans);
console.log(ans);
$.ajax({
type: "POST",
data: ans,
url: "/j",
success: function(){
console.log('success');
}
});
document.forms["myForm"].submit();
});
It reaches my back end as so:
//FETCHING THE JSON OF THE CLAUSE FORM CREATED BY THE ADMIN
router.post('/j', function(req, res, next) {
req.session.fdata = req.body; //JSON FETCHED FROM JAVASCRIPT USING AJAX, STORED IN SESSION
if(req.session.fdata==null) //CHECKING IF WE ARE RECEIVING VALUES
{
res.redirect('/admin');
}
else {
mongo.connect(url, function (err, db) {
assert.equal(null, err);
//var jfdata = JSON.parse(req.session.fdata);
db.collection('clauses').insertOne(req.session.fdata, function (err, result) {
console.log('New Clause Added');
console.log(req.session.fdata);
db.close();
});
});
res.redirect('/admin');
}
});
I insert it into the DB and it looks fine in the DB but on retrieval I cant seem to access the inner portions of the data. Is my data in the wrong format? Is it JSON or a JS object?
it looks like so in the DB:(the db is empty before insertion)enter image description here
This is what the console prints
[ { _id: 596de520ef77eb2614cd1e47,
'[\n\t{\n\t\t"type": "number",\n\t\t"label": "Number",\n\t\t"description":
"total number",\n\t\t"placeholder": "0",\n\t\t"className": "form-
control",\n\t\t"name": "number-1500374279764"\n\t}\n]': '' },
{ _id: 596de520ef77eb2614cd1e48 } ]

The data you are trying to save does not seem right to me.
What you are getting is a string of the JSON object.
You have to use JSON.parse to convert it to a proper JSON object.
JSON.parse('[\n\t{\n\t\t"type": "number",\n\t\t"label":"Number",\n\t\t"description": "total number",\n\t\t"placeholder": "0",\n\t\t"className": "form-control",\n\t\t"name": "number-1500374279764"\n\t}\n]')
After that, you can form the data and insert in DB.
var query = {
array : [{"type": "number",
"label": "Number",
"description": "total number",
"placeholder": "0",
"className": "form-control",
"name": "number - 1500374279764"}]
}
db.collection('clauses').insertOne(query, function (err, result)
{
db.close();
});
Let me know if it helps!

Related

mongodb, express.js. Add new doc to array of documents selector is id

I want to add a new document to an array of documents. So I pass in my param which is the _id of the document I want to add to. Then I need to just add it to the array. I thought I had it working but it was actually adding a nested array to that array. I realized this because I am also trying to sort it so newly added documents are at top. So I ended up having to go back and try and fix my add query. As of now it basically just says cannot add values. This is why I have been using mongodb client, express, await.
I have been looking at mongodb manual and trying what they have but cannot get it to work, obviously something wrong with my adding of new document. Anyone see the issue or show me an example? Thanks!
app.post("/addComment/:id", async (request, response) => {
let mongoClient = new MongoClient(URL, { useUnifiedTopology: true });
try {
await mongoClient.connect();
let id = new ObjectId(request.sanitize(request.params.id));
request.body.comments = { $push: {"comments.author": "myTestPOSTMAN - 1", "comments.comment":
"myTestCommPostMan - 1"}};
let selector = { "_id":id };
//let newValues = {$push: {"comments.comment": "myTestCommPostMan - 1", "comments.author":
"myTestPOSTMAN - 1"}};
let newValues = request.body.comments;
let result = await mongoClient.db(DB_NAME).collection("photos").updateOne(selector,
newValues);
if (JSON.parse(result).n <= 0) {
response.status(404);
response.send({error: "No documents found with ID"});
mongoClient.close();
return;
}
response.status(200);
response.send(result);
} catch (error) {
response.status(500);
response.send({error: error.message});
throw error;
} finally {
mongoClient.close();
}
});
Using post man this is what my json looks like and what the array of documents looks like I am trying to add to.
{"comments": [
{
"comment": "pm - test3",
"author": "pm - test4"
}
]
}
do the mongodb connection outside the function, no need to connect and disconnect everytime when function call, don't create unusual variables too much.
for push object you need to provide main key name and assign object to it.
let mongoClient = new MongoClient(URL, { useUnifiedTopology: true });
await mongoClient.connect();
app.post("/addComment/:id", async (request, response) => {
try {
let result = await mongoClient.db(DB_NAME).collection("photos").updateOne(
{ "_id": new ObjectId(request.sanitize(request.params.id)) },
{ $push: { comments: request.body.comments } }
);
if (JSON.parse(result).n <= 0) {
response.status(404).send({ error: "No documents found with ID" });
return;
}
response.status(200).send(result);
} catch (error) {
response.status(500).send({ error: error.message });
}
});

Why is JQuery casting a string to _id for Mongodb in this? (Please read EDIT)

I have a route that adds an image (a meme) like this:
// add new image by URL
app.post('/api/addMeme', function (req, res) {
var meme = new Meme({
title: req.body.title.trim().toLowerCase(),
image: req.body.image,
meta: {
votes: 0,
favs: 0
},
related: []
});
// Save meme to database
meme.save(function (err) {
if (err) throw err;
Meme.find({}, function (err, meme) {
if (err) throw err;
io.emit('new meme', meme);
});
res.send('Succesfully inserted meme.');
});
});
It takes the only two attribute title and image given by client side ajax and add it to my Mongodb database named Meme. Emit the updated database using socket.io. Both title and image are String type. image is suppose to be an URL to an image.
Now, I'm not ashamed to admit it, but my friend trolled my site and sent image = "www.pornhub.com" to this route and it crashed my database/site. Whenever I go and try to retrieve the image by its _id, I get the error:
CastError: Cast to ObjectId failed for value "www.pornhub.com" at path "_id" for model "meme"
EDIT: it looks like the error is actually coming from the route
app.post('/api/vote', function(req, res){
Meme.findOneAndUpdate({_id: req.body.id}, {$inc : {'meta.votes' : 1}}, {new: true}, function (err, meme) {
if (err) throw err;
if (!meme) return res.send('No meme found with that ID.');
io.emit('new vote', meme);
res.send('Succesfully voted meme.');
});
});
where a POST request is updating the database, and there's a cast error where the _id is given as a string?
The client side script that's doing this is
$("#vote").click(function(){
$.ajax({
type: "POST",
url: '/api/vote',
data: {
id: App.meme._id
},
success: function (data, status) {
console.log(data);
}
});
return false;
});
where App is a Express-state exposed data for which meme, the database, lives under.
But this error ONLY occurs on the object with image = "www.pornhub.com". My guess is that somewhere in the HTML, a cross-site href is visiting www.pornhub.com and somehow App is getting distorted? It doesn't fully make sense why id: App.meme._id would give www.pornhub.com as its value.

Handling nested callbacks/promises with Mongoose

I am a beginner with Node.js and Mongoose. I spent an entire day trying to resolve an issue by scouring through SO, but I just could not find the right solution. Basically, I am using the retrieved values from one collection to query another. In order to do this, I am iterating through a loop of the previously retrieved results.
With the iteration, I am able to populate the results that I need. Unfortunately, the area where I am having an issue is that the response is being sent back before the required information is gathered in the array. I understand that this can be handled by callbacks/promises. I tried numerous ways, but I just haven't been successful with my attempts. I am now trying to make use of the Q library to facilitate the callbacks. I'd really appreciate some insight. Here's a snippet of the portion where I'm currently stuck:
var length = Object.keys(purchasesArray).length;
var jsonArray = [];
var getProductDetails = function () {
var deferred = Q.defer();
for (var i = 0; i < length; i++) {
var property = Object.keys(purchasesArray)[i];
if (purchasesArray.hasOwnProperty(property)) {
var productID = property;
var productQuery = Product.find({asin:
productQuery.exec(function (err, productList) {
jsonArray.push({"productName": productList[0].productName,
"quantity": purchasesArray[productID]});
});
}
}
return deferred.promise;
};
getProductDetails().then(function sendResponse() {
console.log(jsonArray);
response = {
"message": "The action was successful",
"products": jsonArray
};
res.send(response);
return;
}).fail(function (err) {
console.log(err);
})
});
I am particularly able to send one of the two objects in the jsonArray array as the response is being sent after the first element.
Update
Thanks to Roamer-1888 's answer, I have been able to construct a valid JSON response without having to worry about the error of setting headers after sending a response.
Basically, in the getProductDetails() function, I am trying to retrieve product names from the Mongoose query while mapping the quantity for each of the items in purchasesArray. From the function, eventually, I would like to form the following response:
response = {
"message": "The action was successful",
"products": jsonArray
};
where, jsonArray would be in the following form from getProductDetails :
jsonArray.push({
"productName": products[index].productName,
"quantity": purchasesArray[productID]
});
On the assumption that purchasesArray is the result of an earlier query, it would appear that you are trying to :
query your database once per purchasesArray item,
form an array of objects, each containing data derived from the query AND the original purchasesArray item.
If so, and with few other guesses, then the following pattern should do the job :
var getProductDetails = function() {
// map purchasesArray to an array of promises
var promises = purchasesArray.map(function(item) {
return Product.findOne({
asin: item.productID // some property of the desired item
}).exec()
.then(function product {
// Here you can freely compose an object comprising data from :
// * the synchronously derived `item` (an element of 'purchasesArray`)
// * the asynchronously derived `product` (from database).
// `item` is still available thanks to "closure".
// For example :
return {
'productName': product.name,
'quantity': item.quantity,
'unitPrice': product.unitPrice
};
})
// Here, by catching, no individual error will cause the whole response to fail.
.then(null, (err) => null);
});
return Promise.all(promises); // return a promise that settles when all `promises` are fulfilled or any one of them fails.
};
getProductDetails().then(results => {
console.log(results); // `results` is an array of the objects composed in getProductDetails(), with properties 'productName', 'quantity' etc.
res.json({
'message': "The action was successful",
'products': results
});
}).catch(err => {
console.log(err);
res.sendStatus(500); // or similar
});
Your final code will differ in detail, particularly in the composition of the composed object. Don't rely on my guesses.

What is the syntax for a mongoose query where I just want one property's value?

I am sending a query to mongoDB using mongoose. The collection is named Step. I want the result of this query to be an array of _id values, one per step. Currently I am getting all of the step objects in their entirety, because req.query isn't defined in this case.
service:
this.getSteps = function() {
return $http({
method: 'GET',
url: '/api/step'
})
.then(function(response) {
return response.data;
});
};
controller:
readStep: function (req, res) {
Step.find(req.query, function(err, result) {
if (err) {
res.status(500).send(err);
}
res.status(200).send(result);
});
}
Set the second parameter of the find query to '_id' to retrieve only the _id of the objects.
Step.find(req.query, '_id', function(err, result) {
This will return data like this:
[{_id: 123}, {_id: 234}]
If you want to get an array of the Step ids on their own, use the javascript map function like so
result = result.map(function(doc) {
return doc._id;
});
which will give you an array like this:
[123, 234]
You'll need to use query.select, something like as shown below:
Step.find(query).select({ "_id": 1}).then(....);
I'm not able to type much because I'm responding from my handheld.
Hope this help!

Can't fetch with Mongoose but working with Mongo shell

I am playing around with Mongoose and Node.
A want to be able to save a Backbone model, and fetch the saved models from Mongo via Mongoose.
I am able to save models but I can't fetch them via Mongoose. I can access them via the Mongo shell with no problems using:
db.users.find()
My code is:
var mongoose = require('mongoose'),
userschema = mongoose.Schema({name: 'string', email: 'string'}),
db = mongoose.createConnection('localhost', 'test'),
User = db.model('User', userschema);
exports.save = function(req, res){
var userobj = req.body,
newuser = new User(userobj);
newuser.save(function(err){
if(err){
res.send(err);
}
else{
res.send(newuser);
}
});
};
exports.fetch = function(req, res){
var users = User.find();
res.send(users);
}
When I send a request and executes my fetch function the server responds with
{
"options": {
"populate": {}
},
"_conditions": {},
"_updateArg": {},
"op": "find"
}
It is like I am not on the right Collection or something.
User.find() doesn't return the results of the query; those are passed to the callback that you provide as a parameter to find.
exports.fetch = function(req, res) {
User.find(function(err, users) {
res.send(users);
});
};
If you don't provide a callback, then a Query object is returned (which is what you're seeing in the response).