Is there a way to override default functions in Mongodb? - mongodb

So what I want to do is make findOne work more like it does in Meteor, but through the Mongo shell. In short I want to be able to do something like this db.collection.findOne("thisIsAnId") and have it lookup that id in that collection.
I tried loading a file that has this in it...
db.collection.findOne = function(query, fields, options){
if(typeof query === "string") {
return db.collection.originalFindOne({_id : query}, fields, options);
}
return db.collection.originalFindOne(query, fields, options);
}
Where originalFindOne would just chain to the default findOne, this didn't work at all. So after having no luck finding a way to override a default function I thought maybe I could create a new function like db.collection.simpleFindOne() or something, but I can't find a way to attach it to the mongo shell so that it will be available to any collection.
Anyone have some insight on how mongo internals work that could give me some help?

Try adding this snippet to one of your Mongo config files:
(function() {
// Duck-punch Mongo's `findOne` to work like Meteor's `findOne`
if (
typeof DBCollection !== "undefined" && DBCollection &&
DBCollection.prototype && typeof DBCollection.prototype.findOne === "function" &&
typeof ObjectId !== "undefined" && ObjectId
) {
var _findOne = DBCollection.prototype.findOne,
_slice = Array.prototype.slice;
DBCollection.prototype.findOne = function() {
var args = _slice.call(arguments);
if (args.length > 0 && (typeof args[0] === "string" || args[0] instanceof ObjectId)) {
args[0] = { _id: args[0] };
}
return _findOne.apply(this, args);
};
}
})();
I originally found the DBCollection object/class by typing db.myCollection.constructor into the Mongo shell. I then confirmed that findOne was defined on its prototype by verifying that (a) DBCollection was globally accessible, (b) that DBCollection.prototype existed, and (c) that typeof DBCollection.prototype.findOne === "function" (much like the snippet does).
Edit: Added a logic branch to also cover ObjectId-based IDs.

Related

How to take MongoDB backup without data [duplicate]

I have a mongodb instance with a lot of data, now I need to start up a new instance with the same structure without data.
how to get it done?
You can do that with the "query" option, with a query that does not return any document. Something like:
mongodump -q '{ "foo" : "bar" }'
This will dump all the dbs and indexes, you can then do a mongorestore to recreate them into another mongod instance
See documentation:
http://docs.mongodb.org/manual/reference/program/mongodump/#cmdoption--query
You can login into mongo shell and execute the following code statements to generate creating indexes statements. After that, use the statements to recreate indexes.
var collectionList = db.getCollectionNames();
for(var index in collectionList){
var collection = collectionList[index];
var cur = db.getCollection(collection).getIndexes();
if(cur.length == 1){
continue;
}
for(var index1 in cur){
var next = cur[index1];
if(next["name"] == '_id_'){
continue;
}
var unique=next["unique"]?true:false;
print("try{ db.getCollection(\""+collection+"\").createIndex("+JSON.stringify(next["key"])+",{unique:"+unique+"},{background:1})}catch(e){print(e)}");}}
There is really short and briliant script for create backup of indexes queries:
print(`// Backup indexes of : ${db.getName()} : database`);
print(`use ${db.getName()};`);
db.getCollectionNames().forEach(function (collection) {
indexes = db.getCollection(collection).getIndexes().forEach(function (index) {
if (index.name === '_id_') return; // skip defalut _id indexes
const keys = tojsononeline(index.key);
delete index.id; delete index.key; delete index.v; delete index.ns;
print(`db.${collection}.createIndex(${keys}, ${tojsononeline(index)});`);
});
});
You can run it directly from mongo shell like this:
mongo --quiet mongodb://localhost:27017/mydatabase indexes-backup.js
Output looks like:
db.user.createIndex({"user.email":1}, {"name":"userEmail", "background":true});
Based on Ivan's answer, I improved the script by adding more options like expireAfterSeconds (which was crucial for me) and an flag variable to drop indexes before creating them. dropFirst variable at the top of the script can be set to true to drop every index before creating it. Also, this script keeps existing names of the indexes.
var dropFirst = false;
for(var collection of db.getCollectionNames()) {
var indexes = db.getCollection(collection).getIndexes().filter(i => i.name !== '_id_');
if(indexes.length === 0) continue;
print(`\n// Collection: ${collection}`);
for(var index of indexes) {
var key = JSON.stringify(index.key);
var opts = [`name: "${index.name}"`, 'background: true'];
if(index['unique']) opts.push('unique: true');
if(index['hidden']) opts.push('hidden: true');
if(index['sparse']) opts.push('sparse: true');
if(index['expireAfterSeconds'] !== undefined) opts.push(`expireAfterSeconds: ${index['expireAfterSeconds']}`);
if(dropFirst) {
print(`try { db.getCollection("${collection}").dropIndex(${key}); } catch(e) { print('failed to drop ${key}:', e); }`);
}
print(`try { db.getCollection("${collection}").createIndex(${key}, {${opts.join(', ')}}) } catch(e) { print('failed to create ${key}:', e) }`);
}
}

save data in array nested on database nosql - Total.js

i have to save a json in array but i can't in
controllers/api.js
exports.install = function() {
F.cors('/api/db/*',['post'],false)
F.route('/api/db/',saveNoSql,['post']);
};
function saveNoSql(){
console.log('dentro saveNoSql');
var self = this;
var body = self.req.body;
var users = NOSQL('db');
users.find().make(function(builder){
builder.and()
builder.where('name','Report');
builder.where('entries['+0+'].name','Report');
builder.callback(function(err,model){
})
})
}
databases/db.nosql
{"name":"Report","user":"rep","password":"admin","odata":"false","entries":[{"name":"Report","appointment":[{"idOrder":"1","order":"order 1","supplier":"fornitore 1","hours":"numero ore","actions":"icone azioni"}]},{"name":"Admin","appointment":[]}]}
{"name":"Admin","user":"adm","password":"admin","odata":"true","entries":[]}
{"name":"Mike","user":"mike","password":"admin","odata":"true","entries":[]}
Here you see that i have to save a req.body in name->Report/entries->Report/appointment and i guess to do that with find() and insert() , right?
This is solution, but not much effective:
var users = NOSQL('db');
users.find().filter(doc => doc.name === 'Report' && doc.entries && doc.entries[0] && doc.entries[0].name === 'Report').callback(function(err, model) {
console.log(err, model);
});
I recommend to update all documents by adding new filter fields for much simpler filtering. Documentation: https://docs.totaljs.com/latest/en.html#api~DatabaseBuilder~builder.filter

Meteor-Mongo: Error handling for findone

I am trying to handle errors using findOne in meteor-mongo.
From this stackoverflow question, it appears that I should be able to handle errors by doing collection.findOne({query}, function(err, result){ <handleError> }, but doing so results in an errormessage:
"Match error: Failed Match.OneOf, Match.Maybe or Match.Optional validation"
The following code works:
export default createContainer((props) => {
let theID = props.params.theID;
Meteor.subscribe('thePubSub');
return {
x: theData.findOne({_id: theID}),
};
}, App);
The following code does not:
export default createContainer((props) => {
let theID = props.params.theID;
Meteor.subscribe('thePubSub');
return {
x: theData.findOne({_id: theID}, function(err,result){
if(!result){
return {}
};
}),
};
}, App);
What am I doing wrong and how should I be resolving this error? Is this a meteor specific error?
Any help is greatly appreciated!
What kind of error are you exactly trying to handle with your callback?
Meteor's findOne is different from node's mongodb driver's findOne that the post you link to uses.
The expected signature is:
collection.findOne([selector], [options])
There is no callback involved, since the method runs synchronously (but is reactive).
If you want to return a default value when the document is not found, you can simply use a JS logical OR:
// Provide an alternative value on the right that will be used
// if the left one is falsy.
theData.findOne({_id: theID}) || {};
A more rigorous approach would be to compare its type with
typeof queryResult === 'undefined'
Note that if theData collection is fed by the above subscription Meteor.subscribe('thePubSub'), I doubt Meteor will have time to populate the collection on the client by the time you query it…

mongodb how to mongodump only indexes to another mongodb instance

I have a mongodb instance with a lot of data, now I need to start up a new instance with the same structure without data.
how to get it done?
You can do that with the "query" option, with a query that does not return any document. Something like:
mongodump -q '{ "foo" : "bar" }'
This will dump all the dbs and indexes, you can then do a mongorestore to recreate them into another mongod instance
See documentation:
http://docs.mongodb.org/manual/reference/program/mongodump/#cmdoption--query
You can login into mongo shell and execute the following code statements to generate creating indexes statements. After that, use the statements to recreate indexes.
var collectionList = db.getCollectionNames();
for(var index in collectionList){
var collection = collectionList[index];
var cur = db.getCollection(collection).getIndexes();
if(cur.length == 1){
continue;
}
for(var index1 in cur){
var next = cur[index1];
if(next["name"] == '_id_'){
continue;
}
var unique=next["unique"]?true:false;
print("try{ db.getCollection(\""+collection+"\").createIndex("+JSON.stringify(next["key"])+",{unique:"+unique+"},{background:1})}catch(e){print(e)}");}}
There is really short and briliant script for create backup of indexes queries:
print(`// Backup indexes of : ${db.getName()} : database`);
print(`use ${db.getName()};`);
db.getCollectionNames().forEach(function (collection) {
indexes = db.getCollection(collection).getIndexes().forEach(function (index) {
if (index.name === '_id_') return; // skip defalut _id indexes
const keys = tojsononeline(index.key);
delete index.id; delete index.key; delete index.v; delete index.ns;
print(`db.${collection}.createIndex(${keys}, ${tojsononeline(index)});`);
});
});
You can run it directly from mongo shell like this:
mongo --quiet mongodb://localhost:27017/mydatabase indexes-backup.js
Output looks like:
db.user.createIndex({"user.email":1}, {"name":"userEmail", "background":true});
Based on Ivan's answer, I improved the script by adding more options like expireAfterSeconds (which was crucial for me) and an flag variable to drop indexes before creating them. dropFirst variable at the top of the script can be set to true to drop every index before creating it. Also, this script keeps existing names of the indexes.
var dropFirst = false;
for(var collection of db.getCollectionNames()) {
var indexes = db.getCollection(collection).getIndexes().filter(i => i.name !== '_id_');
if(indexes.length === 0) continue;
print(`\n// Collection: ${collection}`);
for(var index of indexes) {
var key = JSON.stringify(index.key);
var opts = [`name: "${index.name}"`, 'background: true'];
if(index['unique']) opts.push('unique: true');
if(index['hidden']) opts.push('hidden: true');
if(index['sparse']) opts.push('sparse: true');
if(index['expireAfterSeconds'] !== undefined) opts.push(`expireAfterSeconds: ${index['expireAfterSeconds']}`);
if(dropFirst) {
print(`try { db.getCollection("${collection}").dropIndex(${key}); } catch(e) { print('failed to drop ${key}:', e); }`);
}
print(`try { db.getCollection("${collection}").createIndex(${key}, {${opts.join(', ')}}) } catch(e) { print('failed to create ${key}:', e) }`);
}
}

Looking for help with reading from MongoDB in Node.JS

I have a number of records stored in a MongoDB I'm trying to output them to the browser window by way of a Node.JS http server. I think I'm a good portion of the way along but I'm missing a few little things that are keeping it from actually working.
The code below uses node-mongo-native to connect to the database.
If there is anyone around who can help me make those last few connections with working in node I'd really appreciate it. To be fair, I'm sure this is just the start.
var sys = require("sys");
var test = require("assert");
var http = require('http');
var Db = require('../lib/mongodb').Db,
Connection = require('../lib/mongodb').Connection,
Server = require('../lib/mongodb').Server,
//BSON = require('../lib/mongodb').BSONPure;
BSON = require('../lib/mongodb').BSONNative;
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : Connection.DEFAULT_PORT;
sys.puts("Connecting to " + host + ":" + port);
function PutItem(err, item){
var result = "";
if(item != null) {
for (key in item) {
result += key + '=' + item[key];
}
}
// sys.puts(sys.inspect(item)) // debug output
return result;
}
function ReadTest(){
var db = new Db('mydb', new Server(host, port, {}), {native_parser:true});
var result = "";
db.open(function (err, db) {
db.collection('test', function(err, collection) {
collection.find(function (err, cursor){
cursor.each( function (err, item) {
result += PutItem(err, item);
});
});
});
});
return result;
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end("foo"+ReadTest());
}).listen(8124);
console.log('Server running on 8124');
Sources:
- mongo connectivity code:
https://github.com/christkv/node-mongodb-native/blob/master/examples/simple.js
- node. http code: nodejs.org
EDIT CORRECTED CODE
Thanks to Mic below who got me rolling in the right direction. For anyone interested, the corrected solution is here:
function ReadTest(res){
var db = new Db('mydb', new Server(host, port, {}), {native_parser:true});
var result = "";
res.write("in readtest\n");
db.open(function (err, db) {
res.write("now open\n");
db.collection('test', function(err, collection) {
res.write("in collection\n");
collection.find(function (err, cursor){
res.write("found\n");
cursor.each( function (err, item) {
res.write("now open\n");
var x = PutItem(err, item);
sys.puts(x);
res.write(x);
if (item == null) {
res.end('foo');
}
});
});
});
});
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write("start\n");
ReadTest(res);
}).listen(8124);
console.log('Server running on 8124');
My guess is that you are returning result, writing the response, and closing the connection before anything is fetched from the db.
One solution would be to pass the response object to where you actually need it, something like:
function readTest(res) {
db.open(function (err, db) {
db.collection('test', function(err, collection) {
collection.find(function (err, cursor) {
res.writeHead(200, {'Content-type' : 'text/plain'});
cursor.each( function (err, item) { res.write(item); });
res.end();
...
Of course, you should also handle errors and try to avoid nesting too many levels, but that's a different discussion.
Instead of writing all the low-level Mongodb access code, you might want to try a simple library like mongous so that you can focus on your data, not on MongoDB quirks.
You might want to try mongoskin too.
Reading documents
To apply specific value filters, we can pass specific values to the find() command. Here is a SQL query:
SELECT * FROM Table1 WHERE name = 'ABC'
which is equivalent to the following in MongoDB (notice Collection1 for Table1):
db.Collection1.find({name: 'ABC'})
We can chain count() to get the number of results, pretty() to get a readable result. The results can be further narrowed by adding additional parameters:
db.Collection1.find({name: 'ABC', rollNo: 5})
It's important to notice that these filters are ANDed together, by default. To apply an OR filter, we need to use $or. These filters will be specified depending upon the structure of the document. Ex: for object attribute name for an object school, we need to specify filter like "school.name" = 'AUHS'
We're using here the DOT notation, by trying to access a nested field name of a field school. Also notice that the filters are quoted, without which we'll get syntax errors.
Equality matches on arrays can be performed:
on the entire arrays
based on any element
based on a specific element
more complex matches using operators
In the below query:
db.Collection1.find({name: ['ABC','XYZ']})
MongoDB is going to identify documents by an exact match to an array of one or more values. Now for these types of queries, the order of elements matters, meaning that we will only match documents that have ABC followed by XYZ and those are the only 2 elements of the array name
{name:["ABC","GHI","XYZ"]},
{name:["DEF","ABC","XYZ"]}
In the above document, let's say that we need to get all the documnts where ABC is the first element. So, we'll use the below filter:
db.Schools.find({'name.0': 'ABC' })