mongodb inserting subdocuments as : [Object] - node.js

i am inserting data into mongodb using mongodb driver in nodejs.
var mongodb = require('mongodb');
var insert = function(uri, collectionName, data, next) {
mongodb.MongoClient.connect(uri, function(err, driverDb) {
if(err) {
next(err);
} else {
driverDb.collection(collectionName).insert(data,function(err,result) {
if(err) {
next(err);
} else {
driverDb.close(function (err) {
if(err) {
next(err);
} else {
next(null,result);
}
});
}
});
}
});
};
insert('mongodb://localhost/mean-test','testcol',{
a : 'Apple',
b : [ { ba: 'Boy' }, {bb : 'Bird'} ]
}, function(err,models) {
console.log(models);
});
The above result into following:
[{a:'Apple', b : [[Object]] }]
How do i achieve this :
[{_id:ObjectId("someid"), a:'Apple', b : [{_id:ObjectId("someid"), ba: 'Boy' }, {_id:ObjectId("someid"), bb : 'Bird'}] }]
Please note i do not want to use any other npm module except of mongodb.
also i want to insert in one db query.

Your objects are inserting correctly, it's just that console.log only shows two levels of object detail by default. To show all object levels you need to call util.inspect directly so you can control that:
console.log(util.inspect(models, {depth: null}));

Related

How to get access to entire subdocument(array of objects within array of objects) using moongoose?

my Schema :
var schoolSchema = new mongoose.Schema({
name: String,
classes:[
{
c_name:String,
c_strn:String,
students:[
{s_name:String,
s_roll:String,
s_address:String
}
]
}
],
});
var school = mongoose.model('school',schoolSchema);
Sample Doc :
var sainik = new school({name:'sain',
classes:
[
{
c_name:'1(one)',
c_strn:'100',
students:[
{
s_name:"Leo",
s_roll:"17",
s_address:"strt24",
},
{
s_name:"Kyle",
s_roll:"18",
s_address:"strt24",
}
]//array of students
}
]//array of classes
});
sainik.save().then(()=>console.log("Save it"));
Code :
app.get('/home/:id/:cid',function(req, res){
school.find().exec(function(err,data){
if (err){
console.log(err);
}
else{
console.log(data);
res.render("classDet",{data:data});
}
})
});
Here i need to know how to get access to the individual classes using the class id and to print the array of students as list in the "classDet"
Basically I am creating a school management system in which there will be many classes and inside that classes will be list of students.
I want to print all the students in each classes only when the parent class is access.
You can try either one of these :
app.get('/home/:id/:cid', function (req, res) {
school.find({ id: id, 'classes.c_id': cid }, { _id: 0, 'classes.c_id.$.students': 1 }).lean().exec(function (err, data) {
if (err) {
console.log(err);
}
else {
console.log(data);
res.render("classDet", { data: data });
}
})
})
Test : MongoDB-Playground
app.get('/home/:id/:cid', function (req, res) {
school.aggregate([{ $match: { id: id } }, { $unwind: '$classes' }, { $match: { 'classes.c_id': cid } }, { $project: { _id: 0, students: '$classes.students' } }]).exec(function (err, data) {
if (err) {
console.log(err);
}
else {
console.log(data);
res.render("classDet", { data: data });
}
})
})
Test : MongoDB-Playground
Note : There will be a slight difference in both results, you can test it & choose one out of those, Also keep in mind if in case id from request is being searched against _id in DB then you need to convert id string to ObjectId() before using it in queries. Also we're using .lean() in .find() to convert mongoose docs into Js objects in order to access keys in data while working on it in code.

Iterate MongoDB collection to pug

I'm trying to display a MongoDB collection of employees into Pug. I know it has something to do with the first 'data' object. I cannot figure out how to render the data within the array.
MongoDB collection:
{
"data":[
{
"active":true,
"userName":"example",
"employeeDetails":{
"personalDetails":{
"firstName":"Dennis",
"lastName":"Glover",
"emailAddress":"example#example.com"
}
}
},
{
"active": false,
"userName": example2,
"employeeDetails": {
"personalDetails": {
"firstName": "Ben",
"lastName": "Dover",
"emailAddress": "example2#example.com"
}
}
},
]
}
Express:
MongoClient.connect(url, function(err, db) {
if (err) {
console.log('Unable to connect to the Server', err);
} else {
console.log('Connection established to', url);
var employeecollection = db.collection('employees');
// Find all employees
employeecollection.find({}).toArray(function(err, employeeResult) {
if (err) {
res.send(err);
} else if (employeeResult.length) {
res.render('employeelist', {
'employeelist': employeeResult,
});
} else {
res.send('No documents found');
}
db.close();
});
};
});
Pug:
table
each employee in employeelist
tr#employee_list_item
td #{employee.userName}
I have fiddled around got it working with Angular2 using ng-repeat, however I cannot seem to get it to render in Pug unless I strip out the 'data' object in the array (which needs to be there).
As much as I can see employeelist[0].data is the array you want to iterate on.
Change employeelist to employeelist[0].data
table
each employee in employeelist[0].data
tr#employee_list_item
td #{employee.userName}
Update. Alternative method:
As Mohit suggested, if you send from the route itself, then your original code will work.
// Find all employees
employeecollection.find({}).toArray(function(err, employeeResult) {
if (err) {
res.send(err);
} else if (employeeResult.length) {
res.render('employeelist', {
'employeelist': employeeResult[0].data,
});
} else {
res.send('No documents found');
}
db.close();
});
Then, in your view:
Pug
table
each employee in employeelist
tr#employee_list_item
td #{employee.userName}
Hope this helps you!

Multiple mongoose call with async

i'm new with node and mongoose.
I do multiple queries to find my datas so I use async to populate an array and then send it back the the client.
I try to get all the stats from the games I found.
Here is my Schema : [Team] 1 ---> N [Game] 1 ---> N [Stat]
var json = []; // The variable I will send back
async.waterfall([
function( team, next ) { // Find all games from a team (with its id)
dbGame.find({ team_id: req.params._id }).exec( next );
},
function( games, next ) {
for (key in games) { // For all games, finds their stats
dbStat.find({ game_id: games[key]._id }).exec(function(err, stats) {
json.push({
_id : games[key]._id,
stats : stats
}); // The json I need to return
});
if ( Number(key) === Number(games.length -1) ) {
next(json);
}
}
}
], function () {
res.status(200);
res.json(json);
});
The variable json send is always empty because of the asynchronous and I don't know how to have the full one.
update #1
Ok it's cool it's working.
But just a trouble, all the stats are in all json in the object :
Juste a problem : The stats from all games are stored in all json.
[{
_id:"57cb15f73945ac9808fe5422",
stats:Array[13]
}, {
_id:"57ff76fd78ca91a17b25dd1b",
stats :Array[13]
}]
But I think i can sort then. Here's my code now :
async.waterfall([
function(next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
jsonResponse(res, 200, json);
});
Try below:
var json = []; // The variable I will send back
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next);
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function() {
res.status(200);
res.json(json);
});
Comment if it has any issue or I have missed something as I have not tested it.
You are not passing the results of the first find to the next step of waterfall, and when you call next(json); you are passing json as error, as the first argument is an error. Try:
async.waterfall([
function(team, next) { // Find all games from a team (with its id)
dbGame.find({
team_id: req.params._id
}).sort({
_id: 1
}).exec(next(err, games));
},
function(games, next) {
var gameIds = games.map(function(game) {
return game._id;
});
dbStat.find({
game_id: {
$in: gameIds
}
}).sort({game_id: 1}).exec(function(err, stats) {
json = gameIds.map(function (id) {
return {
_id: id,
stats: stats
}
});
next(err, json);
});
}
], function(err, json) {
res.status(200);
res.json(json);
});

mongodb incremental numbering - 500 internal server error

I am trying to number the id's of my documents with node and mongodb. I am getting a 500 internal server error: 'Cannot read property 'seq' of null.
// submit a ticket - this is returning a 500 error - not updating ID
router.post('/ticketform', function(req, res){
var db = req.db;
function getNextSequence(name, callback){
db.counters.findAndModify(
{
query:{ _id:name},
update:{$inc:{seq:1} },
new:true
}, callback
);
}
getNextSequence('ticket', function(err, obj) {
if (err) console.error(err);
db.users.insert(
{
'_id': obj.seq,
'firstname':req.body.firstname,
'lastname':req.body.lastname,
'email':req.body.email,
'phone':req.body.phone,
'issue':req.body.issue
}, function(err,docs) {
if (err) console.error(err);
console.log(docs);
res.end();
});
});
});
You appear to be using the node native driver in which the syntax is a bit different from the mongodb shell. The .findAndModify() function in particular works quite differently.
As a complete working example for you to work into your application there is this code, along with the usage of the async module make the logic look a little cleaner:
var async = require("async"),
mongodb = require("mongodb"),
MongoClient = mongodb.MongoClient;
MongoClient.connect('mongodb://localhost/test',function(err,db) {
async.waterfall(
[
function(callback) {
db.collection("counters").findAndModify(
{ "_id": "ticket" },
[],
{ "$inc": { "seq": 1 } },
{ "upsert": true, "new": true },
function(err,doc) {
callback( err, doc.seq );
}
);
},
function(seq, callback) {
var doc = { "_id": seq };
db.collection("users").insert(
{ "_id": seq },
function(err, doc) {
callback( err, doc );
}
);
}
], function( err, result ) {
console.log( result );
}
);
});
The parameters on the function are the "query", "sort" which is an array and required even if blank, then the "update" document, "options" in which you want the updated document returned as well as "upsert" to create a new document where no matching document exists,
and finally the callback.
The async waterfall allows you to "pass through" results without embedding your functionality within the callback of the method you use to get the sequence. You don't need to do it that way, but the code looks a little cleaner and direct in it's purpose.
Alternately there are options such as mongoose if you want to use that which has a helper function in .findOneAndUpdate(). You might find connection management a little easier with that library, but it is up to you.
You're probably referring to this tutorial.. In the example they are showing the implementation in the MongoDB shell, where findAndModify is synchronous. As JohnnyHK pointed in the comments, findAndModify in Node.js is asynchronous and the result is returned in the callback.
In Node.js you would do something like this:
function getNextSequence(name, callback){
db.counters.findAndModify(
{ "_id": "ticket" },
[],
{ "$inc": { "seq": 1 }},
{ "new": true, "upsert": true },
callback
);
}
getNextSequence('ticket', function(err, obj) {
if (err) console.error(err);
db.users.insert(
{
'_id': obj.seq,
'firstname':req.body.firstname,
'lastname':req.body.lastname,
'email':req.body.email,
'phone':req.body.phone,
'issue':req.body.issue
}, function(err,docs) {
if (err) console.error(err);
console.log(docs);
res.end();
});
});
You should also check the documentation for the command to better understand the options.

How to save datetime as BSON Date using Node driver?

I'm using the Node MongoDB driver to collect analytics for my website and send them to MongoLab. How does one insert the current datetime into a document so it can later be used to do aggregation in Mongo?
To be clear, here is the code:
var query = {};
query['date_time'] = new Date();
Db.connect(mongoUri, function (err, db) {
if (!err) {
db.collection(analyticsCollection, function (err, coll) {
if (!err) {
coll.save(query, {safe:true}, function (err, result) {
db.close();
res.send(200,result);
//console.log(result);
});
} else {
res.json(503,{errno:503,msg:'error: collection does not exist.'});
}
});
} else {
res.json(503,{errno:503,msg:'error: database does not exist.'});
}
});
The MongoDB documentation describes how to create a BSON date in the mongo shell, but not in the Node driver and I can't find it in the Node driver docs either.
I found a way to do what I wanted to do. This seems kind of klugey to me, so if anyone can find a better way, please let me know. My solution uses the underscore.js library:
var _ = require('underscore');
vary query = {}
query['foo'] = 'bar'
Db.connect(mongoUri, function (err, db) {
if (!err) {
db.collection(analyticsCollection, function (err, coll) {
if (!err) {
coll.save(_.extend(query,{"datetime":new Date()}), {safe:true}, function (err, result) {
db.close();
res.send(200,result);
//console.log(result);
});
} else {
res.json(503,{errno:503,msg:'error: collection does not exist.'});
}
});
} else {
res.json(503,{errno:503,msg:'error: database does not exist.'});
}
});
Now I can do the aggregation (without needing to use the $project operator):
> db.analytics.aggregate([{$group:{_id:{day:{$dayOfMonth:"$datetime"}}}}])
{ "result" : [ { "_id" : { "day" : 15 } } ], "ok" : 1 }
Hope this helps someone.

Resources