EDIT:
Not looking for the javascript way of doing this. I am looking for the MongoDB C# 2.0 driver way of doing this (I know it might not be possible; but I hope somebody knows a solution).
I am trying to update the value of an item embedded in an array on the primary document in my mongodb.
I am looking for a strongly typed way to do this. I am using the Mongodb c# 2.0 driver
I can do it by popping the element, updating the value, then reinserting. This just doesn't feel right; since I am overwriting what might have been written in the meantime.
Here is what I have tried so far but with no luck:
private readonly IMongoCollection<TempAgenda> _collection;
void Main()
{
var collectionName = "Agenda";
var client = new MongoClient("mongodb://localhost:27017");
var db = client.GetDatabase("Test");
_collection = db.GetCollection<TempAgenda>(collectionName);
UpdateItemTitle(1, 1, "hello");
}
public void UpdateItemTitle(string agendaId, string itemId, string title){
var filter = Builders<TempAgenda>.Filter.Eq(x => x.AgendaId, agendaId);
var update = Builders<TempAgenda>.Update.Set(x => x.Items.Single(p => p.Id.Equals(itemId)).Title, title);
var result = _collection.UpdateOneAsync(filter, update).Result;
}
Took me a while to figure this out as it doesn't appear to be mentioned in any of the official documentation (or anywhere else). I did however find this on their issue tracker, which explains how to use the positional operator $
with the C# 2.0 driver.
This should do what you want:
public void UpdateItemTitle(string agendaId, string itemId, string title){
var filter = Builders<TempAgenda>.Filter.Where(x => x.AgendaId == agendaId && x.Items.Any(i => i.Id == itemId));
var update = Builders<TempAgenda>.Update.Set(x => x.Items[-1].Title, title);
var result = _collection.UpdateOneAsync(filter, update).Result;
}
Notice that your Item.Single()
clause has been changed to Item.Any()
and moved to the filter definition.
[-1]
or .ElementAt(-1)
is apparently treated specially (actually everything < 0) and will be replaced with the positional operator $
.
The above will be translated to this query:
db.Agenda.update({ AgendaId: 1, Items.Id: 1 }, { $set: { Items.$.Title: "hello" } })
Thanks, this was helpful. I have an addition though, I've used the above for arrays, pushing to a nested array and pulling from one. The issue I have found is that if I had an int array (So not an object, just a simple int array) that the PullFilter didn't actually work - "Unable to determine the serialization information" which is strange as it's only an array of ints. What I ended up doing was making it an array of objects with only one int parameter, and it all started to work. Possibly a bug, or perhaps my lack of understanding. Anyway, as I've struggled to find information about pulling and pushing to nested object arrays with the C# 2.0 driver, I thought I should post my findings here, as they use the above syntax.
var filter = Builders<MessageDto>.Filter.Where(x => x._id == entity.ParentID && x.NestedArray.Any(i => i._id == entity._id));
var update = Builders<MessageDto>.Update.PullFilter(x => x.NestedArray.ElementAt(-1).User, Builders<User>.Filter.Eq(f => f.UserID, userID));
Collection<MessageDto>(currentUser).UpdateOneAsync(filter, update);
And also:
var filter = Builders<MessageDto>.Filter.Where(x => x._id == entity.ParentID && x.NestedArray.Any(i => i._id == entity._id));
var update = Builders<MessageDto>.Update.Push(x => x.NestedArray.ElementAt(-1).Users, new User { UserID = userID });
Collection<MessageDto>(currentUser).UpdateOneAsync(filter, update);
The correct way to update a Document or sub array is as follows:
var filter = Builders<Declaracion>.Filter.Where(x => x.Id == di && x.RemuneracionMensualActual.RemuneracionActIndustrial.Any(s => s.Id == oid));
var update = Builders<Declaracion>.Update.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).Ingreso, datos.ActividadIndustrial.Ingreso)
.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).RazonSocial, datos.ActividadIndustrial.RazonSocial)
.Set(x => x.RemuneracionMensualActual.RemuneracionActIndustrial.ElementAt(-1).TipoNegocio, datos.ActividadIndustrial.TipoNegocio);