MongoDB: Setting in a document with a nested document with nested arrays with nested documents - mongodb

I want to $set a field in all documents within all arrays within a document within a document.
Basically, I want to do this
{$set : {'documentname.*anyandallstrings*.*anyandallnum*.fieldname' : value}}
A sample of the document schema is here
{
"_id" : "abc123",
"documenttoset" : {
"arrayname" : [
{
"fieldname" : "fieldvalue"
//i want to add fields here,
},
{
"fieldname2" : "fieldvalue2",
"fieldname3" : "fieldvalue3"
//here,
}
],
"arrayname2" : [
{
"fieldname4" : "fieldvalue4",
"fieldname5" : "fieldvalue5",
"fieldname6" : "fieldvalue6",
//and here.
}
]
},
}
It should add the field in question to these nested documents, and must be scalable if there are more documents and more arrays.
I did not design the schema.
How is this done? I am not sure if it is even possible.

The only way to accomplish this is to iterate through the documents and update each field individually.
import pymongo
c = pymongo.mongo_client.MongoClient(host='localhost')
db = c.local
cursor = db.test.find()
for q in cursor:
dic = q
for f in dic:
field = dic[f]
if(f != '_id'):
for a in field:
array = field[a]
for d in array:
d['newfield'] = 'value'
After you've done that you just have to find the document in question and update it.
db.test.update({'_id':'abc123'}, dic)
You'll have to use a driver (obviously). I like using python and pymongo, but there are tons of drivers out there.
If this is a db admin job I would recommend using pymongo within the python shell.
Hope it helps!!

Related

How do I update values in a nested array?

I would like to preface this with saying that english is not my mother tongue, if any of my explanations are vague or don't make sense, please let me know and I will attempt to make them clearer.
I have a document containing some nested data. Currently product and customer are arrays, I would prefer to have them as straight up ObjectIDs.
{
"_id" : ObjectId("5bab713622c97440f287f2bf"),
"created_at" : ISODate("2018-09-26T13:44:54.431Z"),
"prod_line" : ObjectId("5b878e4c22c9745f1090de66"),
"entries" : [
{
"order_number" : "123",
"product" : [
ObjectId("5ba8a0e822c974290b2ea18d")
],
"customer" : [
ObjectId("5b86a20922c9745f1a6408d4")
],
"quantity" : "14"
},
{
"order_number" : "456",
"product" : [
ObjectId("5b878ed322c9745f1090de6c")
],
"customer" : [
ObjectId("5b86a20922c9745f1a6408d5")
],
"quantity" : "12"
}
]
}
I tried using the following query to update it, however that proved unsuccessful as Mongo didn't behave quite as I had expected.
db.Document.find().forEach(function(doc){
doc.entries.forEach(function(entry){
var entry_id = entry.product[0]
db.Document.update({_id: doc._id}, {$set:{'product': entry_id}});
print(entry_id)
})
})
With this query it sets product in the root of the object, not quite what I had hoped for. What I was hoping to do was to iterate through entries and change each individual product and customer to be only their ObjectId and not an array. Is it possible to do this via the mongo shell or do I have to look for another way to accomplish this? Thanks!
In order to accomplish your specified behavior, you just need to modify your query structure a bit. Take a look here for the specific MongoDB documentation on how to accomplish this. I will also propose an update to your code below:
db.Document.find().forEach(function(doc) {
doc.entries.forEach(function(entry, index) {
var productElementKey = 'entries.' + index + '.product';
var productSetObject = {};
productSetObject[productElementKey] = entry.product[0];
db.Document.update({_id: doc._id}, {$set: productSetObject});
print(entry_id)
})
})
The problem that you were having is that you were not updating the specific element within the entries array, but rather adding a new key to the top-level of the document named product. Generally, in order to set the value of an inner document within an array, you need to specify the array key first (entries in this case) and the inner document key second (product in this case). Since you are trying to set specific elements within the entries array, you need to also specify the index in your query object, I have specified above.
In order to update the customer key in the inner documents, simply switch out the product for customer in my above code.
You're trying to add a property 'product' directly into your document with this line
db.Document.update({_id: doc._id}, {$set:{'product': entry_id}});
Try to modify all your entries first, then update your document with this new array of entries.
db.Document.find().forEach(function(doc){
let updatedEntries = [];
doc.entries.forEach(function(entry){
let newEntry = {};
newEntry["order_number"] = entry.order_number;
newEntry["product"] = entry.product[0];
newEntry["customer"] = entry.customer[0];
newEntry["quantity"] = entry.quantity;
updatedEntries.push(newEntry);
})
db.Document.update({_id: doc._id}, {$set:{'entries': updatedEntries}});
})
You'll need to enumerate all the documents and then update the documents one and a time with the value store in the first item of the array for product and customer from each entry:
db.documents.find().snapshot().forEach(function (elem) {
elem.entries.forEach(function(entry){
db.documents.update({
_id: elem._id,
"entries.order_number": entry.order_number
}, {
$set: {
"entries.$.product" : entry.product[0],
"entries.$.customer" : entry.customer[0]
}
}
);
});
});
Instead of doing 2 updates each time you could possibly use the filtered positional operator to do all updates to all arrays items within one update query.

multi updating a key along the documents of a collection using pymongo

I have lots of documents inside a collection.
The structure of each of the documents inside the collection is as it follows:
{
"_id" : ObjectId(....),
"valor" : {
"AB" : {
"X" : 0.0,
"Y" : 142.6,
},
"FJ" : {
"X" : 0.2,
"Y" : 3.33
....
The collection has currently about 200 documents and I have noticed that one of the keys inside valor has the wrong name. In this case we will say "FJ" shall be "JOF" in all the docs of the collection.
Im pretty sure it is possible to change the key in all the docs using the update function of pymongo. The problem I am facing is that when I visit the online doc available https://docs.mongodb.com/v3.0/reference/method/db.collection.update/ only explains how to change the values(which I would like to remain how they currently are and change only the keys).
This is what I have tried:
def multi_update(spec_key,key_updte):
rdo=col.update((valor.spec_key),{"$set":(valor.key_updte)},multi=True)
return rdo
print(multi_update('FJ','JOF'))
But outputs name 'valor' is not defined . I thought I shall use valor.specific_key to access to the corresponding json
how can I update a key only along the docs of the collection?
You have two problems. First, valor is not an identifier in your Python code, it's a field name of a MongoDB document. You need to quote it in single or double quotes in Python in order to make it a string and use it in a PyMongo update expression.
Your second problem is, MongoDB's update command doesn't allow you set one field to the value of another, nor to rename a field. However, you can reshape all the documents in your collection using the aggregate command with a $project stage and store the results in a second collection using a $out stage.
Here's a complete example to play with:
db = MongoClient().test
collection = db.collection
collection.delete_many({})
collection.insert_one({
"valor" : {
"AB" : {
"X" : 0.0,
"Y" : 142.6,
},
"FJ" : {
"X" : 0.2,
"Y" : 3.33}}})
collection.aggregate([{
"$project": {
"valor": {
"AB": "$valor.AB",
"FOJ": "$valor.FJ"
}
}
}, {
"$out": "collection2"
}])
This is the dangerous part. First, check that "collection2" has all the documents you want, in the desired shape. Then:
collection.drop()
db.collection2.rename("collection")
import pprint
pprint.pprint(collection.find_one())

How to get multiple document using array of MongoDb id?

I have an array of ids and I want to get all document of them at once. For that I am writing but it return 0 records.
How can I search using multiple Ids ?
db.getCollection('feed').find({"_id" : { "$in" : [
"55880c251df42d0466919268","55bf528e69b70ae79be35006" ]}})
I am able to get records by passing single id like
db.getCollection('feed').find({"_id":ObjectId("55880c251df42d0466919268")})
MongoDB is type sensitive, which means 1 is different with '1', so are "55880c251df42d0466919268" and ObjectId("55880c251df42d0466919268"). The later one is in ObjectID type but not str, and also is the default _id type of MongoDB document.
You can find more information about ObjectID here.
Just try:
db.getCollection('feed').find({"_id" : {"$in" : [ObjectId("55880c251df42d0466919268"), ObjectId("55bf528e69b70ae79be35006")]}});
I believe you are missing the ObjectId. Try this:
db.feed.find({
_id: {
$in: [ObjectId("55880c251df42d0466919268"), ObjectId("55bf528e69b70ae79be35006")]
}
});
For finding records of multiple documents you have to use "$in" operator
Although your query is fine, you just need to add ObjectId while finding data for Ids
db.feed.find({
"_id" : {
"$in" :
[ObjectId("55880c251df42d0466919268"),
ObjectId("55bf528e69b70ae79be35006")
]
}
});
Just I have use it, and is working fine:
module.exports.obtenerIncidencias386RangoDias = function (empresas, callback) {
let arraySuc_Ids = empresas.map((empresa)=>empresa.sucursal_id)
incidenciasModel.find({'sucursal_id':{$in:arraySuc_Ids}
}).sort({created_at: 'desc'}).then(
(resp)=> {
callback(resp)}
)
};
where:
empresas = ["5ccc642f9e789820146e9cb0","5ccc642f9e789820146e9bb9"]
The Id's need to be in this format : ObjectId("id").
you can use this to transform your string ID's :
const ObjectId = require('mongodb').ObjectId;
store array list in var and pass that array list to find function
var list=db.collection_name.find()
db.collection_name.find({_id:{$in:list}})
db.getCollection({your_collection_name}).find({
"_id" : {
"$in" :
[({value1}),
({value2})
]
}
})

MongoDB - update a field inside a map entry

We have a mongodb document with the following structure: One of the document's fields is a map, and each map entry has several fields of it's own.
We want a way to update the value of one field inside a specific map entry using mogodb update query.
To clarify things, if we have the document as bellow, we want to update "callBackUrl" for entry 1 in the map "urlSettings" to "yadayada.com".
Is that possible at all?
SystemSettings : {
urlSettings : {
1 : {
callBackUrl : "blabla.com",
(more fields...)
},
2 : {
...
},
...
},
...
}
check the below query :
db.collection.update(
{"SystemSettings.urlSettings.1.callBackUrl" : "blabla.com"},
{"$set":{"SystemSettings.urlSettings.1.callBackUrl" : "yadayada.com"}}
);

Override existing Docs in production MongoDB

I have recently changed one of my fields from object to array of objects.
In my production I have only 14 documents with this field, so I decided to change those fields.
Is there any best practices to do that?
As it is in my production I need to do it in a best way possible?
I got the document Id's of those collections.like ['xxx','yyy','zzz',...........]
my doc structure is like
_id:"xxx",option1:{"op1":"value1","op2":"value2"},option2:"some value"
and I want to change it like(converting object to array of objects)
_id:"xxx",option1:[{"op1":"value1","op2":"value2"},
{"op1":"value1","op2":"value2"}
],option2:"some value"
Can I use upsert? If so How to do it?
Since you need to create the new value of the field based on the old value, you should retrieve each document with a query like
db.collection.find({ "_id" : { "in" : [<array of _id's>] } })
then iterate over the results and $set the value of the field to its new value:
db.collection.find({ "_id" : { "in" : [<array of _id's>] } }).forEach(function(doc) {
oldVal = doc.option1
newVal = compute_newVal_from_oldVal(oldVal)
db.collection.update({ "_id" : doc._id }, { "$set" : { "option" : newVal } })
})
The document structure is rather schematic, so I omitted putting in actual code to create newVal from oldVal.
Since it is an embedded document type you could use push query
db.collectionname.update({_id:"xxx"},{$push:{option1:{"op1":"value1","op2":"value2"}}})
This will create document inside embedded document.Hope it helps