Mongoose models' save() won't update empty array - arrays

I have an array (bookedby) in a Mongoose model defined like this:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var BarSchema = new Schema({
date: {
type: Date,
required: true
},
barid: {
type: String,
required: true
},
bookedby: {
type: [String],
required: true
},
});
module.exports = mongoose.model('Bar', BarSchema);
I update it with following function, called by a nodejs express router:
const Bars = require("../../models/bars");
const { getToday } = require('../../utils');
module.exports = function(req, res) {
const { barid } = req.body;
const { username } = req.user;
const date = getToday();
if( !barid ) return res.json({ success: false, error: 'Please specify parameter \'barid\'.'})
Bars.findOne({ barid, date }, function (err, bar) {
if (err) return next(err);
if (!bar || bar.bookedby.indexOf(username) === -1) return res.json({ error: `Bar is not booked yet.` });
// Someone booked the bar
const index = bar.bookedby.indexOf(username);
bar.bookedby.splice(index, 1);
bar.save(err => {
if (err) res.json({ error: `Error saving booking.` });
else res.json({ success: true });
});
});
};
Everything works fine, except when I remove the last item from the bookedby array. Then the save() function doesn't update the database. The last item remains there. I guess it has something to do with mongodb optimizing empty arrays, but how can I solve this?

According to the Mongoose FAQ:
http://mongoosejs.com/docs/faq.html
For version >= 3.2.0 you should use the array.set() syntax:
doc.array.set(3, 'changed');
doc.save();
If you are running a version less than 3.2.0, you must mark the array modified before saving:
doc.array[3] = 'changed';
doc.markModified('array');
doc.save();

Related

mongodb sending userid's in an array as a query and return the json object of the users present in collection

I have a collection of online users here goes its model
var SessionDetailSchema = mongoose.Schema({
providerID: {
type: String
},
firstName: {
type: String
},
email: {
type: String
},
status: {
type: String
}
},{ timestamps: true });
var sessionDetail = module.exports = mongoose.model('OnlineUser', SessionDetailSchema);
I am trying to send an array of providerID's so that I wanted to check the collection which all providerId's are present and return me those providerID details.
and this is what I tried
router.post('/sessiondetails:find', function (req, res, next) {
console.log(req.body.providerID)
sessionDetail.find({ "providerID": { $in: req.body.providerID} }, function (err, users) {
if (users) {
console.log(users)
} else {
console.log("not there")
}
})
})
unfortunately, I am getting the only first providerid response for multiple times.
i am sending the array from the postman it looks like this
{
"providerID":["1090867867720278", "104761648907225164100", "114316680403119099502", "103668441331122956874"]
}
can some help me? thanks in advance.

save array of objects to the mongo database using node, express

I am trying to pre-load array of objects to MongoDB as below:
the below code works if I do one object at a time. that is,
if I set:
tmp_obj = {
id:1,
name: 'Tmp 1'
}
model file
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var TmpSchema = new Schema({
id: Number,
name: String
});
var Tmp= mongoose.model('Tmp', TmpSchema);
module.exports = Tmp;
routes file
var express = require('express');
var router = express.Router();
var Tmp = require('../models/tmp');
var tmp_obj = [
{
id:1,
name: 'Tmp 1'
},
{
id:2,
name: 'Tmp 2'
},
{
id:3,
name: 'Tmp 3'
}
];
var tmp = new Tmp(tmp_obj);
tmp.save(function (err) {
if (err) return console.log(err);
console.log('tmp saved to the database');
return res.redirect('/login');
})
how do I push an array of objects to the mongo? and also I have multiple collections to add. so, do I do something like:
tmp1.save(function (err) {
if (err) return console.log(err);
console.log('tmp1 saved to the database');
tmp2.save(function (err) {
if (err) return console.log(err);
console.log('tmp2 saved to the database');
return res.redirect('/login');
})
})
Another alternative is to use .create() method, it could accept an array of objects or a single object, and you don't need to create a model instance (i.e var tmp = new Tmp(tmp_obj);), here is an example:
var Tmp = require('../models/tmp');
var tmp_obj = [
{ id:1, name: 'Tmp 1' },
{ id:2, name: 'Tmp 2' },
{ id:3, name: 'Tmp 3' }
];
Tmp.create(tmp_obj, function (err, temps) {
if (err) {
console.log(err);
// terminate request/response cycle
return res.send('Error saving');
}
res.redirect('/login');
});
One last thing, don't forget to terminate the request/response cycle if an error has been occurred, otherwise the page will hangs
You can use the method insertMany from mongoose to insert multiple document at once.
From the documentation of mongoose v5.0.4
var arr = [{ name: 'Star Wars' }, { name: 'The Empire Strikes Back' }];
Movies.insertMany(arr, function(error, docs) {});
An alternative using .save() would be
// Create all objects
const objects = tmp_obj.map(x => new Tmp(x));
try {
// Saves objects
const docs = await Promise.all(objects.map(x => x.save()));
} catch(e) {
// An error happened
}
But you should not use it since insertMany is way better

How can I include data from another collection in my mongoose object?

I am building an app with mongoDB and NodeJS with an Angular front end. I currently have users creating listings and then other users responding with bids within those listings, but want to include data of those responders. I am having a hard time including that user data in the bid create function and not sure why its not pulling.
Here is the listing schema
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var Bid = require('./bid');
var ListingSchema = new Schema({
topic: String,
description: String,
budget: String,
location: String,
req1: String,
req2: String,
req3: String,
created: String,
dateReq: String,
uid: String,
bids: [Bid.schema]
})
This is how I currently create new listings
function create(req, res) {
db.User.findById(req.user, function (err, user) {
if (err) {console.log(err);}
var newListing = new db.Listing(req.body);
newListing.uid = user._id
newListing.save(function (err, savedListing) {
if (err) {
res.status(500).json({ error: err.message });
} else {
user.listings.push(newListing);
user.save();
res.json(savedListing);
}
});
});
};
This is for new bids. For some reason, the db.User information is not getting pulled and my console log shows a blank object. (Yes I commented it out)
function create(req, res) {
// db.User.findById(req.user, function (err, user) {
// console.log(req.user);
// if (err) {console.log(err);}
db.Listing.findById(req.params.listingId, function(err, foundListing) {
var newBid = new db.Bid(req.body); // add data validation later
// newBid.uid = user._id
foundListing.bids.push(newBid);
foundListing.save(function(err, savedBid) {
res.json(newBid);
});
});
});
};
try using user_id as a Object reference and populate it when needed your schema should be somewhat like this
var ListingSchema = new Schema({ topic: String,description: String, budget: String, location: String, req1: String, req2: String, req3: String, created: String, dateReq: String,//THIS CREATES A REFERENCE TO THE SCHEMA USER AND TO USE THIS YOU CAN POPULATE IT.. uid: {type:Schema.type.ObjectId,ref:User}, bids: [Bid.schema]})

Mongoose deep populate with self-referencing children

This is working code to populate my Locations collection, which can have self-referencing child Locations. It uses mongoose-deep-populate. But I need to add another ".childLocations" to my path variable for every extra level of depth that I want to handle. Is there a better way to accomplish unknown depths of self-references?
I have this node.js code.
var path = 'childLocations.childLocations.childLocations.childLocations.childLocations.childLocations';
exports.list = function(req, res) {
Location.find().sort('-created').populate('user', 'displayName').deepPopulate(path).exec(function(err, locations) {
console.log(locations);
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(locations);
}
});
};
Here is the schema:
var LocationSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill Location name',
trim: true
},
projectsExecutedHere: Boolean,
childLocations: [{
type: Schema.ObjectId,
ref: 'Location'
}],
created: {
type: Date,
default: Date.now
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
});
mongoose.model('Location', LocationSchema);
var deepPopulate = require('mongoose-deep-populate');
LocationSchema.plugin(deepPopulate);
----------
Before trying mongoose-deep-populate, I had seen that mongoose 3.6 had support for deep population, but I could only make it go one level deep. This is what I tried:
Location.find().sort('-created').populate('user', 'displayName').populate('childLocations').exec(function (err, locations) {
Location.populate(locations, {path: 'childLocations.childLocations'},
function (err, data) {
console.log(err);
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(locations);
}
}
);
For the path I also tried 'childLocations.childLocations.childLocations' but it didn't populate it at all.

NodeJS - Creating empty array results in array with a lot of nulls

I am creating a Node.js chat using socket.io
The problem is that when I see the console.log of history I see an array with A LOT of nulls and at the end my history entries
[null,null,null......[ { username: 'Nobody Example', message: '231', date: '03/21/2013 14:23:58' } ]]
How come these nulls are in the array?
Here is a part of my code.
var history = [];
io.sockets.on('connection', function (socket) {
socket.on('send', function (message) {
var date = time();
io.sockets.in(socket.room).emit('message', socket.username, message, date);
history[socket.room].push({ username: socket.username, message: message, date: date });
console.log(history);
});
socket.on('joinroom', function (room, username) {
socket.room = room;
socket.join(room);
if ( typeof history[room] === 'undefined' )
history[room] = [];
});
});
Edit for more details:
The problem is in the 'joinroom' event when creating the empty array for each room.
Here are few tests that I've made:
socket.on('joinroom', function (room, username) {
socket.room = room;
socket.join(room);
console.log(typeof history[room] == 'undefined');
history[room] = [];
console.log(typeof history[room] == 'undefined');
console.log(JSON.stringify(history));
});
The console logs:
true
false
[null,null,null,null,..................,null,[]]
If you have an empty array and index it with a large number (like your room id's), all slots in the array before that number are filled with undefined (which translates to null in JSON).
So try making history an object instead:
var history = {};
try below code changing to object(hash);
var history = {};
io.sockets.on('connection', function (socket) {
socket.on('send', function (message) {
var date = time();
io.sockets.in(socket.room).emit('message', socket.username, message, date);
if(history[socket.room] !== undefined) {
history[socket.room].push = { username: socket.username, message: message, date: date };
} else {
history[socket.room] = [{ username: socket.username, message: message, date: date }];
}
console.log(history);
});

Resources