MongoDB collection.save() duplicating objects - node.js

I'm working on my first web dev project involving backend work, and i'm giving mongoDB a shot. I'm currently working on a simple admin panel where every user is listed, and by clicking on that user the admin can go in and manually edit their info, update, and return to the master user list to see the changes.
I'm trying to use the collection.save() method to update the existing users, but I've encountered a problem where, instead of just updating, it creates a duplicate copy of the document with a matching _id number. Here is an image illustrating what I mean, and here is all of my code for the update:
router.post('/updateUser', function(req, res) {
var db = req.db;
var collection = req.collection;
var userId = req.body.userId;
var djStatus = req.body.djStatus;
var access = req.body.access;
var firstName = req.body.firstName;
var lastName = req.body.lastName;
var email = req.body.email;
var phone = req.body.phone;
var studentStatus = req.body.studentStatus;
var macIdNum = req.body.macIdNum;
var iclass = req.body.iclass;
var gradYear = req.body.gradYear;
var show = req.body.show;
var blurb = req.body.blurb;
collection.save(
{
_id: userId,
djStatus: djStatus,
access: access,
firstName: firstName,
lastName: lastName,
email: email,
phone: phone,
studentStatus: studentStatus,
macIdNum: macIdNum,
iclass: iclass,
gradYear: gradYear,
show: show,
blurb: blurb
}, function (err, doc) {
if (err) {
res.send('there was a problem updating');
} else {
console.log(doc + ' doc');
res.location('admin/users');
res.redirect('admin/users');
}
});
});
I am really not sure why this is happening. any help would be much appreciated!

From the post, I'm not sure which mongodb driver you're using but save() is probably not used for updating a document in mongodb. There should be an update() function from the mongodb driver you are using. For example, with mongoskin driver, this is the syntax for insert/update:
collection.update(_id: userId, {$set: data}, {safe:true, upsert:true}, function (err, result) {
// check err and result and do something
});

Related

Form not saving to MongoDB

I have been trying to get a form to submit to the database but every time I submit it only saves an id.
Below is my code in my app.js file for the post route the form submits to.
// CREATE add new startup to database
app.post("/startup-submit", function(req, res) {
// Get data from form
var startupname = req.body.startupname;
var url = req.body.url;
var shortdescription = req.body.shortdescription;
var longdescription = req.body.longdescription;
var techstack = req.body.techstack;
var foundeddate = req.body.foundeddate;
var blog = req.body.blog;
var twitter = req.body.twitter;
var facebook = req.body.facebook;
var linkedin = req.body.linkedin;
var email = req.body.email;
var foundername = req.body.foundername;
var foundersocialmedia = req.body.foundersocialmedia;
//Pass data through to page
var newStartup = {
startupname:startupname,
url: url,
shortdescription: shortdescription,
longdescription: longdescription,
techstack: techstack,
foundeddate: foundeddate,
blog: blog,
twitter: twitter,
facebook: facebook,
linkedin: linkedin,
email: email,
foundername: foundername,
foundersocialmedia: foundersocialmedia
};
Startup.create(newStartup, function(err, newlyCreatedStartup){
if(err){
console.log(err);
} else {
// Redirect back to show all page
res.redirect("/startups");
}
console.log(newlyCreatedStartup);
});
});
My model I am using is:
var mongoose = require("mongoose");
var startupSchema = new mongoose.Schema({
about_startup: {
startup_name: String,
url: String,
short_description: String,
long_description: String,
tech_stack: String,
date_founded: Date
},
social_media: {
blog: String,
twitter: String,
facebook: String,
linkedin: String,
email: String,
},
about_founder: {
name: String,
social_media_founder: String
}
});
module.exports = mongoose.model("Startup", startupSchema);
These should line up correctly, or am I off base on this? I am new to node and express so learning has been interesting. As for how I decided on the model and layout of the code, it is mostly from a previous project I did as part of a course. I wrote the above code out but referenced I guess is the best way of saying it to the old code.
I know the variables are all pulling the data because I console.log(variableName) each one to test it out. The model looks right to me but maybe I am missing something.
You should use the same variable name which you used in model. Your code should be like this. This code will perfectly suits you.
var newStartup = { about_startup: {}, social_media: {}, about_founder: {}};
newStartup.about_startup.startup_name = req.body.startupname;
newStartup.about_startup.url = req.body.url;
newStartup.about_startup.short_description = req.body.shortdescription;
newStartup.about_startup.long_description = req.body.longdescription;
newStartup.about_startup.tech_stack = req.body.techstack;
newStartup.about_startup.date_founded = req.body.foundeddate;
newStartup.social_media.blog = req.body.blog;
newStartup.social_media.twitter = req.body.twitter;
newStartup.social_media.facebook = req.body.facebook;
newStartup.social_media.linkedin = req.body.linkedin;
newStartup.social_media.email = req.body.email;
newStartup.about_founder.name = req.body.foundername;
newStartup.about_founder.social_media_founder = req.body.foundersocialmedia;
Startup.create(newStartup, function (err, newlyCreatedStartup) {
if (err) {
console.log(err);
} else {
// Redirect back to show all page
res.redirect("/startups");
}
console.log(newlyCreatedStartup);
});

How to stop node inserting data into mongodb when I reload page

I have a page with a form that allows you to insert information into a collection called "Farm". The function works, but if I ever reload the page the information gets submitted into the again. I created a filter function that avoids inserting the data if the req.body has a similar name and species property. But, I feel that there is a better way to go about doing this. Here is the code that creates the information and pushes the data into mongodb
var mongoose = require('mongoose');
var f = mongoose.model('Farm');
module.exports create = function(req,res){
var obj = req.body;
console.log(obj)
f.find({}, function(err,docs){
var same = docs.some(function(element,index){
return (element.name === obj.name && element.species === obj.species);
})
if(same === true){
console.log('That name is already in the database');
res.render('add',{msg:"Animal is already in the database"});
}else{
f.create({
name: obj.name,
species: obj.species.toLowerCase(),
sex:obj.sex,
weight: obj.weight,
age: obj.age,
}, function(err,info){
if (err){
throw err;
}
var message = 'Congratulations you have successfully added a '+info.species;
res.render('add',{msg:message})
})
}
})
}
Typically the solution to this problem is to redirect the user to another page after inserting successfully (but not if there were errors, so the form can be edited and resubmitted).
res.redirect('/success')

how to create a new collection automatically in mongodb

my main issue is how do this architecture - if a user creates a new list in database and there is a separate collections for storing lists ,then i should redirect him when he want to access a specific list where he can store his contacts by assigning him some collection in database, now the problem comes for me, if there are 'n' lists of a specific user for saving his contacts by categorizing in to lists based on his need , then how should i implement this architecture any suggestions ? or there's a mistake with what i have done so far ? any corrections ?
i am using mongodb,nodejs with express framework
What i have so far done is manually i was able to create a new list and allocate a collection for it so that user can add his contacts in that list.
But creating collections manually for each and every list is not possible so i am searching for a method where we can automatically create a new collection on demand.
Is there any method to create n collection or n sub collections in mongodb ?
This is my js file for adding contacts in a collection , where i have stored those contacts in two separate list with separate collection for each list.
var express = require('express');
var router = express.Router();
var $ = require("jquery");
var mongoose = require('mongoose');
/* GET New User page. in list 1 */
router.get('/mylist', function(req, res) {
res.render('mylist', { title: 'Go To My-List' });
});
router.post('/delcontact', function(req, res){
console.log('Using delcontact');
var db = req.db;
var collection = db.get('golists');
console.log('Got this : ' + JSON.stringify(req.body));
var delcontact = req.body;
var emails = Object.keys(delcontact).map(function(k) { return delcontact[k] });
for(var z=0; z<emails.length; z++){
//console.log('email: \n' + emails[z]);
collection.remove({email: emails[z]},
function(err, doc, z) {
if (err)
res.send('delete unsuccessfull');
else {
console.log('Selected contacts deleted');
}
});
res.send({redirect: '/userlist2'});
var collection = db.get('golists');
};
});
/* POST to Add User Service */
router.post('/addusers', function(req, res) {
// Set our internal DB variable
var db = req.db;
// Get our form values. These rely on the "name" attributes
var firstName = req.body.firstname;
var lastName = req.body.lastname
var userEmail = req.body.useremail;
// Set our collection
var collection = db.get('golists');
// Submit to the DB
collection.insert({
"firstname" : firstName,
"lastname" : lastName,
"email" : userEmail
}, function (err, doc) {
if (err) {
// If it failed, return error
res.send("There was a problem adding the information to the database.");
}
else {
// If it worked, set the header so the address bar doesn't still say /adduser
res.location("mailinglist");
// And forward to success page
res.redirect("mailinglist");
}
});
});
/* GET Userlist page. */
router.get('/mailinglist', function(req, res) {
var db = req.db;
var collection = db.get('golists');
collection.find({},{},function(e,docs){
res.render('userlist2', {
"userlist2" : docs
});
});
});
/* this is for list 2 */
router.get('/mylist2', function(req, res) {
res.render('mylist2', { title: 'Go To My-List' });
});
router.post('/addusers2', function(req,res){
//Set our internal DB variable
var db = req.db;
// Get our form values. These rely on the "name" attributes
var firstName = req.body.firstname;
var lastName = req.body.lastname
var userEmail = req.body.useremail;
// Set our collection
var collection = db.get('golists2');
// Submit to the DB
collection.insert({
"firstname" : firstName,
"lastname" : lastName,
"email" : userEmail
}, function (err, doc) {
if (err) {
// If it failed, return error
res.send("There was a problem adding the information to the database.");
}
else {
// If it worked, set the header so the address bar doesn't still say /adduser
res.location("mailinglist2");
// And forward to success page
res.redirect("mailinglist2");
}
});
});
/* GET Userlist page. */
router.get('/mailinglist2', function(req, res) {
var db = req.db;
var collection = db.get('golists2');
collection.find({},{},function(e,docs){
res.render('mailinglist2', {
"mailinglist2" : docs
});
});
});
router.post('/delcontact2', function(req, res){
console.log('Using delcontact');
var db = req.db;
var collection = db.get('golists2');
console.log('Got this : ' + JSON.stringify(req.body));
var delcontact = req.body;
var emails = Object.keys(delcontact).map(function(k) { return delcontact[k] });
for(var z=0; z<emails.length; z++){
//console.log('email: \n' + emails[z]);
collection.remove({email: emails[z]},
function(err, doc, z) {
if (err)
res.send('delete unsuccessfull');
else {
console.log('Selected contacts deleted');
}
});
res.send({redirect: '/mailinglist2'});
var collection = db.get('golists2');
};
});
this is my file for creating new lists in some collection
router.get('/newlist',function(req,res){
res.render("newlist" ,{titile:'Add new list'}) ;
});
router.post('/addlist',function(req,res){
var db= req.db;
var listname=req.body.listname;
var collection=db.get("lists");
collection.insert({
"listname":listname
}, function (err,doc) {
if(err) {
res.send("There was a problem adding new list to database");
}
else {
res.location('lou');
res.redirect('lou');
}
});
});
router.get('/lou',function(req,res){
var db=req.db;
var collection=db.get("lists");
collection.find({},{},function(e,docs){
res.render('lou', {
"lou" : docs
});
});
});
router.get('/drop', function(req, res) {
res.render('drop', { title: 'Go To My-List' });
});
my jade file for this is
body
nav.navbar.navbar-default
.container-fluid
ul.nav.navbar-nav
li.active
a(href='/mailinglist')
| List 1
span.sr-only (current)
|
li
a(href='/mailinglist2')
| List 2
tbody
ul
p Import Contact From Your DB
a(href='/upload2') Go
table.table#stable
caption User Contact Details
|
thread
tr
th #
|
th FirstName
|
th LastName
|
th Email ID
|
each user, i in userlist2
tr(id=i)
td
input.contactID(type='checkbox')
td
| #{user.firstname}
td
| #{user.lastname}
td(id='email'+i)
| #{user.email}
button.btn.btn-danger#delete-button Delete Selected Contacts
First of all, this doesn't sound like a good idea. It would be much better when you have one collection for all lists of all users. The problem with having too many collections is that MongoDB doesn't offer any tools for selecting data from more than one collection at a time. So when you want to query data from multiple collections, you will have to perform multiple queries, which is very unperformant.
However, when you are determined to cling to this architecture, MongoDB automatically creates a collection when you insert data into a collection-name it doesn't know.

Dynamically create collection with Mongoose

I want to give users the ability to create collections in my Node app. I have really only seen example of hard coding in collections with mongoose. Anyone know if its possible to create collections dynamically with mongoose? If so an example would be very helpful.
Basically I want to be able to store data for different 'events' in different collections.
I.E.
Events:
event1,
event2,
...
eventN
Users can create there own custom event and store data in that collection. In the end each event might have hundreds/thousands of rows. I would like to give users the ability to perform CRUD operations on their events. Rather than store in one big collection I would like to store each events data in a different collection.
I don't really have an example of what I have tried as I have only created 'hard coded' collections with mongoose. I am not even sure I can create a new collection in mongoose that is dynamic based on a user request.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
var schema = mongoose.Schema({ name: 'string' });
var Event1 = mongoose.model('Event1', schema);
var event1= new Event1({ name: 'something' });
event1.save(function (err) {
if (err) // ...
console.log('meow');
});
Above works great if I hard code 'Event1' as a collection. Not sure I create a dynamic collection.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
...
var userDefinedEvent = //get this from a client side request
...
var schema = mongoose.Schema({ name: 'string' });
var userDefinedEvent = mongoose.model(userDefinedEvent, schema);
Can you do that?
I believe that this is a terrible idea to implement, but a question deserves an answer. You need to define a schema with a dynamic name that allows information of 'Any' type in it. A function to do this may be a little similar to this function:
var establishedModels = {};
function createModelForName(name) {
if (!(name in establishedModels)) {
var Any = new Schema({ any: Schema.Types.Mixed });
establishedModels[name] = mongoose.model(name, Any);
}
return establishedModels[name];
}
Now you can create models that allow information without any kind of restriction, including the name. I'm going to assume an object defined like this, {name: 'hello', content: {x: 1}}, which is provided by the 'user'. To save this, I can run the following code:
var stuff = {name: 'hello', content: {x: 1}}; // Define info.
var Model = createModelForName(name); // Create the model.
var model = Model(stuff.content); // Create a model instance.
model.save(function (err) { // Save
if (err) {
console.log(err);
}
});
Queries are very similar, fetch the model and then do a query:
var stuff = {name: 'hello', query: {x: {'$gt': 0}}}; // Define info.
var Model = createModelForName(name); // Create the model.
model.find(stuff.query, function (err, entries) {
// Do something with the matched entries.
});
You will have to implement code to protect your queries. You don't want the user to blow up your db.
From mongo docs here: data modeling
In certain situations, you might choose to store information in
several collections rather than in a single collection.
Consider a sample collection logs that stores log documents for
various environment and applications. The logs collection contains
documents of the following form:
{ log: "dev", ts: ..., info: ... } { log: "debug", ts: ..., info: ...}
If the total number of documents is low you may group documents into
collection by type. For logs, consider maintaining distinct log
collections, such as logs.dev and logs.debug. The logs.dev collection
would contain only the documents related to the dev environment.
Generally, having large number of collections has no significant
performance penalty and results in very good performance. Distinct
collections are very important for high-throughput batch processing.
Say I have 20 different events. Each event has 1 million entries... As such if this is all in one collection I will have to filter the collection by event for every CRUD op.
I would suggest you keep all events in the same collection, especially if event names depend on client code and are thus subject to change. Instead, index the name and user reference.
mongoose.Schema({
name: { type: String, index: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true }
});
Furthermore I think you came at the problem a bit backwards (but I might be mistaken). Are you finding events within the context of a user, or finding users within the context of an event name? I have a feeling it's the former, and you should be partitioning on user reference, not the event name in the first place.
If you do not need to find all events for a user and just need to deal with user and event name together you could go with a compound index:
schema.index({ user: 1, name: 1 });
If you are dealing with millions of documents, make sure to turn off auto index:
schema.set('autoIndex', false);
This post has interesting stuff about naming collections and using a specific schema as well:
How to access a preexisting collection with Mongoose?
You could try the following:
var createDB = function(name) {
var connection = mongoose.createConnection(
'mongodb://localhost:27017/' + name);
connection.on('open', function() {
connection.db.collectionNames(function(error) {
if (error) {
return console.log("error", error)
}
});
});
connection.on('error', function(error) {
return console.log("error", error)
});
}
It is important that you get the collections names with connection.db.collectionNames, otherwise the Database won't be created.
This method works best for me , This example creates dynamic collection for each users , each collection will hold only corresponding users information (login details), first declare the function dynamicModel in separate file : example model.js
/* model.js */
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function dynamicModel(suffix) {
var addressSchema = new Schema(
{
"name" : {type: String, default: '',trim: true},
  "login_time" : {type: Date},
"location" : {type: String, default: '',trim: true},
}
);
return mongoose.model('user_' + suffix, addressSchema);
}
module.exports = dynamicModel;
In controller File example user.js,first function to create dynamic collection and second function to save data to a particular collection
/* user.js */
var mongoose = require('mongoose'),
function CreateModel(user_name){//function to create collection , user_name argument contains collection name
var Model = require(path.resolve('./model.js'))(user_name);
}
function save_user_info(user_name,data){//function to save user info , data argument contains user info
var UserModel = mongoose.model(user_name) ;
var usermodel = UserModel(data);
usermodel.save(function (err) {
if (err) {
console.log(err);
} else {
console.log("\nSaved");
}
});
}
yes we can do that .I have tried it and its working.
REFERENCE CODE:
app.post("/",function(req,res){
var Cat=req.body.catg;
const link= req.body.link;
const rating=req.body.rating;
Cat=mongoose.model(Cat,schema);
const item=new Cat({
name:link,
age:rating
});
item.save();
res.render("\index");
});
I tried Magesh varan Reference Code ,
and this code works for me
router.post("/auto-create-collection", (req, res) => {
var reqData = req.body; // {"username":"123","password":"321","collectionName":"user_data"}
let userName = reqData.username;
let passWord = reqData.password;
let collectionName = reqData.collectionName;
// create schema
var mySchema = new mongoose.Schema({
userName: String,
passWord: String,
});
// create model
var myModel = mongoose.model(collectionName, mySchema);
const storeData = new myModel({
userName: userName,
passWord: passWord,
});
storeData.save();
res.json(storeData);
});
Create a dynamic.model.ts access from some where to achieve this feature.
import mongoose, { Schema } from "mongoose";
export default function dynamicModelName(collectionName: any) {
var dynamicSchema = new Schema({ any: Schema.Types.Mixed }, { strict: false });
return mongoose.model(collectionName, dynamicSchema);
}
Create dynamic model
import dynamicModelName from "../models/dynamic.model"
var stuff = { name: 'hello', content: { x: 1 } };
var Model = await dynamicModelName('test2')
let response = await new Model(stuff).save();
return res.send(response);
Get the value from the dynamic model
var Model = dynamicModelName('test2');
let response = await Model.find();
return res.send(response);

Update MongoDB collection - JavaScript

I am trying to update a value in a collection. The user clicks a button, and a number corresponding to that button, gets sent to the server to be added to the collection.
I cannot get the collection to update, but it works fine in the console if i use db.orders.update()
orders model:
// DB Initiation stuff
var orderSchema = new mongoose.Schema({
status: String,
rated: Number
});
var collection = 'orders';
var Order = db.model('Order', orderSchema, collection);
module.exports = Order;
client side (when button click)
// starID = 5; id = 50e6a57808a1d92dcd000001
socket.emit('rated', {rated: starID, id: id});
socket.js:
var Order = require('../models/orders');
socket.on('rated', function(data) {
/* Probably a better way to do this but, wrap
* the id in 'ObjectId(id)'
*/
var id = 'ObjectId("'+data.id+'")';
Order.update( {_id: id}, {$set: {rated: data.rated}} );
socket.emit('updated', {
note: 'Some HTML notification to append'
});
});
Let Mongoose do the casting of the id string to an ObjectId:
socket.on('rated', function(data) {
Order.update( {_id: data.id}, {$set: {rated: data.rated}} );
socket.emit('updated', {
note: 'Some HTML notification to append'
});
});
Mongoose expects a String as id. So passing an ObjectId does not work.
Try:
Order.update( {_id: id.toString()}, ....... );
It is safe to use toString with both String and ObjectId.
I tried everything stated and it just didn't seem to work, so I changed the update code to:
Order.findOne({_id : id}, function(err, doc) {
doc.rated = data.rated;
doc.status = data.status;
doc.save();
});
And now it works fine. Thank you to everyone who contributed to answering

Resources