.push() not pushing into array with mongodb [duplicate] - javascript

I am trying to add status to a response on successful update but I am not able to add the status property to json object of form. Here is my code
apiRouter.post('/forms/update', function(req, res){
if(req.body.id !== 'undefined' && req.body.id){
var condition = {'_id':req.body.id};
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form;
objForm.status = "saved successfully";
return res.send(objForm);
});
}else{
res.send("Requires form id");
}
});
and here is the response that I get, notice status is missing
{
"_id": "5580ab2045d6866f0e95da5f",
"test": "myname",
"data": "{\"name\":3321112,\"sdfsd\"344}",
"__v": 0,
"id": "5580ab2045d6866f0e95da5f"
}
I am not sure what I am missing.

Try to .toObject() the form:
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form.toObject();
objForm.status = "saved successfully";
return res.send(objForm);
});

Mongoose query result are not extensible (object are frozen or sealed), so you can't add more properties. To avoid that, you need to create a copy of the object and manipulate it:
var objectForm = Object.create(form);
objectForm.status = 'ok';
Update: My answer is old and worked fine, but i will put the same using ES6 syntax
const objectForm = Object.create({}, form, { status: 'ok' });
Another way using spread operator:
const objectForm = { ...form, status: 'ok' }

Try changing res.send(objForm) to res.send(JSON.stringify(objForm)). My suspicion is that the the Mongoose model has a custom toJson function so that when you are returning it, it is transforming the response in some way.
Hopefully the above helps.

Create empty object and add all properties to it:
const data = {};
data._id = yourObject._id; // etc
data.status = "whatever";
return res.send(data);

Just create a container.
array = {};
Model.findOneAndUpdate(condition, function(err, docs){
array = docs;
array[0].someField ="Other";
});

Related

How to update an object using for-of loop in node.js [duplicate]

I am trying to add status to a response on successful update but I am not able to add the status property to json object of form. Here is my code
apiRouter.post('/forms/update', function(req, res){
if(req.body.id !== 'undefined' && req.body.id){
var condition = {'_id':req.body.id};
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form;
objForm.status = "saved successfully";
return res.send(objForm);
});
}else{
res.send("Requires form id");
}
});
and here is the response that I get, notice status is missing
{
"_id": "5580ab2045d6866f0e95da5f",
"test": "myname",
"data": "{\"name\":3321112,\"sdfsd\"344}",
"__v": 0,
"id": "5580ab2045d6866f0e95da5f"
}
I am not sure what I am missing.
Try to .toObject() the form:
Form.findOneAndUpdate(condition, req.body, {upsert:true}, function(err, form){
if (err) return res.send(500, { error: err });
var objForm = form.toObject();
objForm.status = "saved successfully";
return res.send(objForm);
});
Mongoose query result are not extensible (object are frozen or sealed), so you can't add more properties. To avoid that, you need to create a copy of the object and manipulate it:
var objectForm = Object.create(form);
objectForm.status = 'ok';
Update: My answer is old and worked fine, but i will put the same using ES6 syntax
const objectForm = Object.create({}, form, { status: 'ok' });
Another way using spread operator:
const objectForm = { ...form, status: 'ok' }
Try changing res.send(objForm) to res.send(JSON.stringify(objForm)). My suspicion is that the the Mongoose model has a custom toJson function so that when you are returning it, it is transforming the response in some way.
Hopefully the above helps.
Create empty object and add all properties to it:
const data = {};
data._id = yourObject._id; // etc
data.status = "whatever";
return res.send(data);
Just create a container.
array = {};
Model.findOneAndUpdate(condition, function(err, docs){
array = docs;
array[0].someField ="Other";
});

fetch a particular value using node js and mongodb

I have to try to fetch a field value from MongoDB using Node.js. But it shows me undefined in my console. My requirement is to print the data in the console or browser from MongoDB using Node.js.
1). This is my node js
this.levelChange = function(req, res, next){
try{
var query = {'level_num':2};
QuizLevel.find(query,function(err,data){
var a = data.min_score;
console.log(a);
res.send(a);
});
}catch(err){
console.log("Error");
return next(err);
}
};
2). This is my js-schema
{
_id:{type:String},
age:{type:Number},
level_num:{type:String},
min_score:{type:String},
max_questions:{type:String}
}
3).This is my console output
undefined
4). This is my JSON data
{
"age":5,
"level_num":1,
"min_score":10,
"max_questions":30
},
{
"age":5,
"level_num":2,
"min_score":12,
"max_questions":33
}
Simply use findOne(find return an array of document) with a project field(return only desired fields).
And don't forget to check the err field !
try{
var query = {'level_num':2};
QuizLevel.findOne(query,{min_score: 1}, function(err,data){
if(err || !data)
{
console.log(err);
return next(err);
}
else
{
var a = data.min_score;
console.log(a);
res.send(a);
}
});
}catch(err){
console.log("Error");
return next(err);
}
I might be incorrect but it looks like you're trying to access object property while the result is a collection, see:
data.min_score // => [{ ... }, { ... }].min_score
vs
data[0].min_score
What you want to achieve is something like:
var scores = data.map((function (item) {
return item.min_score;
});
console.log(scores);
You can always check the type of result with console.log(typeof data) or simply write console.log(data), sometimes console.log(Object.keys(data)) come in handy as well for simple debugging not to mention node-inspector.

nodejs and MongoDB's collection.find() does not respond

I have about 30,000 documents in a MongoDB collection. And have been stuck in developing a node.js script to retrieve only the records with a specific string key-value pair.
this query on MongoDB server returns me the exact results I've been looking for:
db.getCollection('posts').find({authorName: "Ashwin-kumar"})
Returns me about 33 documents instantly. Likewise I've about 40 authors with different names.
Here's my node.js script to retrieve posts by authorName (Yes, it is based on Name, a string, as there is no ID for these authors :( ):
var fs = require('fs'),
request = require('request'),
async = require("async"),
assert = require('assert');
_ = require('lodash'),
MongoClient = require('mongodb').MongoClient;
var db, postsCollection, postCol;
async.series([dbConnect, checkCollection, createMeta, dbClose], function(){
console.log("Executed all calls in series.");
process.exit(0);
});
function dbConnect(callback){
MongoClient.connect("mongodb://localhost:27017/jPosts", function(pErr, pDb) {
if(pErr) {
console.dir(pDb);
return 0;
}
db = pDb;
callback();
});
}
function dbClose(callback){
db.close(true, function (err) {
if (err) console.error(err);
else console.log("close complete");
callback();
});
}
function checkCollection(callback) {
db.collection('posts', function(err, collection) {});
postsCollection = db.collection('posts');
postCol = db.collection('posts');
callback();
}
function createMeta(callback){
var meta = [];
postsCollection.aggregate([
{
$group : {_id : "$authorName"}
}]).toArray(function(err, result) {
assert.equal(err, null);
async.forEachLimit(result, 1, function(pPost, callback) {
getPosts(pPost._id, callback);
}, function(err) {
console.log(err);
callback();
});
});
}
function getPosts(pAuthor, callback){
var cursor = postCol.find({ "authorName": pAuthor});
cursor.toArray(function(err,items){
if(err)
callback(err);
else
callback(null, items);
});
}
This does not seem to work for me. cursor.toArray() does nothing but wait forever. Is it because of too many fields in each document?
I tried to get the count of the documents the cursor fetched and it works well.
function getPosts(pAuthor, callback){
var cursor = postCol.find({ "authourName": pAuthor});
cursor.count().then(function(items_count) {
console.log(items_count);
callback();
});
}
Also, I tried the cursor's .each method to iterate the documents fetched. But no luck yet.
function getPosts(pAuthor, callback){
var cursor = postCol.find({ "authourName": pAuthor});
cursor.each(function(err, doc) {
assert.equal(err, null);
if (doc != null) {
console.dir(doc);
} else {
console.log(err);
}
});
}
Am I missing something here? What else can be done to make this work? Is there any issues with the way I'm using async?
P.S: The idea here is to query the dump and generate the PDF's for authours in the jPost collection.
P.S 2: Here's a sample document
{
"_id" : ObjectId("571d36b55672f713fe346a66"),
"id" : 56517,
"authorName" : "Ashwin-kumar",
"comment_count" : 380,
"tagline" : "... Opinions you don't really need",
"vote_count" : 5152,
"exclusive" : null,
"post": [
],
"post_comments" : [
//comment_count objects
],
"date" : "2016-03-27"
}
(I've omitted post & post_comments parts for brevity.)
try this:
var collection = db.collection("collection_name");
collection.find({authourName: "Ashwin-kumar"}).toArray(function (err,items) {
if (err) {
console.dir(err);
} else {
//do something with items array
console.dir(items);
}
});
Did you check what is the value of pAuthor in getPosts? Because when you do aggregation, you receive a collection of objects with _id field (not authourName), so you should do:
// not sure why you need meta array, at least it's not used in the code you provided
meta.push({
author: pPost._id
});
getPosts(pPost._id, callback);

How to wait mongoose .exec function to be done?

I am some cofused by asychronous nodejs and mongoose. Simplily, I want to post an array of usernames and check, if a username is in database, then I put it in the valid array, otherwise, put it in the invalid array.
Here is my current code:
var User = require('../../db/models/user');
api.post('/userlist', function(req, res) {
var invalid = []; // usernames which can not be found in database
var valid = []; // usernames which can be found in database
(req.body.userlist).forEach(function(username) {
User
.findOne({username: username})
.exec(function(err, user) {
if (err) {
res.send(err);
return;
} else if (!user) {
invalid.push(username);
} else {
valid.push(req.params.item);
}
});
});
res.send({
Invalid: invalid,
Valid: valid
});
});
When I executed the above code, it outputs the intial empty array directly.
Invalid: [],
Valid: []
I know it is because nodejs first execute this res.send then execute function .exec(function(err, user), but i do not know how to get the right invalid and valid array, pls advise.
Your best bet is to use a promise:
api.post('/userlist', (req, res) => {
// Takes a username and returns a promise for information on that username.
function findByUsername(username) {
return new Promise((resolve, reject) =>
User.findOne({username}).exec((err, user) =>
err ? reject(err) : resolve(user)
)
);
}
// Iterate the array and transform each user to a promise for data on that user.
Promise.all(req.body.userlist.map(findByUsername))
// Then, when all of the promises in that new array resolve
.then(allUserDataInOrder => {
// Find all the valid ones (if (user))
let Valid = allUserDataInOrder.filter(Boolean); // Only those who are truthy
// And all the invalid ones (if (!user))
let Invalid = allUserDataInOrder.filter(userData => !userData); // Sadly, no convenient function here :(
// And send both away
res.send({Valid, Invalid}); // Short syntax FTW!
})
.catch(res.send); // Called with error object if any.
});
While these other solutions solve what you're trying to accomplish, they still incorporate bad design by iterating findOne(). Executing 1 query for every item in your list is incredibly inefficient. Using an $in query and a basic map, you can use a single query:
var User = require('../../db/models/user');
api.post('/userlist', function(req, res) {
User.find({username: {$in: req.body.userlist}}, function(err, users) {
if (err) {
return res.send(err);
}
// create a map of all the users in your list that exist in your database
var dbUserMap = {};
users.forEach(function(user) {
dbUserMap[user.username] = true;
});
var valid = [];
var invalid = [];
// check your POST list against the database map
req.body.userlist.forEach(function(username){
if (dbUserMap[username]) {
valid.push(username);
}
else {
invalid.push(username);
}
});
res.send({
valid: valid,
invalid: invalid
});
});
});
Try to use async module:
var invalid = [];
var valid = [];
async.each(req.body.userlist, function(name, next) {
User.findOne({username: name}, function(err, user) {
if (err) {
return next(err);
}
if (!user) {
invalid.push(name);
} else {
valid.push(name);
}
next();
)};
}, function(err) {
if (err) {
return res.send(err);
}
res.send({
Invalid: invalid,
Valid: valid
});
});

Mongoose - Create document if not exists, otherwise, update- return document in either case

I'm looking for a way to refactor part of my code to be shorter and simpler, but I don't know Mongoose very well and I'm not sure how to proceed.
I am trying to check a collection for the existence of a document and, if it doesn't exist, create it. If it does exist, I need to update it. In either case I need to access the document's contents afterward.
What I've managed to do so far is query the collection for a specific document and, if it's not found, create a new document. If it is found, I update it (currently using dates as dummy data for this). From there I can access either the found document from my initial find operation or the newly saved document and this works, but there must be a better way to accomplish what I'm after.
Here's my working code, sans distracting extras.
var query = Model.find({
/* query */
}).lean().limit(1);
// Find the document
query.exec(function(error, result) {
if (error) { throw error; }
// If the document doesn't exist
if (!result.length) {
// Create a new one
var model = new Model(); //use the defaults in the schema
model.save(function(error) {
if (error) { throw error; }
// do something with the document here
});
}
// If the document does exist
else {
// Update it
var query = { /* query */ },
update = {},
options = {};
Model.update(query, update, options, function(error) {
if (error) { throw error; }
// do the same something with the document here
// in this case, using result[0] from the topmost query
});
}
});
I've looked into findOneAndUpdate and other related methods but I'm not sure if they fit my use case or if I understand how to use them correctly. Can anyone point me in the right direction?
(Probably) Related questions:
How to check if that data already exist in the database during update (Mongoose And Express)
Mongoose.js: how to implement create or update?
NodeJS + Mongo: Insert if not exists, otherwise - update
Return updated collection with Mongoose
Edit
I didn't come across the question pointed out to me in my searching, but after reviewing the answers there I've come up with this. It's certainly prettier, in my opinion, and it works, so unless I'm doing something horribly wrong I think my question can probably be closed.
I would appreciate any additional input on my solution.
// Setup stuff
var query = { /* query */ },
update = { expire: new Date() },
options = { upsert: true };
// Find the document
Model.findOneAndUpdate(query, update, options, function(error, result) {
if (!error) {
// If the document doesn't exist
if (!result) {
// Create it
result = new Model();
}
// Save the document
result.save(function(error) {
if (!error) {
// Do something with the document
} else {
throw error;
}
});
}
});
You are looking for the new option parameter. The new option returns the newly created document(if a new document is created). Use it like this:
var query = {},
update = { expire: new Date() },
options = { upsert: true, new: true, setDefaultsOnInsert: true };
// Find the document
Model.findOneAndUpdate(query, update, options, function(error, result) {
if (error) return;
// do something with the document
});
Since upsert creates a document if not finds a document, you don't need to create another one manually.
Since you wish to refactor parts of your code to be shorter and simpler,
Use async / await
Use .findOneAndUpdate() as suggested in this answer
let query = { /* query */ };
let update = {expire: new Date()};
let options = {upsert: true, new: true, setDefaultsOnInsert: true};
let model = await Model.findOneAndUpdate(query, update, options);
///This is simple example explaining findByIDAndUpdate from my code added with try catch block to catch errors
try{
const options = {
upsert: true,
new: true,
setDefaultsOnInsert: true
};
const query = {
$set: {
description: req.body.description,
title: req.body.title
}
};
const survey = await Survey.findByIdAndUpdate(
req.params.id,
query,
options
).populate("questions");
}catch(e){
console.log(e)
}
Here is an example I am using. I have to return custom responses for UI updates etc. This can be even shorter. User is
const UserScheme = mongoose.Schema({
_id: String,
name: String,
city: String,
address: String,
},{timestamps: true});
const User = mongoose.model('Users', UserScheme);
async function userUpdateAdd(data){
var resp = '{"status": "error"}';
if(data){
var resp = await User.updateOne({ _id: data._id }, data).then(function(err, res){
console.log("database.userUpdateAdd -> Update data saved in database!");
if(err){
var errMessage = err.matchedCount == 0 ? "User Record does not exist, will create new..." : "Record not updated";
// If no match, create new
if(err.matchedCount == 0){
const create_user = new User(data);
resp = create_user.save().then(function(){
console.log("database.userUpdateAdd -> Data saved to database!");
return '{"status":"success", "message": "New User added successfully"}';
});
return resp;
}
// Exists, return success update message
if(err.matchedCount == 1){
return '{"status": "success", "message" : "Update saved successfully"}';
} else {
return '{"status": "error", "code": "' + err.modifiedCount + '", "message": "' + errMessage + '"}';
}
}
})
.catch((error) => {
//When there are errors We handle them here
console.log("database.userUpdateAdd -> Error, data not saved! Server error");
return '{"status": "error", "code": "400", "message": "Server error!"}';
});
}
return resp;
}
Here's an example:
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/rsvp', {useNewUrlParser: true, useUnifiedTopology: true});
const db = mongoose.connection;
db.on('error', () => {
console.log('mongoose connection error');
});
db.once('open', () => {
console.log('mongoose connected successfully');
});
const rsvpSchema = mongoose.Schema({
firstName: String,
lastName: String,
email: String,
guests: Number
});
const Rsvp = mongoose.model('Rsvp', rsvpSchema);
// This is the part you will need... In this example, if first and last name match, update email and guest number. Otherwise, create a new document. The key is to learn to put "upsert" as the "options" for the argument.
const findRsvpAndUpdate = (result, callback) => {
Rsvp.findOneAndUpdate({firstName: result.firstName, lastName: result.lastName}, result, { upsert: true }, (err, results) => {
if (err) {
callback(err);
} else {
callback(null, results);
}
})
};
// From your server index.js file, call this...
app.post('/rsvps', (req, res) => {
findRsvpAndUpdate(req.body, (error, result) => {
if (error) {
res.status(500).send(error);
} else {
res.status(200).send(result);
}
})
});

Categories

Resources