mongo on nodejs collection not found - node.js

I connect to a database and receive a client.
The next step is to create a collection (table).
db.createCollection("test", function(err, collection){ //I also tried db.collection(...
if(collection!=null)
{
collection.insert({"test":"value"}, function(error, model){console.log(model)});
}
else if(err!=null)
console.log(err);
});
Now I would have created a collection "test" as well as a document(row) "test" in it.
Next is to get the content of the collection:
db.test.find({}); //An empty query document ({}) selects all documents in the collection
Here I get the error: Cannot call "find" of undefined . So, what did I do wrong?
Edit: I connect to the database this way:
var mongoClient = new MongoClient(new Server("localhost", 27017, {native_parser:true}));
mongoClient.open(function(err,mongoclient){
if(mongoclient!=null)
{
var db = mongoclient.db("box_tests");
startServer(db);
}
else if(err!=null)
console.log(err);
});

In the mongo command line you can use db.test.find({}) but in javascript there is no way to replicate that interface so far (maybe with harmonies proxies some day).
So it throws an error Cannot call "find" of undefined because there is no test in db.
The api for the node.js driver of mongodb is like this:
db.collection('test').find({}).toArray(function (err, docs) {
//you have all the docs here.
});
Another complete example:
//this how you get a reference to the collection object:
var testColl = db.collection('test');
testColl.insert({ foo: 'bar' }, function (err, inserted) {
//the document is inserted at this point.
//Let's try to query
testColl.find({}).toArray(function (err, docs) {
//you have all the docs in the collection at this point
});
});
Also remember that mongodb is schema-less and you don't need to create the collections ahead of time. There are few specific cases like creating a capped collection and few others.

If you call db.test.find "next" after the db.createCollection block it ends up being immediately next before db.createCollection succeeds. So at that point, db.test is undefined.
Remember that node is async.
To get the results I believe you are expecting, db.test.find would have to be in the collection.insert callback where you're calling console.log(model).
db.createCollection("test", function(err, collection){
if(collection!=null)
{
// only at this point does db.test exist
collection.insert({"test":"value"}, function(error, model){
console.log(model)
// collection and inserted data available here
});
}
else if(err!=null)
console.log(err);
});
// code here executes immediately after you call createCollection but before it finishes
Checkout the node async.js module. Good writeup here: http://www.sebastianseilund.com/nodejs-async-in-practice

Related

Find() Method MongoDB with Nodejs

I'm currently trying to find all the documents that have a certain value of 'bar' in the key 'foo'. Using mongodb and nodejs.
When I try to run this I get:
"TypeError: Cannot read property 'find' of undefined" error return.
If I try using findOne() it'll just return the first document that has the document with the value "bar" for the key "foo", however there are 3.
module.exports = function(app, db) {
app.get('/foo', (req, res)=>{
db.collection('barCollection').find({foo: {$all: ['bar']}}
,(err, item)=>{
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(item);
}
});
});
};
Thanks
Paul is right, there is some issue in your code which is why it's returning null.
Here try this snippet. I'm using 2 files for demo.
model.js
const mongoose = require('mongoose');
mongoose.connect('mongo_url');
var barSchema = new mongoose.Schema({
// your schema
});
module.exports = mongoose.model('Bar', barSchema);
main.js
var BarCollection = require('./models'); // assuming both files are in same directory.
BarCollection.find({foo: {$all: ['bar']}}
,(err, item)=>{
if (err) {
res.send({'error':'An error has occurred'});
} else {
res.send(item);
}
});
});
};
Basically what I am trying here is:
separate MongoDB model code in separate files
Import mongo collection in API files for CRUD operations
db.collection('barCollection') is returning null for some reason. You'll need to figure out why, it's probably somewhere else in your code since you don't have the setup logic here.
I'd look at whether mongodb was connected properly (i.e. is the db instance you're passing that function actually connected to the database), is mongodb running and available on the port you configured it with, etc.

How to make two successives synchronous queries on collections in MongoDB?

I am wondering how I can make two successives or synhcronous access to two different collections in a MongoDB database. I need to take a parameter in a first collection to use it as a parameter of .find() method in the second query. Here is my code:
MongoClient.connect(url, function(err, db) {
db.collection('questions').find( { "status": "active" } ).toArray(
function(err, item) {
var fb_id = item[0]._id;
console.log(fb_id);
db.close();
MongoClient.connect(url, function(err, db) {
var cursorC = db.collection('comments').find({questionId : fb_id }).toArray(
function(err, items) {
console.log(items);
}
);
db.close();
});
});
});
I tried unsuccessfully to chain the two connection to the database, however the second query states an undefined result.
When using both db.collection().find() function at the saeme level, in the MongoClient.connect() function I suppose both are executed asynchronously and it ends with another undefined result for the second function whose result depends on the first.
Do you have any idea to proceed using the MongoDB NodeJS driver I am using?
Thanks

Object #<Promise> has no method 'limit'

when I run code
var collection = db.get('categories');
console.log(collection.find().limit(1).sort( { _id : -1 } ));
on nodejs using mongodb I am getting error Object # has no method 'limit' . I am a beginner to node and really stuck on this section of node
here is full code for geting last insert document.
router.post('/addcategory', function(req, res) {
// Set our internal DB variable
var db = req.db;
// Get our form values. These rely on the "name" attributes
var name = req.body.name;
var description = req.body.description;
// Set our collection
var collection = db.get('categories');
// Submit to the DB
collection.insert({
"name" : name,
"description" : description,
}, function (err, doc) {
if (err) {
// If it failed, return error
res.send("There was a problem adding the information to the database.");
}
else {
// And forward to success page
/******************/
console.log(collection.find().limit(1).sort( { _id : -1 } ));
/*************/
}
});
});
The key piece of missing information here was that you are using Monk, not the native MongoDB Node.JS driver. The command you have for find() is how you would use the native driver (with the changes suggested by #BlakesSeven above for asynchronity), but Monk works a little bit differently.
Try this instead:
collection.find({}, { limit : 1, sort : { _id : -1 } }, function (err,res) {
console.log(res);
});
The method is still asynchronous so you still need to invoke itm either as a promise with .then() or a callback. No methods are sychronous and return results in-line.
Also the result returned from the driver is s "Cursor" and not the object(s) you expect. You either iterate the returned cursor or just use .toArray() or similar to convert:
collection.find().limit(1).sort({ "_id": -1 }).toArray().then(function(docs) {
console.log(docs[0]);
});
Or:
collection.find().limit(1).sort({ "_id": -1 }).toArray(function(err,docs) {
console.log(docs[0]);
});
But really the whole premise is not correct. You seem to basically want to return what you just inserted. Event with the correction in your code, the returned document is not necessarily the one you just inserted, but rather the last one inserted into the collection, which could have occurred from another operation or call to this route from another source.
If you want what you inserted back then rather call the .insertOne() method and inspect the result:
collection.insertOne({ "name": name, "description": description },function(err,result) {
if (err) {
res.send("There was an error");
} else {
console.log(result.ops)
}
});
The .insert() method is considered deprecated, but basically returns the same thing. The consideration is that they return a insertWriteOpResult object where the ops property contains the document(s) inserted and their _id value(s),

Retrieve unique ID from MongoDB using Express.js

I'm fairly new to MongoDB. My current code is as follows:
var app = express();
app.post('/myDB', function(req, res) {
console.log('POSTed data');
db.myDB.insert(req.body, function(err, doc) {
console.log(err);
res.json(doc);
});
})
I know that when data is inserted into MongoDB, a unique _id key:value pair is created. I would like to have access to this unique id upon data insertion.
But from what I know currently (admittedly not a whole lot), I would have to do an app.get('/myDB/:id')... (Or something similar) in order to access that unique identifier. Is there a way to do this without having to make an extra http request?
In MongoDB, with the Node.js driver, you can get the inserted id from the callback function's second parameter. The syntax is:
collection.insert(docs[[, options], callback])
where docs is a single document object or an array of documents, options is an options object and callback is a callback function to run after the record is inserted. The second paramter returns an array of documents inserted.
Thus you can retrieve the inserted id this way:
var app = express();
app.post('/myDB', function(req, res) {
console.log('POSTed data');
db.myDB.insert(req.body, {w:1}, function(err, doc) {
console.log("Record added as "+ doc[0]._id);
res.json(doc);
});
You can read more on the docs here.

How can I save multiple documents concurrently in Mongoose/Node.js?

At the moment I use save to add a single document. Suppose I have an array of documents that I wish to store as single objects. Is there a way of adding them all with a single function call and then getting a single callback when it is done? I could add all the documents individually but managing the callbacks to work out when everything is done would be problematic.
Mongoose does now support passing multiple document structures to Model.create. To quote their API example, it supports being passed either an array or a varargs list of objects with a callback at the end:
Candy.create({ type: 'jelly bean' }, { type: 'snickers' }, function (err, jellybean, snickers) {
if (err) // ...
});
Or
var array = [{ type: 'jelly bean' }, { type: 'snickers' }];
Candy.create(array, function (err, jellybean, snickers) {
if (err) // ...
});
Edit: As many have noted, this does not perform a true bulk insert - it simply hides the complexity of calling save multiple times yourself. There are answers and comments below explaining how to use the actual Mongo driver to achieve a bulk insert in the interest of performance.
Mongoose 4.4 added a method called insertMany
Shortcut for validating an array of documents and inserting them into
MongoDB if they're all valid. This function is faster than .create()
because it only sends one operation to the server, rather than one for each
document.
Quoting vkarpov15 from issue #723:
The tradeoffs are that insertMany() doesn't trigger pre-save hooks, but it should have better performance because it only makes 1 round-trip to the database rather than 1 for each document.
The method's signature is identical to create:
Model.insertMany([ ... ], (err, docs) => {
...
})
Or, with promises:
Model.insertMany([ ... ]).then((docs) => {
...
}).catch((err) => {
...
})
Mongoose doesn't have bulk inserts implemented yet (see issue #723).
Since you know the number of documents you're saving, you could write something like this:
var total = docArray.length
, result = []
;
function saveAll(){
var doc = docArray.pop();
doc.save(function(err, saved){
if (err) throw err;//handle error
result.push(saved[0]);
if (--total) saveAll();
else // all saved here
})
}
saveAll();
This, of course, is a stop-gap solution and I would recommend using some kind of flow-control library (I use q and it's awesome).
Bulk inserts in Mongoose can be done with .insert() unless you need to access middleware.
Model.collection.insert(docs, options, callback)
https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js#L71-91
Use async parallel and your code will look like this:
async.parallel([obj1.save, obj2.save, obj3.save], callback);
Since the convention is the same in Mongoose as in async (err, callback) you don't need to wrap them in your own callbacks, just add your save calls in an array and you will get a callback when all is finished.
If you use mapLimit you can control how many documents you want to save in parallel. In this example we save 10 documents in parallell until all items are successfully saved.
async.mapLimit(myArray, 10, function(document, next){
document.save(next);
}, done);
I know this is an old question, but it worries me that there are no properly correct answers here. Most answers just talk about iterating through all the documents and saving each of them individually, which is a BAD idea if you have more than a few documents, and the process gets repeated for even one in many requests.
MongoDB specifically has a batchInsert() call for inserting multiple documents, and this should be used from the native mongodb driver. Mongoose is built on this driver, and it doesn't have support for batch inserts. It probably makes sense as it is supposed to be a Object document modelling tool for MongoDB.
Solution: Mongoose comes with the native MongoDB driver. You can use that driver by requiring it require('mongoose/node_modules/mongodb') (not too sure about this, but you can always install the mongodb npm again if it doesn't work, but I think it should) and then do a proper batchInsert
Newer versions of MongoDB support bulk operations:
var col = db.collection('people');
var batch = col.initializeUnorderedBulkOp();
batch.insert({name: "John"});
batch.insert({name: "Jane"});
batch.insert({name: "Jason"});
batch.insert({name: "Joanne"});
batch.execute(function(err, result) {
if (err) console.error(err);
console.log('Inserted ' + result.nInserted + ' row(s).');
}
Use insertMany function to insert many documents. This sends only one operation to the server and Mongoose validates all the documents before hitting the mongo server. By default Mongoose inserts item in the order they exist in the array. If you are ok with not maintaining any order then set ordered:false.
Important - Error handling:
When ordered:true validation and error handling happens in a group means if one fails everything will fail.
When ordered:false validation and error handling happens individually and operation will be continued. Error will be reported back in an array of errors.
Here is another way without using additional libraries (no error checking included)
function saveAll( callback ){
var count = 0;
docs.forEach(function(doc){
doc.save(function(err){
count++;
if( count == docs.length ){
callback();
}
});
});
}
You can use the promise returned by mongoose save, Promise in mongoose does not have all, but you can add the feature with this module.
Create a module that enhance mongoose promise with all.
var Promise = require("mongoose").Promise;
Promise.all = function(promises) {
var mainPromise = new Promise();
if (promises.length == 0) {
mainPromise.resolve(null, promises);
}
var pending = 0;
promises.forEach(function(p, i) {
pending++;
p.then(function(val) {
promises[i] = val;
if (--pending === 0) {
mainPromise.resolve(null, promises);
}
}, function(err) {
mainPromise.reject(err);
});
});
return mainPromise;
}
module.exports = Promise;
Then use it with mongoose:
var Promise = require('./promise')
...
var tasks = [];
for (var i=0; i < docs.length; i++) {
tasks.push(docs[i].save());
}
Promise.all(tasks)
.then(function(results) {
console.log(results);
}, function (err) {
console.log(err);
})
Add a file called mongoHelper.js
var MongoClient = require('mongodb').MongoClient;
MongoClient.saveAny = function(data, collection, callback)
{
if(data instanceof Array)
{
saveRecords(data,collection, callback);
}
else
{
saveRecord(data,collection, callback);
}
}
function saveRecord(data, collection, callback)
{
collection.save
(
data,
{w:1},
function(err, result)
{
if(err)
throw new Error(err);
callback(result);
}
);
}
function saveRecords(data, collection, callback)
{
save
(
data,
collection,
callback
);
}
function save(data, collection, callback)
{
collection.save
(
data.pop(),
{w:1},
function(err, result)
{
if(err)
{
throw new Error(err);
}
if(data.length > 0)
save(data, collection, callback);
else
callback(result);
}
);
}
module.exports = MongoClient;
Then in your code change you requires to
var MongoClient = require("./mongoHelper.js");
Then when it is time to save call (after you have connected and retrieved the collection)
MongoClient.saveAny(data, collection, function(){db.close();});
You can change the error handling to suit your needs, pass back the error in the callback etc.
This is an old question, but it came up first for me in google results when searching "mongoose insert array of documents".
There are two options model.create() [mongoose] and model.collection.insert() [mongodb] which you can use. View a more thorough discussion here of the pros/cons of each option:
Mongoose (mongodb) batch insert?
Here is an example of using MongoDB's Model.collection.insert() directly in Mongoose. Please note that if you don't have so many documents, say less than 100 documents, you don't need to use MongoDB's bulk operation (see this).
MongoDB also supports bulk insert through passing an array of
documents to the db.collection.insert() method.
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
email : { type: String, index: { unique: true } },
name : String
});
var User = mongoose.model('User', userSchema);
function saveUsers(users) {
User.collection.insert(users, function callback(error, insertedDocs) {
// Here I use KrisKowal's Q (https://github.com/kriskowal/q) to return a promise,
// so that the caller of this function can act upon its success or failure
if (!error)
return Q.resolve(insertedDocs);
else
return Q.reject({ error: error });
});
}
var users = [{email: 'foo#bar.com', name: 'foo'}, {email: 'baz#bar.com', name: 'baz'}];
saveUsers(users).then(function() {
// handle success case here
})
.fail(function(error) {
// handle error case here
});

Resources