How can I add a new field from another one value? - mongodb

I have a collection "people" with documents like this one:
{
_id: "...",
name: "...",
age: "..."
}
I want to perform an update to all the documents of this collection so they have an extra field "actualName" with the exact same content like "name". I have tried this:
db.people.updateMany({}, { $set { actualName: $name } })
But I get this error:
$name is not defined
I'm doing it from MongoSH (Compass).

You can use something with aggregation updates
db.collection.update({},
[
{
"$set": {
"actualName": "$name"
}
}
])
Working Mongo playground

Starting with Mongo 4.2, you can use aggregation pipeline with update() method to update fields with values of other fields. You can do it like this:
db.collection.update({},
[
{
"$set": {
"actualName": "$name"
}
}
],
{
"multi": true
})
Here is a working example: https://mongoplayground.net/p/sqZBDLGJy48

Related

Azure CosmosDB - update the property name

I am trying to update the property name of the json in mongodb document.
{
"_id" : ObjectId("1234556789"),
"apps" : [
{
"_id" : 101,
"regions" : [
"WANAE",
"WANAF"
]
},
{
"_id" : 102,
"regions" : [
"WANAE",
"WANAF"
]
}
]
}
in the above josn, I want to change apps regions to codes. Treid below queries but did not work
db.packs.updateMany( {}, { $rename: { 'apps.$.regions': 'apps.$.codes' } } );
db.packs.updateMany( {}, { $rename: { 'apps.$[].regions': 'apps.$[].codes' } } );
any help
Update: As Joe suggested, I have a aggregation that changes the document with the changes needed and I tried updating the entire collection like below with the aggregated result
db.packs.aggregate([
{
$addFields: {
apps: {
$map: {
input: "$apps",
as: "app",
in: {
_id: "$$app._id",
did: "$$app.did",
name: "$$app.name",
codes: "$$app.regions"
}
}
}
}
},
{
$project:{
"apps.regions":0
}
},
{
$out:"packs"
}
])
As per the documentation, $out should replace the existing collection if it is exists but I received an error that says I have to supply a new collection name Please supply a collection that does not already exist to the $out stage.. Isn't $Out replace the exiting packs with new aggregated results
When you reference a field in an array of objects, like "$apps.regions", the value is an array containing all of the values of that field from all of the elements.
If you set the value of regions directly, each sub document will contain an array of arrays, probably not what you want.
renaming the field in the entire array of objects will require iterating the array, perhaps with $map or $reduce.
If you are using MongoDB 4.2, you can do that with a pipeline in an update:
db.packs.updateMany( {}, [
{$set: {
"apps": {
$map: {
input: "$apps",
in: {
$mergeObjects: [
"$$this",
{
codes: "$$this.regions"
}
]
}
}
}
}},
{$unset: "apps.regions"}
]}
If you are using an earlier version, you'll need to do that with aggregation, perhaps with $out, and then replace the original collection with the updated one.

MongoDB Shell - Add new field to all the documents from an existing field

I want to create a TTL for all the existing documents in the collection, we have a field in all the document as String type - "OrigDate": "2020-03-24"
Now I want to add another filed "updatedAt" based on this field, I tried the below in mongo shell but not working -
db.SHOP.update(
{},
{
$set:{
"updatedAt": { "$toDate": "$OrigDate"}
}
},
false,
true
)
This gives the below exception - The dollar ($) prefixed field '$toDate' in 'updatedAt.$toDate' is not valid for storage
Please help
Use an aggregation to make the new field from the existing field value, and then update it to the collection. You must use an aggregation to refer to an existing field from the collection's document.
db.test.aggregate( [
{ $addFields: { updatedAt: { "$toDate": "$OrigDate"} } }
] ).forEach( doc => db.test.updateOne( { _id: doc._id }, { $set: { updatedAt: doc.updatedAt } } ) )
This following update accepts an aggregation pipeline to specify the modifications to be applied on the collection documents: Update with an Aggregation Pipeline. Note this feature is available with MongoDB version 4.2 or later only.
db.test.updateMany(
{ },
[
{ $set: { updatedAt: { "$toDate": "$OrigDate"} } }
]
)

mongodb distinct the key of a map in an array which is a property of a document

I use mongodb,one document format is like this:
{
sid:2,
attr:[
{
key:"name",
value:"bike"
},{
key:"weight",
value:"100"
}
]
}
there is a property "attr" which is like a map with uncertain number of keys.
the whole collection look like this:
[{
sid:1,
attr:[
{
key:"name",
value:"bike"
}
]
},{
sid:2,
attr:[
{
key:"name",
value:"bike"
},{
key:"weight",
value:"100"
}
]
},{
sid:3,
attr:[
{
key:"color",
value:"red"
},{
key:"weight",
value:"100"
}
]
}]
Now, what I want to know is how many distinct keys there are,and what are them.
In this example,the distinct keys I expected should be:
["name","weight","color"]
but what about more document inserted into the collection?The data in this collection is not fixed
You need to first $unwind the attr array then you can use $group aggregation to distinct the fields.
db.collection.aggregate([
{ "$unwind": "$attr" },
{ "$group": { "_id": "$attr.key" } }
])

Using $in but matching on specific fields

I'm attempting to use $in to query a mongoDB collection. The issue I'm running up against is that the arrays are arrays of JSON objects, not just arrays of a single field. So the full object looks like this:
{
items: [
{
name: "Jacket",
other_field: "1234"
}
]
}
So if I have an array that looks like this:
[{name:"Jacket", other_field:"3456"}]
I'd like to query for documents that contain within their items array any object which has a matching name field, ignoring all other fields. Is that possible?
You can use $elemMatch for this.
Example -
db.users.find({items: { $elemMatch: {name:'Jacket'} } } )
For more understanding, you can refer to - $elemMatch
As per your question there should be two possibilities if your documents structure looks like below
{
items: [
{
name: "Jacket",
other_field: "1234"
}
]
}
In this case you use following query
db.collectionName.find({"items.name":"Jacket"},{"items":1})
so above query will return only matching items.name matching criteria.
And another more possibilities if your documents structure looks like nested as below
{
items: [
{
name: "Jacket",
other_field: "1234"
},
{
name: "Jacket",
other_field: "12345"
},
{
name: "Jacket2",
other_field: "1234e"
}
]
}
In this case should use aggregate function as below
db.collectionName.aggregate({
"$unwind": "$items"
},
{
"$match": {
"items.name": "Jacket"
}
},
{
"$project": {
"_id": 0,
"items.name": 1,
"items.other_field": 1
}
})
if I understood your requirement correctly then could could ask
db.collectionName.find({'items.name': 'Jacket'})
MongoDB automatically does the "descending down" into the arrays and pick the ones that match.
fricke

MongoDB concatenate strings from two fields into a third field

How do I concatenate values from two string fields and put it into a third one?
I've tried this:
db.collection.update(
{ "_id": { $exists: true } },
{ $set: { column_2: { $add: ['$column_4', '$column_3'] } } },
false, true
)
which doesn't seem to work though, and throws not ok for storage.
I've also tried this:
db.collection.update(
{ "_id": { $exists : true } },
{ $set: { column_2: { $add: ['a', 'b'] } } },
false, true
)
but even this shows the same error not ok for storage.
I want to concatenate only on the mongo server and not in my application.
You can use aggregation operators $project and $concat:
db.collection.aggregate([
{ $project: { newfield: { $concat: [ "$field1", " - ", "$field2" ] } } }
])
Unfortunately, MongoDB currently does not allow you to reference the existing value of any field when performing an update(). There is an existing Jira ticket to add this functionality: see SERVER-1765 for details.
At present, you must do an initial query in order to determine the existing values, and do the string manipulation in the client. I wish I had a better answer for you.
You could use $set like this in 4.2 which supports aggregation pipeline in update.
db.collection.update(
{"_id" :{"$exists":true}},
[{"$set":{"column_2":{"$concat":["$column_4","$column_3"]}}}]
)
Building on the answer from #rebe100x, as suggested by #Jamby ...
You can use $project, $concat and $out (or $merge) in an aggregation pipeline.
https://docs.mongodb.org/v3.0/reference/operator/aggregation/project/
https://docs.mongodb.org/manual/reference/operator/aggregation/concat/
https://docs.mongodb.com/manual/reference/operator/aggregation/out/
For example:
db.collection.aggregate(
[
{ $project: { newfield: { $concat: [ "$field1", " - ", "$field2" ] } } },
{ $out: "collection" }
]
)
With MongoDB 4.2 . . .
MongoDB 4.2 adds the $merge pipeline stage which offers selective replacement of documents within the collection, while $out would replace the entire collection. You also have the option of merging instead of replacing the target document.
db.collection.aggregate(
[
{ $project: { newfield: { $concat: [ "$field1", " - ", "$field2" ] } } },
{ $merge: { into: "collection", on: "_id", whenMatched: "merge", whenNotMatched: "discard" }
]
)
You should consider the trade-offs between performance, concurrency and consistency, when choosing between $merge and $out, since $out will atomically perform the collection replacement via a temporary collection and renaming.
https://docs.mongodb.com/manual/reference/operator/aggregation/merge/
https://docs.mongodb.com/manual/reference/operator/aggregation/merge/#merge-out-comparison
**
in my case this $concat worked for me ...
**
db.collection.update( { "_id" : {"$exists":true} },
[ {
"$set" : {
"column_2" : { "$concat" : ["$column_4","$column_3"] }
}
}
]
let suppose that you have a collection name is "myData" where you have data like this
{
"_id":"xvradt5gtg",
"first_name":"nizam",
"last_name":"khan",
"address":"H-148, Near Hero Show Room, Shahjahanpur",
}
and you want concatenate fields (first_name+ last_name +address) and save it into "address" field like this
{
"_id":"xvradt5gtg",
"first_name":"nizam",
"last_name":"khan",
"address":"nizam khan,H-148, Near Hero Show Room, Shahjahanpur",
}
now write query will be
{
var x=db.myData.find({_id:"xvradt5gtg"});
x.forEach(function(d)
{
var first_name=d.first_name;
var last_name=d.last_name;
var _add=d.address;
var fullAddress=first_name+","+last_name+","+_add;
//you can print also
print(fullAddress);
//update
db.myData.update({_id:d._id},{$set:{address:fullAddress}});
})
}
You can also follow the below.
db.collectionName.find({}).forEach(function(row) {
row.newField = row.field1 + "-" + row.field2
db.collectionName.save(row);
});
Find and Update Each Using For Loop
Try This:
db.getCollection('users').find({ }).forEach( function(user) {
user.full_name = user.first_name + " " + user.last_name;
db.getCollection('users').save(user);
});
Or Try This:
db.getCollection('users').find({ }).forEach( function(user) {
db.getCollection('users').update(
{ _id: user._id },
{ $set: { "full_name": user.first_name + " " + user.last_name } }
)
});