MongoDB/C# Update Collection entries - c#

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

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);

How get List of JSON Objects from within a JSON object (non-array) (NOT Deserializing)

I'm using C# with Json.NET NuGet.
I have JSON that looks like this:
{
"pre" : "",
"options": {
"0001" : {
"id" : "0001",
"desc" : "first"
},
"0002" : {
"id" : "0002",
"desc" : "second"
},
"0003" : {
"id" : "0003",
"desc" : "third"
}
},
"post" : ""
}
How can I query the above Json to get a List<JObject> with the 3 option items in it?
Or/Also How can I get just the second item where item 2 should be:
{
"id" : "0002",
"desc" : "second"
}
I've tried stuff like
var items = json.SelectTokens("options[*]").ToList();
and
var item = json.SelectTokens("options[1]");
but those clearly don't work.
EDIT:
In case I wasn't clear I DO NOT want to deserialize. I want a List<JObject>.
Your json is valid but your thinking of this json is not quite accurate.
You are thinking that options have a list of objects that you want to iterate over, but, thats not the case. options is not a list but an object that has more objects within it.. Not an array.
You can access each of the element within the JObject by first looking up its properties. Properties are 0001, 0002 etc. Once you have those, you can iterate over the properties of options and get the values you need.
JObject options = (JObject)JObject.Parse(json)["options"];
// Get a list of all tokens within this object.
List<JObject> allObjects = new List<JObject>();
foreach (var node in options.Properties())
allObjects.Add((JObject)options[node.Name]);
// Access the IDs
allObjects.ForEach(x => Console.WriteLine(x["id"].ToString()));
// Access the 2nd ID only
Console.WriteLine(); // Just to space it out.
Console.WriteLine(allObjects[1]["id"].ToString());
Output
0001
0002
0003
0002
You could create the required List using Linq. For example,
var list = ((JObject)JObject.Parse(str)["options"])
.Properties()
.Select(x=>x.Value)
.Cast<JObject>()
.ToList();
For accessing the second element, you could use
var secondId = (string)list[1]["id"];
var secondDesc = (string)list[1]["desc"];
I was actually close but trying too hard:
var items = json.SelectToken("options").ToList();
I was unable to figure out how to get a single option from a query but since I got the whole list I did it like this:
var item = json.SelectToken("options").ToList()[1];

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

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.

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.

MongoDB Get names of all keys in collection using c#

How can I get names of all the keys in a MongoDB collection using c#.
I am using mongocsharpdriver.
I am able to get all the records using
var collection1 = database1.GetCollection("Games").FindAll();
Now I need the key names to display/use it. I need key names of collection that I have fetched.
e.g. If I have collection which
{ "_id" : ObjectId("c3"), "GameID" : 20, "GameName" : "COD5", "Cost" : 100}
{ "_id" : ObjectId("c4"), "GameID" : 21, "GameName" : "NFS", "Publisher" : "EA"}
{ "_id" : ObjectId("c5"), "GameID" : 22, "GameName" : "CS", "Cost" : 200}
So I should get list of keys like GameID, GameName, Cost, Publisher.
I also went through MongoDB Get names of all keys in collection but was not able to implement it, didnot understood it & got problem with mapreduce.
Inspired from the link in your question:
string map = #"function() {
for (var key in this) { emit(key, null); }
}";
string reduce = #"function(key, stuff) { return null; }";
string finalize = #"function(key, value){
return key;
}";
MapReduceArgs args = new MapReduceArgs();
args.FinalizeFunction = new BsonJavaScript(finalize);
args.MapFunction = new BsonJavaScript(map);
args.ReduceFunction = new BsonJavaScript(reduce);
var results = collection1.MapReduce(args);
foreach (BsonValue result in results.GetResults().Select(item => item["_id"]))
{
Console.WriteLine(result.AsString);
}
A very inefficient, but simple way to do this is
HashSet<string> keys = new HashSet<string>();
foreach (var rover in collection1.FindAll())
{
rover.Names.ToList().ForEach(p => keys.Add(p));
}
Keep in mind that finding the set of keys, no matter how it's implemented, will always have to iterate the entire collection, so it will be terribly slow on larger collections.
It makes sense to use Map/Reduce for this problem on larger collections, because that avoids all the data transfer and deserialization overhead that is incurred by the solution I posted above, but you should generally try to avoid doing something like this at all. At least, don't do it where it's needed synchronously.
If you somehow need to know the set of all fields quickly, you're better off keeping track of the fields during writes and store the list of fields in a separate collection somewhere.

Categories