C# - MongoDB - Update an element inside a Nested Document - c#

I have a MongoDB Document as follows
{
"_id" : ObjectId("5a55775cbd12982cc063c71a"),
"ShipmentNumber" : "00004000000048652254",
"Cartons" : [
{
"_id" : ObjectId("5a5575bcbd12982cc063b718"),
"CartonNumber" : "0076013926580S",
"Skus" : [
{
"_id" : ObjectId("5a5575bcbd12982cc063b719"),
"SkuNumber" : "06577647",
"ShippedQuantity" : 12,
},
{
"_id" : ObjectId("5a5575bcbd12982cc063b519"),
"SkuNumber" : "06577657",
"ShippedQuantity" : 15,
}
],
"IsScanned" : false,
},
}
How can I update the "ShippedQuantity" for a particular Sku element based on its "_id" in C# code ?
I tried something like below. But it is not working.
Getting error message like
cannot use the part (Cartons of Cartons.$[].Skus.$.ShippedQuantity) to
traverse the element
var filter = Builders<BsonDocument>.Filter.Eq("Cartons.Skus._id", new ObjectId("5a5575bcbd12982cc063b519"));
var update = Builders<BsonDocument>.Update.Set("Cartons.$[].Skus.$.ShippedQuantity", 50)
I am facing difficulties when I try to update multi level documents.
(In this case I have a list of Cartons and each carton will have its own list of skus and I need to update a specific sku's element)
Please provide a solution or alternative approach to update this inner level (more than 2 levels) documents in MongoDB using C#.
I updated my MongoDB server to the latest 3.6.1. But that is also not helping.
Thanks for your help.

First, you need to run this command in your MongoDB to apply the new features of version 3.6.1 db.adminCommand( { setFeatureCompatibilityVersion: "3.6" } )
Here is the code you need for that update:
var filter = Builders<YOUR_CLASS>.Filter.Eq("_id", new ObjectId("5a55775cbd12982cc063c71a"));
var update = Builders<YOUR_CLASS>.Update.Set("Cartons.$[i].Skus.$[j].ShippedQuantity", 50);
var arrayFilters = new List<ArrayFilterDefinition>
{
new BsonDocumentArrayFilterDefinition<Setup>(new BsonDocument("i._id", new ObjectId("5a5575bcbd12982cc063b718"))),
new BsonDocumentArrayFilterDefinition<Setup>(new BsonDocument("j._ID", new ObjectId("5a5575bcbd12982cc063b719")))
};
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var (updated, errorMessage) = await UpdateOneAsync(filter, update, updateOptions);
Additionally, you can run set these settings in your MongoDB to look at your final queries and run them manually in RoboMongo or Studio 3T to debug them:
db.setProfilingLevel(2) -> to view query logs under C:\data\log\mongod.log
db.setLogLevel(5) -> to view query logs under C:\data\log\mongod.log
look for the "UPDATE" query in the log file. After that, you can reset the log setting back to 0
db.setProfilingLevel(0)
db.setLogLevel(0)
I've had the same problem and asked the same question Here
Have a look at it.

Related

Whole MongoDB documented deleted when filtering on a subdocument - c# delete

I've created my MongoDB documents below with subdocuments/arrays, however the arrays aren't named and I would like to delete the whole subdocument if a match is found on an elements within a subdocument.
For example, if a match is found on userID and userLogs.Name. My query is deleting the whole documents instead of only the userLog array. I've also tried other methods such as Pull and PullFilter whilst researching this topic but it doesn't seem to work with this structure, please can you advise on whether there is a way or if I will have to change my document structure?
Document
{
"_id" : ObjectId("43535"),
"userID" : "1",
"userLogs" : [
{
"logID" : 1,
"Name" : "Book 1",
"Genre" : "Fiction",
....
},
{
"logID" : 2,
"Name" : "Book 2",
"Genre" : "Non-Fiction",
....
}
]
}
C# Code behind
var collection = db.GetCollection<BsonDocument>("Users");
var arrayFilter = Builders<BsonDocument>.Filter.Eq("userID", uID) &
Builders<BsonDocument>.Filter.Eq("userLogs.Name", name);
collection.DeleteOne(arrayFilter);
Thank you Christoph! I also solved this using the following method:
var query = Builders<BsonDocument>.Filter.Eq("UserID", uID) &
Builders<BsonDocument>.Filter.Eq("userLogs.Name", name);
var update = Builders<BsonDocument>.Update.Pull("userLogs", new BsonDocument(){
{ "Name", name }
});
collection.UpdateOne(query, update);

MongoDB updateOne

I am trying to update an existing Mongo record, but am getting an "Additional information: Element name 'ID' is not valid'." error
I have a a BsonDocument "document" containing data that I retrieve from another source that looks like this:
{ "ID" : "ABCecdcf9851efbf0ef66953", ListingKey : "234534345345", "Created" : ISODate("2017-08-04T00:31:23.357Z"), "Modified" : ISODate("2017-08-04T00:31:23.358Z"), "Field1" : 1, "Field2" : "0.09", "Field3" : "1.10", "Field4" : "1", "Field5" : "1" }
Here is the C# code that I have written:
var collection = db.GetCollection<BsonDocument>("MyCollection");
//Hard coded for testing
var filter = Builders<BsonDocument>.Filter.Eq("ListingKey", "234534345345");
collection.UpdateOne(filter, document);
Is this related to the BsonDocument that I am trying to use to update? I found this documentation, which causes me to think that this is the cause. If so, is there a way to do an update with the format I have been provided?
https://docs.mongodb.com/getting-started/csharp/update/
I had a process working where it would delete the document and then add a new document, but for efficiency's sake I need this to update. Ideally it will only update the fields that are present in the BsonDocument and keep the existing fields in the Mongo document as is.
My problem was because I did not have the correct value when trying to update. My code works with this:
var collection = db.GetCollection<BsonDocument>("MyCollection");
//Hard coded for testing
var filter = Builders<BsonDocument>.Filter.Eq("ListingKey", "234534345345");
var update = Builders<BsonDocument>.Update.Set("Created", DateTime.UtcNow);
foreach (BsonElement item in document)
{
update = update.Set(item.Name, item.Value);
}
var result = collection.UpdateOne(filter, update);
I had to convert my string into an update BsonDocument.

How to implement MongoDB nested $elemMatch Query in C#

I have a MongoDB collection in the following format.
{
"_id" : ObjectId("56c6f03ffd07dc1de805e84f"),
"Details" : {
"a" : [
[ {
"DeviceID" : "log0",
"DeviceName" : "Dev0"
},
{
"DeviceID" : "log1",
"DeviceName" : "Dev1"
}
],
[ {
"DeviceID" : "Model0",
"DeviceName" : "ModelName0"
},
{
"DeviceID" : "Model1",
"DeviceName" : "ModelName1"
}
]
]
}
}
And I am trying to fetch all the documents where the DeviceName in array "a" contains a particular value, say "Name0". However I could get the desired result while using below Mongo query:
db.test_collection.find({"Details.a":{$elemMatch:{$elemMatch:{DeviceName : /.*Name0.*/}}}});
Now I am struggling to implement the above query in C#. Can anyone guide me with that?
so far I have tried the below code and it was not working as expected
query = Query.And(Query.ElemMatch("Details.a", Query.And(Query.ElemMatch("DeviceName", Query.Matches("DeviceName", new BsonRegularExpression("Name0"))))));
Thanks in advance
Well, honestly writing queries in C# are bit tricky but you can always play a trick.
var bsonQuery = "{'Details.a':{$elemMatch:{$elemMatch:{DeviceName : /.*Name0.*/}}}}";
var filter = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<BsonDocument>(bsonQuery);
var result = col.FindSync (filter).ToList();
I'm deserializing a plain MongoDB queries into a BsonDocument which in return I'm passing to FindAsync as filter.
In the end, you'll have desired outcome in variable result.
Note: I'm assuming MongoDB connection has been established and variable col holds reference to MongoDB collection.
EDIT: Please see following link https://groups.google.com/forum/#!topic/mongodb-csharp/0dcoVlbFR2A. Now it's confirmed that C# driver doesn't support nameless filters so writing above query using Buidlers<BsonDocument>.Filter at moment is not supported.
Long story short, you are left with only one choice and that is to query as I mentioned above in my solution.

Adding OfType<T> on MongoCollection "breaks" the behaviour of UpdateOneAsync

I've run into what I can only understand to be a bug in the C# driver.
This gist illustrates the problem.
If I run
collection.UpdateOneAsync(
"{ \"_id\" : ObjectId(\"5656277cd4d37b13b4e7e009\"), \"Addresses.Index\" : 4 },
"{ \"$set\" : { \"Addresses.$\" : { \"_t\" : [\"Address\", \"EmailAddress\"], \"Index\" : 4, \"MailTo\" : \"Never#home.com\" } } }")
I get the desired result.
If however I use the Builders to build the filter definition and update definition like so:
var filter = Builders<Person>
.Filter
.And(Builders<Person>.Filter.Eq(p => p.Id, person.Id),
Builders<Person>.Filter.Eq("Addresses.Index", 4));
var update = Builders<Person>.Update.Set("Addresses.$", new EmailAddress { Index = 4, MailTo = "Never#home.com" });
then I must change my call to update to be
await collection.OfType<Person>().UpdateOneAsync(filter, update);
and the call to OfType results in the wrong address being replaced.
This is unrelated to the MongoDB C# driver, but it seems to be a bug in MongoDB itself when the type is used in the query.
You can see that this causes the same issue without using OfType, but specifying the type field explicitly (i.e. "_t"):
var filter = Builders<Person>.Filter.And(
new BsonDocument("_t", "Person"),
Builders<Person>.Filter.Eq(p => p.Id, person.Id),
Builders<Person>.Filter.Eq("Addresses.Index", 4));
var update = Builders<Person>.Update.Set(
"Addresses.$",
new EmailAddress { Index = 4, MailTo = "Never#home.com" });
await db.GetCollection<Person>("Objects").UpdateOneAsync(filter, update);
You can see the query this will generate with this piece of code:
Console.WriteLine(db.GetCollection<Person>("Objects").Find(filter));
And the query is the following which is perfectly correct:
{ "_t" : "Person", "_id" : ObjectId("5656356f64c22e5d38aeb92e"), "Addresses.4 }

MongoDB/C# Update Collection entries

Hello I have a mongoDB Collection called Nodes whose structure is as follows:
{
"_id" : new BinData(3, "Ljot2wCBskWG9VobsLA0zQ=="),
"logicalname" : "Test Node",
"host" : "eh5tmb054pc",
"port" : 104,
"appendtimestamp" : false,
"studies" : ["1.3.12.2.1107"],
"tests" : [],
"mainentries" : [{
"_id" : "1.3.12.2.1107",
"Index" : 0,
"Type" : "Study"
}]
}
I created a new key called "mainentries" which is now storing the "studies" and "tests". So in order to support my new versions without hassle, I now want to write a method in my Setup Helper, which would enable me to read this collection - Check if studies,tests exists , If yes add the key "mainentries" and remove the studies/tests key.
My question is: What kind of query must I use to reach each collection of Nodes to check for the fields and update. I am using the MongoDB-CSharp community driver.
Would appreciate any help and pointers.
You can simply check whether the field(s) still exist(s):
var collection = db.GetCollection<Node>("nodes");
var nodes = collection.Find(Query.And( // might want Query.Or instead?
Query<Node>.Exists(p => p.Tests),
Query<Node>.Exists(p => p.Studies)).SetSnapshot();
foreach(var node in nodes) {
// maybe you want to move the Tests and Studies to MainEntries here?
node.MainEntries = new List<MainEntries>();
node.Test = null;
node.Studies = null;
collection.Update(node);
}
If you don't want to migrate the data, but just remove the fields and create new ones, you can also do in a simple batch update using $exists, $set and $remove

Categories