Use of $sum inside set - Mongodb - mongodb

I have the following problem.
I want to create inside my mongodb database a new field for each documentation. This field should consist as a value the sum of two other fields of the documentation.
I tried this:
db.collection('restaurants').updateMany(
{}, { $set:
{ allScores: {$sum: "$grades.score"} }
}
)
but it doesn't work. I get the following error:
The dollar ($) prefixed field '$sum' in 'allScores.$sum' is not valid for storage.
Why can't I use $sum inside of $set?
And what can I do instead?
The database I used can be found here: https://www.w3resource.com/mongodb-exercises/
Thanks!
Julia
const MongoClient = require('mongodb').MongoClient;
ObjectID = require('mongodb').ObjectID;
// Connection URL
const url = 'mongodb://localhost:27017';
// Database Name
const dbName = 'myproject';
// Use connect method to connect to the server
MongoClient.connect(url, { useNewUrlParser: true }, function(error, client) {
if(error) {
return console.log('An error occured!')
}
console.log("Connected successfully to server");
const db = client.db(dbName);
var objectId = new ObjectID();
db.collection('restaurants').updateMany(
{}, { $set:
{ allScores: {$sum: "$grades.score"} }
}
)

You can use aggregate() with $out to do that:
db.collection('restaurants').aggregate([
{$addFields : {allScores : {$sum : "$grades.score"}}},
{$out : "restaurants"}
])
What this code does:
1- Find all documents inside restaurants;
2- Add the field allScores to each document;
3- Save all documents back to the collection restaurants;

Related

Mongodb find array of ids

I have one array which has document ids.:
var ids = [ '5b3c7db4c079dc17dc75fc26', '5b3c7db4c079dc17dc75fc28' ]
I have one collection called Machines with documents inside.
I am trying to get documents from Machines collection using ids which are in my array.
Machines.find({ _id : { $in : ids } }).fetch();
this returns []
Try this:
var ids = [ ObjectId("5b3c7db4c079dc17dc75fc26"), ObjectId("5b3c7db4c079dc17dc75fc28") ]
Because Mongodb stores id as ObjectId("Actual Id")
Your
var ids = [ '5b3c7db4c079dc17dc75fc26', '5b3c7db4c079dc17dc75fc28' ]
looks like they're either hex string or native MongoDB BSON type ObjectID.
Try this for Meteor's Mongo:
import { Mongo } from 'meteor/mongo';
const ids = [
new Mongo.ObjectID('5b3c7db4c079dc17dc75fc26'),
new Mongo.ObjectID('5b3c7db4c079dc17dc75fc28'),
]
Machines.find({ _id : { $in : ids } }).fetch();
For better syntax, use .map() to get a new array of Mongo.ObjectID type IDs.
import { Mongo } from 'meteor/mongo';
const ids = ['5b3c7db4c079dc17dc75fc26', '5b3c7db4c079dc17dc75fc28', ...];
const mongoIds = ids.map(id => new Mongo.ObjectID(id));
Machines.find({ _id : { $in : mongoIds } }).fetch();
// if ids were ObjectIDs instead of literal strings
const objectIdToMongoIds = ids.map(id => new Mongo.ObjectID(id.toString()));
Machines.find({ _id: { $in: objectIdToMongoIds } });
meteor mongo es6 functional-programming
You can also try Machine.findById(req.params.id, () => {}

mongoose how to project protected fields on update

I have following model in mongoose. projects field is protected.
var UserProjectSchema = new Schema({
user : ObjectId
, projects : {type : [ObjectId], select:false} //protected field
, projectCount : Number
});
I want that protected field after update so that I could return the new set of projects.
UserProjectSchema.statics.addProject = function(userId, projectId) {
UserProject.findOneAndUpdate({
user:userId
},
{
$addToSet: {"projects" : projectId}
, $inc : {"projectCount" : 1}
},
{
upsert : true
//project : '+projects' it won't work
},
function(err, doc){
//doc.projects is undefined
// UserProject.findOne({user:userId},'+projects', function(err, doc){
// doc.projects is now available but this extra query ???
//})
});
}
var UserProject = mongoose.model('user_projects', UserProjectSchema);
Mongoose returns the updated document after successful query but lacks to specify the fields to project.
Is there any way to specify what fields to project after updating in mongoose so that I could remove the extra query ?
Include a select parameter in your options param and list all the fields you would like to project.
{
upsert : true,
select:{"projects":1} // all the fields you would want to select
}

How to dynamically $set a subdocument field in mongodb? [duplicate]

This question already has answers here:
Nodejs Mongo insert into subdocument - dynamic fieldname
(2 answers)
Closed 8 years ago.
I've run into a situation where I need to dynamically update the value of a field in a subdocument. The field may or may not already exist. If it doesn't exist, I'd like mongo to create it.
Here's an example document that would be found in my Teams collection, which is used to store members of any given team:
{
_id : ObjectId('JKS78678923SDFD678'),
name : "Bob Lawblaw",
status : "admin",
options : {
one : "One",
two : "Two"
}
}
And here's the query I'm using (I'm using mongojs as my mongo client) to try and update (or create) a value in the options subdocument:
var projectID = 'JKS78678923SDFD678';
var key = 'Three';
var value = 'Three';
Teams.findAndModify({
query: {
projectID:mongojs.ObjectId(projectID)
},
update: {
$set : { options[key] : value }
},
upsert: true,
multi: false,
new: true
},
function(error, result, lastErrorObject){
console.log(result);
});
But I can't get it to 'upsert' the value.
I also found this similar question, but that method didn't work either:
Nodejs Mongo insert into subdocument - dynamic fieldname
Thanks in advance for any help.
Figured this out.
Essentially, you need to construct a 'placeholder' object of the sub-document you're trying to update before running the query, like so:
var projectID = 'JKS78678923SDFD678';
var key = 'Three';
var value = 'Three';
var placeholder = {};
placeholder['options.' + key] = value;
Teams.findAndModify({
query: {
projectID:mongojs.ObjectId(projectID)
},
update: {
$set : placeholder
},
upsert: true,
multi: false,
new: true
},
function(error, result, lastErrorObject){
console.log(result);
});
This updates any fields that already exist, and creates the field/value pair if it didn't already exist.

How to replace substring in mongodb document

I have a lot of mongodb documents in a collection of the form:
{
....
"URL":"www.abc.com/helloWorldt/..."
.....
}
I want to replace helloWorldt with helloWorld to get:
{
....
"URL":"www.abc.com/helloWorld/..."
.....
}
How can I achieve this for all documents in my collection?
db.media.find({mediaContainer:"ContainerS3"}).forEach(function(e,i) {
e.url=e.url.replace("//a.n.com","//b.n.com");
db.media.save(e);
});
Nowadays,
starting Mongo 4.2, db.collection.updateMany (alias of db.collection.update) can accept an aggregation pipeline, finally allowing the update of a field based on its own value.
starting Mongo 4.4, the new aggregation operator $replaceOne makes it very easy to replace part of a string.
// { URL: "www.abc.com/helloWorldt/..." }
// { URL: "www.abc.com/HelloWo/..." }
db.collection.updateMany(
{ URL: { $regex: /helloWorldt/ } },
[{
$set: { URL: {
$replaceOne: { input: "$URL", find: "helloWorldt", replacement: "helloWorld" }
}}
}]
)
// { URL: "www.abc.com/helloWorld/..." }
// { URL: "www.abc.com/HelloWo/..." }
The first part ({ URL: { $regex: /helloWorldt/ } }) is the match query, filtering which documents to update (the ones containing "helloWorldt") and is just there to make the query faster.
The second part ($set: { URL: {...) is the update aggregation pipeline (note the squared brackets signifying the use of an aggregation pipeline):
$set is a new aggregation operator (Mongo 4.2) which in this case replaces the value of a field.
The new value is computed with the new $replaceOne operator. Note how URL is modified directly based on the its own value ($URL).
Before Mongo 4.4 and starting Mongo 4.2, due to the lack of a proper string $replace operator, we have to use a bancal mix of $concat and $split:
db.collection.updateMany(
{ URL: { $regex: "/helloWorldt/" } },
[{
$set: { URL: {
$concat: [
{ $arrayElemAt: [ { $split: [ "$URL", "/helloWorldt/" ] }, 0 ] },
"/helloWorld/",
{ $arrayElemAt: [ { $split: [ "$URL", "/helloWorldt/" ] }, 1 ] }
]
}}
}]
)
Currently, you can't use the value of a field to update it. So you'll have to iterate through the documents and update each document using a function. There's an example of how you might do that here: MongoDB: Updating documents using data from the same document
Using mongodump,bsondump and mongoimport.
Sometimes the mongodb collections can get little complex with nested arrays/objects etc where it would be relatively difficult to build loops around them. My work around is kinda raw but works in most scenarios regardless of complexity of the collection.
1. Export The collection using mongodump into .bson
mongodump --db=<db_name> --collection=<products> --out=data/
2. Convert .bson into .json format using bsondump
bsondump --outFile products.json data/<db_name>/products.bson
3. Replace the strings in the .json file with sed(for linux terminal) or with any other tools
sed -i 's/oldstring/newstring/g' products.json
4. Import back the .json collection with mongoimport with --drop tag where it would remove the collection before importing
mongoimport --db=<db_name> --drop --collection products <products.json
Alternatively you can use --uri for connections in both mongoimport
and mongodump
example
mongodump --uri "mongodb://mongoadmin:mystrongpassword#10.148.0.7:27017,10.148.0.8:27017,10.148.0.9:27017/my-dbs?replicaSet=rs0&authSource=admin" --collection=products --out=data/
To replace ALL occurrences of the substring in your document use:
db.media.find({mediaContainer:"ContainerS3"}).forEach(function(e,i) {
var find = "//a.n.com";
var re = new RegExp(find, 'g');
e.url=e.url.replace(re,"//b.n.com");
db.media.save(e);
});
nodejs. Using mongodb package from npm
db.collection('ABC').find({url: /helloWorldt/}).toArray((err, docs) => {
docs.forEach(doc => {
let URL = doc.URL.replace('helloWorldt', 'helloWorld');
db.collection('ABC').updateOne({_id: doc._id}, {URL});
});
});
The formatting of my comment to the selected answer (#Naveed's answer) has got scrambled - so adding this as an answer. All credit goes to Naveed.
----------------------------------------------------------------------
Just awesome.
My case was - I have a field which is an array - so I had to add an extra loop.
My query is:
db.getCollection("profile").find({"photos": {$ne: "" }}).forEach(function(e,i) {
e.photos.forEach(function(url, j) {
url = url.replace("http://a.com", "https://dev.a.com");
e.photos[j] = url;
});
db.getCollection("profile").save(e);
eval(printjson(e));
})
This can be done by using the Regex in the first part of the method replace and it will replace the [all if g in regex pattern] occurrence(s) of that string with the second string, this is the same regex as in Javascript e.g:
const string = "www.abc.com/helloWorldt/...";
console.log(string);
var pattern = new RegExp(/helloWorldt/)
replacedString = string.replace(pattern, "helloWorld");
console.log(replacedString);
Since the regex is replacing the string, now we can do this is MongoDB shell easily by finding and iterating with each element by the method forEach and saving one by one inside the forEach loop as below:
> db.media.find()
{ "_id" : ObjectId("5e016628a16075c5bd26fbe3"), "URL" : "www.abc.com/helloWorld/" }
{ "_id" : ObjectId("5e016701a16075c5bd26fbe4"), "URL" : "www.abc.com/helloWorldt/" }
>
> db.media.find().forEach(function(o) {o.URL = o.URL.replace(/helloWorldt/, "helloWorld"); printjson(o);db.media.save(o)})
{
"_id" : ObjectId("5e016628a16075c5bd26fbe3"),
"URL" : "www.abc.com/helloWorld/"
}
{
"_id" : ObjectId("5e016701a16075c5bd26fbe4"),
"URL" : "www.abc.com/helloWorld/"
}
> db.media.find()
{ "_id" : ObjectId("5e016628a16075c5bd26fbe3"), "URL" : "www.abc.com/helloWorld/" }
{ "_id" : ObjectId("5e016701a16075c5bd26fbe4"), "URL" : "www.abc.com/helloWorld/" }
>
If you want to search for a sub string, and replace it with another, you can try like below,
db.collection.find({ "fieldName": /.*stringToBeReplaced.*/ }).forEach(function(e, i){
if (e.fieldName.indexOf('stringToBeReplaced') > -1) {
e.content = e.content.replace('stringToBeReplaced', 'newString');
db.collection.update({ "_id": e._id }, { '$set': { 'fieldName': e.fieldName} }, false, true);
}
})
Now you can do it!
We can use Mongo script to manipulate data on the fly. It works for me!
I use this script to correct my address data.
Example of current address: "No.12, FIFTH AVENUE,".
I want to remove the last redundant comma, the expected new address ""No.12, FIFTH AVENUE".
var cursor = db.myCollection.find().limit(100);
while (cursor.hasNext()) {
var currentDocument = cursor.next();
var address = currentDocument['address'];
var lastPosition = address.length - 1;
var lastChar = address.charAt(lastPosition);
if (lastChar == ",") {
var newAddress = address.slice(0, lastPosition);
currentDocument['address'] = newAddress;
db.localbizs.update({_id: currentDocument._id}, currentDocument);
}
}
Hope this helps!
db.filetranscoding.updateMany({ profiles: { $regex: /N_/ } },[{$set: { profiles: {$$replaceAll: { input: "$profiles", find:"N_",replacement: "" }},"status":"100"}}])
filetranscoding -- Collection Name
profiles -- ColumnName in which you want to update
/N_/ -- String which you are searching (where Condition )
find:"N_",replacement: "" -- N_ which u want to remove "" from which you want to remove here we are taking blank String

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 )