How do I get the MongoDb connection from inside Loopback.io - mongodb

I'm writing a remote method that would be greatly enhanced by running an aggregation pipeline query.
To do that I need to get the actual mongodb connection and work with it directly.
How can I run something along the lines of
module.exports = function(ZipCodes) {
ZipCodes.pipeline = function (cb) {
//Get the MongoDB Connection
var mongodbConnection = ***whatever magic***
var result = mongodbConnection.db.zipcodes.aggregate( { $group :
{ _id : "$state",
totalPop : { $sum : "$pop" } } },
{ $match : {totalPop : { $gte : 10*1000*1000 } } } );
cb(result);
};
ZipCodes.remoteMethod('pipeline', {
returns: {arg: 'zips', type: 'array', root: false},
http: {path:'/pipeline', verb: 'get'}
});
};
I have mongo defined in my datasources.json as
{
"db": {
"name": "db",
"connector": "memory"
},
"MongoDB": {
"host": "localhost",
"port": 27017,
"name": "MongoDB",
"connector": "mongodb"
}
}

Okay, did a little more digging, mostly into the loopback and mongo connector source code. If you want to get direct access to the mongoDB connection you can, but be careful!
module.exports = function(ZipCodes) {
ZipCodes.pipeline = function (cb) {
//Get the MongoDB Connection
var mongodbConnection = ZipCodes.dataSource.connector.db;
if (!mongodbConnection) {
// may not be connected yet, you might have to do that manually:
// (careful! this is asynchronous)
ZipCodes.dataSource.connect(function(err, db) {
mongodbConnection = db;
});
}
// do whatever you need to with the mongo connection...
cb(result);
};
// ... other stuff
};

Related

Isn't it possible to concat new value to a field in MongoDB document while updating it?

I have few documents in a MongoDB Collection and each has a unique field called "requestid". Now I need to update a field "requeststatus" by concatenating a new value to an existing one in NodeJS application. I started using MongoDB for very recent and have less exposure in it's features.
After doing some research I got to know I could use "$set" with "$concat"
Updating with filter & options:
var filter = { requestid: data.requestid };
var updateDoc = { $set: { requeststatus: { $concat: ["$requeststatus","-",`. ${data.status}`] } } };
var options = { multi: true };
var jobDetails = { filter, updateDoc, options };
NodeJS code:
async function updateJobDetails(connection, data, mongoDetails){
const result = await connection.db(mongoDetails.mongoDatabase).collection(mongoDetails.collection).updateOne(data.filter, data.updateDoc, data.options);
}
This is not doing as expected, instead it's adding the new concatenated value as array of Object into MongoDB collection.
Existing document:
{
"_id": {
"$oid": "6307120d3oiu895oi9e82eea5"
},
"requestid": "123456789",
"iscancelled": true,
"organizationid": "3",
"instanceid": "172",
"offerid": "offer123",
"promotionid": "promo123",
"jobtype": "portaljob123",
"jobid": "job123",
"requeststatus": "began"
}
Updated document:
{
"_id": {
"$oid": "6307120d3oiu895oi9e82eea5"
},
"requestid": "123456789",
"iscancelled": true,
"organizationid": "3",
"instanceid": "172",
"offerid": "offer123",
"promotionid": "promo123",
"jobtype": "portaljob123",
"jobid": "job123",
"requeststatus": {
"$concat": ["$requeststatus", "-", "tigger_datalink stopped since request was cancelled"]
}
}
Is there anything that I am doing wrong here? I even tried updateMany() but of no use. Run it as many times as desired it won't concat but keep updating same value as Object Any help is appreciated here.
(Working) Updated NodeJS code:
async function updateJobDetails(connection, data, mongoDetails){
const result = await connection.db(mongoDetails.mongoDatabase).collection(mongoDetails.collection).updateOne(data.filter, [data.updateDoc], data.options);
}
In order to use an existing field's data you need to use an update with a pipeline.
Try using your updateDoc inside a pipeline, like this:
var filter = { requestid: data.requestid };
var updateDoc = { $set: { requeststatus: { $concat: ["$requeststatus","-",`. ${data.status}`] } } };
var options = { multi: true };
var jobDetails = { filter, [updateDoc], options };
See how it works on the playground example

can't update DB using put request in express.js

I'm trying to update the languages_known through post request to the mongodb collection using the body of the request but its not updating i'm newbie any help would be really appreciated.
mongodb document :
{
"_id": {
"$oid": "5d63e81c342987154cdc698e"
},
"name": "anjana",
"email": "anju#gmail.com",
"age": "22",
"languages_known": [
"Bengali",
"Kannada"
],
"phone": "684684684846"
}
server code :
app.put('/students/add',function(req,res){
var myobj={
$set:{_id:require('mongodb').ObjectID(req.body.id),languages_known:req.body.remove}
}
db.collection('students').updateOne({languages_known:req.body.add},myobj,function(err,result){
if(err){throw err}
console.log("updated")
})
})
request body :
{
"id":"5d63e81c342987154cdc698e",
"remove": ["Bengali", "Kannada"],
"add": ["Tamil", "Punjabi"]
}
I've expected to update the languages_known field using this req through postman to this id
Ok this issue is with the query, you can't update _id of a document like that, also as your requirement is not to do that, Please try this :
server code :
// replace languages_known with new languages based on _id, in case if you wanted to update the array you can use $push
app.put('/students/add', function (req, res) {
const id = require('mongodb').ObjectID(req.body.id)
let updateQuery = {
$set: { languages_known: req.body.add }
}
db.collection('students').updateOne({ _id: id }, updateQuery, function (err, result) {
if (err) { throw err }
console.log("updated")
})
})

Update Record in MonogoDB using Node-Red

I'm new in Node-Red and MongoDB.
I need to update a document in collection using Node-Red.
Here is my code in Function node:
var newMsg = msg;
newMsg.operation = 'findAndModify';
newMsg.payload = {
query: { "UserKey": "mykey" },
update: { $set: {"Locked": false } }
};
return newMsg;
But got the below error
"Monog Error: need remove or update".
Edit:
Although the below code is working fine in Mongo shell.
db.Users.findAndModify({
query: { "UserKey": "mykey" },
update: { $set: { "Locked": false } }
})
Thanks
findAndModify is not one of the supported methods for the mongo node, you will have to do it as a 2 stage process.
Use Mongo In node to do a find, then update the payload and feed it into a Mongo Out node to do a store
#phani-indra, for example, if you have a collection of users with document:
{ "chatid" : 324323,
"alert" : false,
"accesslevel" : 4,
"_id" : ObjectId("5d76b33c6ff21e0155d035a0")
}
and you want to change key "alert" to true, so your function node in NodeRED will looks something like:
var newMsg = {
'collection': 'users',
'operation': 'update',
'query': {
chatid : 324323
},
'payload': {
$set: {
alert : false
}
}
};
return newMsg;
Then feed function node into a Mongo Out node.

Is there a way to get only the found document as the result of findAndModify without the wrapping info

I have a findAndModify MongoDb request. The mongo server wraps the document in:
{
value: { ... },
lastErrorObject: { updatedExisting: true, n: 1 },
ok: 1
}
is there a way to only get the object from the value key?
{ ... }
The only way you get that kind of response is by directly running the findAndModify database command form, for example:
db.runCommand({
"findAndModify": "newdata",
"query": { "a": 1 },
"update": { "$set": { "b": 2 } },
"new": true,
"upsert": true
})
In the MongoDB shell and all driver implementations these methods are always wrapped with a collection method so that just the document which is modified ( or the original if you ask for that ) is returned in the response. In the shell the wrapping code looks like this:
function (args){
var cmd = { findandmodify: this.getName() };
for (var key in args){
cmd[key] = args[key];
}
var ret = this._db.runCommand( cmd );
if ( ! ret.ok ){
if (ret.errmsg == "No matching object found"){
return null;
}
throw "findAndModifyFailed failed: " + tojson( ret );
}
return ret.value;
}
So when you in fact just invoke that method from the collection object then you just get the document in response:
db.newdata.findAndModify({
"query": { "a": 1 },
"update": { "$set": { "b": 2 } },
"new": true,
"upsert": true
})
{ "_id" : ObjectId("5445af1ac745bf5663de72dd"), "a" : 1, "b" : 2 }
This is common to all drivers, some of which show alternate names to "findAndModify" such as "findAndUpdate" and specifically separate the "remove" and "update" functionality for each. Consult your language driver documentation for more details on other implementations, but they basically work in the same way.

How can I rename a field for all documents in MongoDB?

Assuming I have a collection in MongoDB with 5000 records, each containing something similar to:
{
"occupation":"Doctor",
"name": {
"first":"Jimmy",
"additional":"Smith"
}
Is there an easy way to rename the field "additional" to "last" in all documents? I saw the $rename operator in the documentation but I'm not really clear on how to specify a subfield.
You can use:
db.foo.update({}, {
$rename: {
"name.additional": "name.last"
}
}, false, true);
Or to just update the docs which contain the property:
db.foo.update({
"name.additional": {
$exists: true
}
}, {
$rename: {
"name.additional": "name.last"
}
}, false, true);
The false, true in the method above are: { upsert:false, multi:true }. You need the multi:true to update all your records.
Or you can use the former way:
remap = function (x) {
if (x.additional) {
db.foo.update({
_id: x._id
}, {
$set: {
"name.last": x.name.additional
}, $unset: {
"name.additional": 1
}
});
}
}
db.foo.find().forEach(remap);
In MongoDB 3.2 you can also use
db.students.updateMany({}, {
$rename: {
"oldname": "newname"
}
})
The general syntax of this is
db.collection.updateMany(filter, update, options)
https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/
You can use the $rename field update operator:
db.collection.update(
{},
{ $rename: { 'name.additional': 'name.last' } },
{ multi: true }
)
If ever you need to do the same thing with mongoid:
Model.all.rename(:old_field, :new_field)
UPDATE
There is change in the syntax in monogoid 4.0.0:
Model.all.rename(old_field: :new_field)
Anyone could potentially use this command to rename a field from the collection (By not using any _id):
dbName.collectionName.update({}, {$rename:{"oldFieldName":"newFieldName"}}, false, true);
see FYI
I am using ,Mongo 3.4.0
The $rename operator updates the name of a field and has the following form:
{$rename: { <field1>: <newName1>, <field2>: <newName2>, ... } }
for e.g
db.getCollection('user').update( { _id: 1 }, { $rename: { 'fname': 'FirstName', 'lname': 'LastName' } } )
The new field name must differ from the existing field name. To specify a in an embedded document, use dot notation.
This operation renames the field nmae to name for all documents in the collection:
db.getCollection('user').updateMany( {}, { $rename: { "add": "Address" } } )
db.getCollection('user').update({}, {$rename:{"name.first":"name.FirstName"}}, false, true);
In the method above false, true are: { upsert:false, multi:true }.To update all your records, You need the multi:true.
Rename a Field in an Embedded Document
db.getCollection('user').update( { _id: 1 }, { $rename: { "name.first": "name.fname" } } )
use link : https://docs.mongodb.com/manual/reference/operator/update/rename/
This nodejs code just do that , as #Felix Yan mentioned former way seems to work just fine , i had some issues with other snipets hope this helps.
This will rename column "oldColumnName" to be "newColumnName" of table "documents"
var MongoClient = require('mongodb').MongoClient
, assert = require('assert');
// Connection URL
//var url = 'mongodb://localhost:27017/myproject';
var url = 'mongodb://myuser:mypwd#myserver.cloud.com:portNumber/databasename';
// Use connect method to connect to the server
MongoClient.connect(url, function(err, db) {
assert.equal(null, err);
console.log("Connected successfully to server");
renameDBColumn(db, function() {
db.close();
});
});
//
// This function should be used for renaming a field for all documents
//
var renameDBColumn = function(db, callback) {
// Get the documents collection
console.log("renaming database column of table documents");
//use the former way:
remap = function (x) {
if (x.oldColumnName){
db.collection('documents').update({_id:x._id}, {$set:{"newColumnName":x.oldColumnName}, $unset:{"oldColumnName":1}});
}
}
db.collection('documents').find().forEach(remap);
console.log("db table documents remap successfully!");
}
If you are using MongoMapper, this works:
Access.collection.update( {}, { '$rename' => { 'location' => 'location_info' } }, :multi => true )