I'm having trouble deleting an document in an array using the offical mongodb c# driver. What i'm trying to do is also return the document that was deleted.
Here's what my class looks like:
public class Holder
{
public string Owner { get; set; }
public List<Card> Cards { get; set; }
}
public class Card
{
public string Id { get; set; }
public string Name { get; set; }
public string Placement { get; set; }
}
And here is an exerpt of the document:
{
"owner": "656dd33e0300"
"cards" : [
{
"id": "s39CNzu4Na3",
"name" : "Department",
"placement" : "8a"
},
...
]
}
My first thought was to just get the card and then delete it using the following:
var filter = Builders<Card>.Filter.Eq(x => x.Id, cardId);
var res = await collection.DeleteOneAsync(
Builders<Holder>.Filter.Eq(e => e.Owner, owner) &
Builders<Holder>.Filter.ElemMatch(e => e.Cards, filter)
);
try {
return res.DeletedCount > 0;
}
catch (Exception ex) {
throw;
}
But that just ended up deleting all the cards in the list.
I've been trying to use FindOneAndDeleteAsync
but i can't get it to work.
Update First a big thanks to both @markus and @yongshun as both their answers worked for me, i have picked @markus as the answer because it was the first answer that worked and i like the shorter code that is similar the rest of my code.
As far as I understand your question, you do not want to delete the root document, but remove a sub-document from an array. You can do this using an update-Statement (delete is for deleting root documents as a whole).
In order for this to work, you need to create a filter that finds the document and then run an update with a PullFilter
on the document.
In addition, you want to return the sub-document that you deleted during the update. There is no statement to return the sub-document, but you can use FindOneAndUpdateAsync
to return the root document in the state before the sub-document was removed:
var arrayFilter = Builders<Card>.Filter.Eq(x => x.Id, cardId);
// Find a document with a matching owner that contains at least one card with the specified id
var docFilter = Builders<Holder>.Filter.Eq(e => e.Owner, owner)
& Builders<Holder>.Filter.ElemMatch(e => e.Cards, arrayFilter);
// Remove all cards from the array that have a matching id
// (id should be unique so only one card should be removed)
var update = Builders<Holder>.Update.PullFilter(e => e.Cards, arrayFilter);
// Find the document and return it in the state before the update
var before = await coll.FindOneAndUpdateAsync(
docFilter,
update,
new FindOneAndUpdateOptions<Holder, Holder>() { ReturnDocument = ReturnDocument.Before });
// Get removed card from the returned document
var removedCard = before.Cards.SingleOrDefault(x => x.Id == cardId);