How to update item from array nested within array - c#

I'm using MongoDB 4.0 via the latest C# driver (v2.7.0 at this time). I have a document which has Options and Options have Inventory. So in other words, an array of inventory is nested within an array of options. How do I get down to the inventory level and update the inventory only?
Here's what my document looks like in JSON form:
{
"Options": [
{
"Id": 1,
"Description": "This is one option",
"Inventory": [
{
"Id": 1,
"Name": "Box of stuff"
},
{
"Id": 2,
"Name": "Another box of stuff"
}
]
},
{
"Id": 2,
"Description": "This a second option",
"Inventory": [
{
"Id": 1,
"Name": "Box of stuff"
},
{
"Id": 2,
"Name": "Another box of stuff"
}
]
}
]
}
Using the C# driver, how do I change the name of a single inventory item within a single option, if I know the Id of the option and the Id of the inventory item?

In MongoDB 4.0 you can use the $[<identifier>] syntax and add ArrayFilters to UpdateOptions parameter:
var filter = Builders<Model>.Filter.Empty;
var update = Builders<Model>.Update.Set("Options.$[option].Inventory.$[inventory].Name", "New name");
var arrayFilters = new List<ArrayFilterDefinition>();
ArrayFilterDefinition<BsonDocument> optionsFilter = new BsonDocument("option.Id", new BsonDocument("$eq", optionId));
ArrayFilterDefinition<BsonDocument> inventoryFilter = new BsonDocument("inventory.Id", new BsonDocument("$eq", inventoryId));
arrayFilters.Add(optionsFilter);
arrayFilters.Add(inventoryFilter);
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
var result = DefaultCollection.UpdateOne(filter, update, updateOptions);
That will uniquely identify Inventory item that needs to be updated inside Options

Related

Mongo DB remove nested object

I want to remove a data in the my document. I bolded the id I want to remove
My Code like that but "This code deletes all KeyResultActions with same Id";
var filter = builder.Eq("Id", ObjectId.Parse(objectiveId))
& builder.Eq("KeyResults.Id", ObjectId.Parse(keyResultId))
& builder.Eq("KeyResults.KeyResultActions.Id", ObjectId.Parse(actionId));
var update = Builders<Objective>.Update.PullFilter("KeyResults.$[].KeyResultActions",
Builders<KeyResultAction>.Filter.Eq(x => x.Id, ObjectId.Parse(actionId)));
My document like that;
{
"_id": "**6311d1612559020ef536cb6f**",
"KeyResults": [
{
"_id": "6311d1612559020ef536cb69",
"Title": "Test KeyResult -1 ",
"Description": "Test KeyResult Desc -1",
"KeyResultActions": [
{
"_id": "630f5d4ebb4428127b11fb8e"
},
{
"_id": "630f5d4ebb4428127b11fb8f"
}
]
},
{
"_id": "**6311d1612559020ef536cb6b**",
"Title": "Test KeyResult -2",
"Description": "Test KeyResult Desc -2",
"KeyResultActions": [
{
"_id": "**630f5d4ebb4428127b11fb8e**"
},
{
"_id": "630f5d4ebb4428127b11fb8f"
}
]
}
]
}
You shouldn't use the $[] all positional operator which will remove the first item from the KeyResultActions array for all items in the KeyResults.
Instead, replace it with $ positional operator which selects the first matched element in the array.
MongoDB query
db.collection.update({
"_id": "6311d1612559020ef536cb6f",
"KeyResults._id": "6311d1612559020ef536cb6b",
"KeyResults.KeyResultActions._id": "630f5d4ebb4428127b11fb8e"
},
{
$pull: {
"KeyResults.$[].KeyResultActions": {
_id: "630f5d4ebb4428127b11fb8e"
}
}
})
MongoDB .NET Driver syntax
var update = Builders<Objective>.Update.PullFilter("KeyResults.$.KeyResultActions",
Builders<KeyResultAction>.Filter.Eq(x => x.Id, ObjectId.Parse(actionId)));
Demo

Querying CosmosDB Child Collection using ORDER BY

Say I have a document like below for each userId. And each userId has a child collection object named "lnk" and it can grow up to 100 items per userId.
I would like to sort the child collection for a given userId based on a single property
(ex: Topic or Pid or URL). for userId = "5663a8f7-6d2e-40ef-8972-515944080474"
SELECT * FROM c IN PageLinksContainer.lnk order by c.top asc OFFSET 1 LIMIT 3
Error I'm getting:
Failed to query item for container PageLinksContainer:
{
"errors": [
{
"severity": "Error",
"location": {
"start": 7,
"end": 25
},
"code": "SC2001",
"message": "Identifier 'PageLinksContainer' could not be resolved."
},
{
"severity": "Error",
"location": {
"start": 67,
"end": 85
},
"code": "SC2001",
"message": "Identifier 'PageLinksContainer' could not be resolved."
}
]
}
{
"userid": "5663a8f7-6d2e-40ef-8972-515944080474",
"lnk": [
{
"pid": 1,
"top": "Topic 1",
"por": "www.google.com",
"sdt": "10/26/2021"
},
{
"pid": 2,
"top": "Topic 2",
"por": "www.google.com",
"sdt": "10/26/2021"
},
{
"pid": 3,
"top": "Topic 3",
"por": "www.google.com",
"sdt": "10/26/2021"
}
]
}
C#
var query = "SELECT * FROM c IN PageLinksContainer.lnk OFFSET 1 LIMIT 10"; //Works fine with no order by.
var container = _cosmosClient.GetContainer(_databaseName, containerName);
using var iterator = container.GetItemQueryStreamIterator(new QueryDefinition(query),
requestOptions: new QueryRequestOptions
{
PartitionKey = new PartitionKey("5663a8f7-6d2e-40ef-8972-515944080474"),
MaxItemCount = 100
});
Links:
https://learn.microsoft.com/en-us/azure/cosmos-db/sql/sql-query-order-by
CosmosDB sql query with/without "ORDER BY" returns different number of items
You can't ORDER BY properties in arrays currently.
The following query better demonstrates that with the following error Order-by over correlated collections is not supported:
SELECT VALUE l
FROM c
JOIN l IN c.lnk
ORDER BY l.por
What you can do is create a User Defined Function that sorts the array before return. Although I would advise you to retrieve the results and use OrderBy in your C# code as it's easier, more transparant, and avoids spending unnecessary RU's on an UDF.

mongodb C#: query the value of untyped nested document

Here is an example of a BsonDocument in my database
{
"employee": "A",
"job history": [{
"job name": "sales",
"time": 2016
},
{
"job name": "manager",
"time": 2018,
"location": [
"NY",
"OH"
]
}
]
}
If I need a simple query like "name of the employee", I would go like this
var query = collection.Find(new BsonDocument("employee", "{}")).ToList();
Now, if I want to retrieve the value of "location", what should I do?
Here is an example to get you going:
// search by employee field
var result = collection.Find("{ 'employee': 'A' }").ToList();
Console.WriteLine(result.First()["job history"].AsBsonArray[1].AsBsonDocument["location"]); // prints [NY, OH]
// search by location
result = collection.Find("{ 'job history.location': 'NY' }").ToList();
Console.WriteLine(result.First()["employee"]); // prints A

How do I properly format this MongoDB update clause?

I have the following document:
{
"_id": {
"$oid": "55e1f841ff149c2228a5c33d"
},
"Status": "Open",
"Date": "8/30/2015",
"ContestName": "Test Contest",
"SearchableContestName": "test contest",
"ClassName": "Test",
"SearchableClassName": "test",
"Judges": [
{
"Name": "First Last",
"IsHeadJudge": null,
"_id": {
"$oid": "55e20962ff149c1f70d1aab0"
},
"ContestScores": null
},
{
"Name": "Another Name",
"IsHeadJudge": null,
"_id": {
"$oid": "55e20947ff149c1f70d1aaaf"
},
"ContestScores": [
1,
2,
3,
4,
5,
6
]
},
...
There are multiple judges in this list. I want to selectively update only the contest scores for the "First Last" name individual. I'm having difficulty figuring out the proper way to build my filters for this. I have:
public async void UpdateContestScores(ContestJudge judgeData, ObjectId contestId)
{
var contests = _db.GetCollection<BsonDocument>("contests");
var builder = Builders<BsonDocument>.Filter;
var updateFilter = builder.Eq("_id", contestId) & builder.Eq("Judges.Name", judgeData.Name);
var update = Builders<BsonDocument>.Update.Set("Judges.ContestScores", judgeData.ContestScores);
await contests.UpdateOneAsync(updateFilter, update);
}
This throws a bulk update error of some form. How do I go about updating this empty contest score field? I'm unsure of the proper filter syntax for such a thing.

mongodb c# how to work with BSON document

I've spent MANY hours looking for the answer...
This is very easy in PHP but I just can't put it together in C#(I'm new to C# and mongo...)
I'm trying to iterate through all levels of a stored document. The document looks like this:
{
"_id": ObjectId("51f90101853bd88971ecdf27"),
"fields": [
{
"ID": ObjectId("51fd09498b080ee40c00514e"),
"NAME": "ID",
"TYPE": "Text"
},
{
"ID": ObjectId("51fd09a68b080ee40c0064db"),
"NAME": "Title",
"TYPE": "Text"
},
{
"ID": ObjectId("51fd09b28b080ee40c004d31"),
"NAME": "Start Date",
"TYPE": "Date"
},
{
"ID": ObjectId("51fd09c28b080ee40c007f2e"),
"NAME": "Long Description",
"TYPE": "Memo"
}
],
"name": "TODB",
"updated": "Wed Jul 31 2013 08:20:17 GMT-0400 (Eastern Daylight Time)"
}
I have no problem accessing the "name" and "updated" but can't figure out how to access the "fields" array.
Code so far :
{
MongoServer mongo = MongoServer.Create();
mongo.Connect();
var db = mongo.GetDatabase("forms");
mongo.RequestStart(db);
var collection = db.GetCollection("forms");
var query = new QueryDocument("name",
"TODB");
mongo.Disconnect();
}
#foreach(BsonDocument item in collection.Find(query))
{
#item.GetElement("name").Value
#item.GetElement("_id").Value
}
Again, I am able to access the name and _id just not any of the sub document values.
Thanks in advance for any assistance!
After I get the reading figured out, I am also going to want to write data....
There are a few ways, but here's one:
// build some test data
BsonArray dataFields = new BsonArray { new BsonDocument {
{ "ID" , ObjectId.GenerateNewId()}, { "NAME", "ID"}, {"TYPE", "Text"} } };
BsonDocument nested = new BsonDocument {
{ "name", "John Doe" },
{ "fields", dataFields },
{ "address", new BsonDocument {
{ "street", "123 Main St." },
{ "city", "Madison" },
{ "state", "WI" },
{ "zip", 53711}
}
}
};
// grab the address from the document,
// subdocs as a BsonDocument
var address = nested["address"].AsBsonDocument;
Console.WriteLine(address["city"].AsString);
// or, jump straight to the value ...
Console.WriteLine(nested["address"]["city"].AsString);
// loop through the fields array
var allFields = nested["fields"].AsBsonArray ;
foreach (var fields in allFields)
{
// grab a few of the fields:
Console.WriteLine("Name: {0}, Type: {1}",
fields["NAME"].AsString, fields["TYPE"].AsString);
}
You can often use the string indexer ["name-of-property"] to walk through the fields and sub document fields. Then, using the AsXYZ properties to cast the field value to a particular type as shown above.

Categories

Resources