Can you help me to run correctly "Pull (remove)" with 2.0 driver.
I have a collection like this and I want to remove first follower named as fethiye by follower field.
{
"_id": ObjectId("554e05dfc90d3d4dfcaa2aea"),
"username": "bodrum",
"followerList": [
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "fethiye",
"avatar": "fethiye.png"
},
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "izmir",
"avatar": "izmir.png"
}
]
}
How can I fix this query?
var filter = new BsonDocument("username", "bodrum");
var update = Builders<Person>.Update.Pull("followerList:follower", "fethiye");
Person pr = collection.FindOneAndUpdateAsync(filter, update).Result;
Thanks.
When using a filter to remove array elements, you need to use the PullFilter builder instead of Pull (which matches whole elements).
var collection = db.GetCollection<Person>("people");
var filter = new BsonDocument("username", "bodrum");
var update = Builders<Person>.Update.PullFilter("followerList",
Builders<Follower>.Filter.Eq("follower", "fethiye"));
var result = collection.FindOneAndUpdateAsync(filter, update).Result;
Or somewhat more succinctly, using lambdas:
var update = Builders<Person>.Update.PullFilter(p => p.followerList,
f => f.follower == "fethiye");
var result = collection
.FindOneAndUpdateAsync(p => p.username == "bodrum", update).Result;
Assuming you have a collection name Person,
You can use PullFilter to remove the records from array
var filterBuilder = Builders<Person>.Filter.Eq(person => person.username, "bodrum");
var updateBuilder = Builders<Person>.Update.PullFilter(p => p.followerList,
Builders<Person>.Filter.Eq(per => per.follower, "fethiye"));
var updateResult = collection.UpdateOne(filterBuilder, updateBuilder);
Console.WriteLine(
$"MatchedCount: {updateResult.MatchedCount}, ModifiedCount: {updateResult.ModifiedCount}");
If we also need to remove array of values inside a filtered document we can replace the update builder with this line
var updateBuilder = Builders<Person>.Update.PullFilter(p => p.followerList,
Builders<Person>.Filter.In(per => per.follower, new List<string> {"fethiye", "izmir"}));
Also to save many document, updateOne can be replace with updateMany
var updateResult = collection.UpdateMany(filterBuilder, updateBuilder);
This is what i use to delete a nested array object
-parentpath: followerList
-propertie: follower
-value: fethiye.png
var filter = new BsonDocument("_id", ObjectId.Parse(id));
var updateValues = Builders<object>.Update.PullFilter(parentPath,
Builders<object>.Filter.Eq(propertie, value));
DatabaseCollection.FindOneAndUpdate(filter, updateValues);
Example to delete a deeper nested array object:
Let's delete the object with the name Doe
-parentPath: followerList.0.testArray
-propertie:name
-value:Doe
{
"_id": ObjectId("554e05dfc90d3d4dfcaa2aea"),
"username": "bodrum",
"followerList": [
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "fethiye",
"testArray": [
{
"name":"John"
},
{
"name":"Doe"
},
{
"name":"Jason"
}
]
},
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "izmir",
"avatar": "izmir.png"
}
]
}
may i offer a much simpler solution using MongoDB.Entities. the person and followers are stored in their own collections and represents a one-to-many relationship.
using System.Linq;
using MongoDB.Entities;
namespace StackOverflow
{
public class Person : Entity
{
public string Username { get; set; }
public Many<Follower> FollowerList { get; set; }
public Person() => this.InitOneToMany(() => FollowerList);
}
public class Follower : Entity
{
public string Name { get; set; }
public string Avatar { get; set; }
}
class Program
{
static void Main(string[] args)
{
new DB("followers");
var person1 = new Person { Username = "bodrum"};
person1.Save();
var follower1 = new Follower { Name = "fethiye", Avatar= "fethiye.png" };
follower1.Save();
var follower2 = new Follower { Name = "izmir", Avatar = "izmir.png" };
follower2.Save();
person1.FollowerList.Add(follower1);
person1.FollowerList.Add(follower2);
var fathiye = person1.FollowerList.Collection()
.Where(p => p.Name == "fethiye")
.FirstOrDefault();
person1.FollowerList.Remove(fathiye);
}
}
}
Related
I've found plenty of examples showing how to remove an item from an array within the root of a collection of documents and I can successfully add items to nested arrays. But I can't seem to make headway on removing or updating an item in a nested array.
An example would be where I have a collection of groups. In that group, there is an array(list) of Members. Each of those members could have multiple nicknames that they go by. I need to know how to go about deleting/updating a Nickname for a specific member.
Ex:
I wanted to update Member: Robert James' Nickname "Not Rick James" to "Rick"
Delete the Nickname "Smithy" from Member John Smith
// example mongo data
[{
"_id": 482232389408781300,
"Name": "Group A",
"Members": [
{
"_id": "X4cLx72J9",
"Name": "John Smith",
"NickNames: [
{
"_id": "V2889Jw8",
"Name": "Smithy"
},
{
"_id": "V82lvi2",
"Name": "Ol John"
}
]
},
{
"_id": "X4c7872J9",
"Name": "Robert James",
"NickNames: [
{
"_id": "V2Bl9Jw8",
"Name": "Not Rick James"
},
{
"_id": "V8Qrvi2",
"Name": "Slick"
}
]
}
}
]
// c# classes
class Group{
public ulong Id {get;set;} // Unique Id
public string Name {get;set;}
public List<Member> Members{get;set;}
}
class Member{
public string Id {get;set;} // Unique Id
public string Name {get;set;}
public List<NickName> NickNames{get;set;}
}
public NickName{
public string Id {get;set;} // Unique Id
public string Name {get;set;}
}
Editing for clarification
I'm needing to know how to complete this request using the MongoDB driver for C#
var guilds = db.GetCollection<Group>("groups");
var filter = Builders<Group>.Filter;
/// At this point I'm unsure how the filter/update values should be coded in order to update the mongo db.
Assuming this is your data,
List<NickName> nickNameList = new List<NickName>();
nickNameList.Add(new NickName() { Id = "V2889Jw8", Name = "Smithy" });
nickNameList.Add(new NickName() { Id = "V82lvi2", Name = "Ol John" });
List<NickName> nickNameList2 = new List<NickName>();
nickNameList2.Add(new NickName() { Id = "V2Bl9Jw8", Name = "Not Rick James" });
nickNameList2.Add(new NickName() { Id = "V8Qrvi2", Name = "Slick" });
List<Member> members = new List<Member>();
members.Add(new Member() { Id = "X4cLx72J9", Name = "John Smith", NickNames = nickNameList });
members.Add(new Member() { Id = "X4c7872J9", Name = "Robert James", NickNames = nickNameList2 });
List<Group> group = new List<Group>();
group.Add(new Group(){
Id = 482232389408781300,
Name = "Group A",
Members = members
});
We can use the .Find() method of List to find the specific record in the list and update or delete it.
For example:
//Update
group.Find(x => x.Id == 482232389408781300).Members.Find(x => x.Id == "X4c7872J9").NickNames.Find(n => n.Name == "Not Rick James").Name = "Rick";
//Delete
List<NickName> nickNames = group.Find(x => x.Id == 482232389408781300).Members.Find(x => x.Id == "X4cLx72J9").NickNames;
nickNames.Remove(nickNames.Find(n => n.Name == "Smithy"));
I think I've worked out two methods that work for me. They might not be the most efficient so I will continue reading more into. But here is what I have thus far.
The following deletion method uses the positional all '$[]' option as each Nickname has a unique ID.
// Delete An array item
var groupId = 482232389408781300;
var memberId = "X4cLx72J9";
var nickId = "V2889Jw8";
var collection = _db.GetCollection<Group>("groups");
var filter = Builders<Group>.Filter.Eq(group => group.Id, groupId);
var update = Builders<Group>.Update.PullFilter("Members.$[].NickNames", Builders<BsonDocument>.Filter.Eq("_id", nickId));
collection.UpdateOne(filter, update);
The update method is using the ArrayFilters option. I imagine you could use this for the deletion method as well.
// Update An array item
var groupId = 482232389408781300;
var memberId = "X4cLx72J9";
var nickId = "V2889Jw8";
var newNick = "Loon";
var collection = _db.GetCollection<Group>("groups");
var filter = Builders<Group>.Filter.Eq(group => group.Id, groupId);
var update = Builders<Group>.Update.Set("Members.$[m].NickNames.$[n].Name", newNick);
var filterArray = new List<ArrayFilterDefinition>{
new JsonArrayFilterDefinition<Group>(string.Format("{{'m._id': '{0}'}}", memberId)),
new JsonArrayFilterDefinition<Group>(string.Format("{{'n._id': '{0}'}}", nickId))
};
var updateOptions = new UpdateOptions { ArrayFilters = filterArray };
collection.UpdateOne(filter, update, updateOptions);
My Json Response is Following below:
{"d":
{"RowData":
[{"GenreId":11,"GenreName":"Musical","subjecturl":"subjecturl_1","logourl":"logourl_1"},
{"GenreId":12,"GenreName":"kids","subjecturl":"subjecturl_2","logourl":"logourl_2"},
{"GenreId":13,"GenreName":"other","subjecturl":"subjecturl_3","logourl":"logourl_3"},
{"GenreId":14,"GenreName":"Musical","subjecturl":"subjecturl_4","logourl":"logourl_4"},
{"GenreId":15,"GenreName":"Music","subjecturl":"subjecturl_5","logourl":"logourl_5"},
{"GenreId":16,"GenreName":"Faimaly","subjecturl":"subjecturl_6","logourl":"logourl_6"},
{"GenreId":17,"GenreName":"other","subjecturl":"subjecturl_7","logourl":"logourl_7"},
{"GenreId":18,"GenreName":"other","subjecturl":"subjecturl_8","logourl":"logourl_8"},
{"GenreId":19,"GenreName":"kids","subjecturl":"subjecturl_9","logourl":"logourl_9"},
{"GenreId":20,"GenreName":"Musical","subjecturl":"subjecturl_10","logourl":"logourl_10"},
{"GenreId":21,"GenreName":"other","subjecturl":"subjecturl_11","logourl":"logourl_11"}]}}
Using the above Response I tried to make like below Response :
{"rows": [{
"title": "Musical",
"items": [{"hdsubjecturl": "subjecturl_1"},{"hdsubjecturl": "subjecturl_4"},{"hdsubjecturl": "subjecturl_10"}]
},{
"title": "kids",
"items": [{"hdsubjecturl": "subjecturl_2"},{"hdsubjecturl": "subjecturl_9"}]
},{
"title": "Music",
"items": [{"hdsubjecturl": "subjecturl_5"}]
},{
"title": "other",
"items": [{"hdsubjecturl": "subjecturl_3"},{"hdsubjecturl": "subjecturl_7"},{"hdsubjecturl": "subjecturl_8"},{"hdsubjecturl": "subjecturl_11"}]
},{
"title": "Faimaly",
"items": [{"hdsubjecturl": "subjecturl_6"}]
}]
}
My Code is below :
JObject Root = JObject.Parse(jsonData["d"].ToString());
var unique = Root["RowData"].GroupBy(x => x["GenreName"]).Select(x => x.First()).ToList(); // here fetch 5 record
foreach (var un in unique)
{
var GenreName = new
{
title = un["GenreName"],
items = new
{
hdsubjecturl = "logourl"
}
};
var GenreNamereq = JsonConvert.SerializeObject(GenreName, Newtonsoft.Json.Formatting.Indented);
genstr.Append(GenreNamereq, 0, GenreNamereq.Length);
genstr.Append(",");
using (System.IO.StreamWriter file = new System.IO.StreamWriter(subdir + "\\GenreName.json"))
{
string st = genstr.ToString().Substring(0, (genstr.Length - 1));
file.WriteLine("{\n\"rows\": [\n" + st + "\n}"); //seasion number 21 terminate
file.Close();
}
}
Using above code my output is below for First Field :
{"rows":
[{
"title": "Musical",
"items": {
"hdsubjecturl": "logourl"
}
}]
}
Using below code I tried to fetch Multiple values using specific Field :
List<string> CategoryList = new List<string>();
var unique = Root["RowData"].GroupBy(x => x["GenreName"]).Select(x => x.First()).ToList(); // here fetch 8 record
foreach (var cat in unique)
{
CategoryList.Add(cat["GenreName"].ToString());
}
List<List<string>> myList = new List<List<string>>();
for (int i=0;i<CategoryList.Count();i++)
{
var results = from x in Root["RowData"]
where x["GenreName"].Value<string>() == CategoryList[i]
select x;
foreach (var token in results)
{
Console.WriteLine(token["logourl"]);
}
// myList.Add(results);
}
In the First code, I used JObject for fetching a Root node. But, using the above query it takes by default JTocken. So, I used foreach loop here.
I used Dictionary instances for JSON Creation. Using this code I successfully fetched hdsubjecturl in for loop. But, I don't know how to put multiple values in Dictionary instances. Because I get title fields only single times using a unique query and items fields inside a hdsubjetcurl is more than one. Does anyone know how it's possible?
You can group your RowData by GenreName token, then use ToDictionary method to get a result dictionary and map it to desired structure (with title and hdsubjecturl). Finally create a result object using JObject.FromObject
var json = JObject.Parse(jsonString);
var data = json["d"]?["RowData"]
.GroupBy(x => x["GenreName"], x => x["subjecturl"])
.ToDictionary(g => g.Key, g => g.ToList())
.Select(kvp => new { title = kvp.Key, items = kvp.Value.Select(x => new { hdsubjecturl = x }) });
var result = JObject.FromObject(new { rows = data });
Console.WriteLine(result);
It gives you the expected result.
Edit: according to comments, GroupBy and Select expressions should be updated to map more then one property in result title item
var data = json["d"]?["RowData"]
.GroupBy(x => x["GenreName"])
.ToDictionary(g => g.Key, g => g.ToList())
.Select(kvp => new
{
title = kvp.Key,
items = kvp.Value.Select(x => new { hdsubjecturl = x["subjecturl"], url = x["logourl"] })
});
var result = JObject.FromObject(new { rows = data });
Consider trying this code, (using Newtonsoft Json deserializer);
public partial class Root
{
public D D { get; set; }
}
public partial class D
{
public RowDatum[] RowData { get; set; }
}
public partial class RowDatum
{
public long GenreId { get; set; }
public string GenreName { get; set; }
public string Subjecturl { get; set; }
public string Logourl { get; set; }
}
public partial class Response
{
public Row[] Rows { get; set; }
}
public partial class Row
{
public string Title { get; set; }
public Item[] Items { get; set; }
}
public partial class Item
{
public string Hdsubjecturl { get; set; }
}
public class Program
{
public static void Main(string[] args)
{
var json =
#"{""d"":{""RowData"":[{""GenreId"":11,""GenreName"":""Musical"",""subjecturl"":""subjecturl_1"",""logourl"":""logourl_1""},{""GenreId"":12,""GenreName"":""kids"",""subjecturl"":""subjecturl_2"",""logourl"":""logourl_2""},{""GenreId"":13,""GenreName"":""other"",""subjecturl"":""subjecturl_3"",""logourl"":""logourl_3""},{""GenreId"":14,""GenreName"":""Musical"",""subjecturl"":""subjecturl_4"",""logourl"":""logourl_4""},{""GenreId"":15,""GenreName"":""Music"",""subjecturl"":""subjecturl_5"",""logourl"":""logourl_5""},{""GenreId"":16,""GenreName"":""Faimaly"",""subjecturl"":""subjecturl_6"",""logourl"":""logourl_6""},{""GenreId"":17,""GenreName"":""other"",""subjecturl"":""subjecturl_7"",""logourl"":""logourl_7""},{""GenreId"":18,""GenreName"":""other"",""subjecturl"":""subjecturl_8"",""logourl"":""logourl_8""},{""GenreId"":19,""GenreName"":""kids"",""subjecturl"":""subjecturl_9"",""logourl"":""logourl_9""},{""GenreId"":20,""GenreName"":""Musical"",""subjecturl"":""subjecturl_10"",""logourl"":""logourl_10""},{""GenreId"":21,""GenreName"":""other"",""subjecturl"":""subjecturl_11"",""logourl"":""logourl_11""}]}}";
var root = JsonConvert.DeserializeObject<Root>(json);
var rows = root.D.RowData.ToLookup(d => d.GenreName)
.Select(g => new Row()
{
Title = g.Key,
Items = g.ToList().Select(rd => new Item() {Hdsubjecturl = rd.Logourl}).ToArray()
}).ToArray();
var response = new Response()
{
Rows = rows
}; // reponse is the type of Json Response you wanted to achieve
Console.WriteLine();
}
}
I have a list of parent elements that needs to be filtered based on the structure inside a child list.
Here is the data structure
"items": [
{
"id": "6691e62b-90as-43b2-k1l3-2fbf039295b5",
"details": {
"zone": {
"id": "cc07de83-3m21-1pp1-a123-a98bd8fb5fb8",
"name": "TestName",
}
},
}
]
var findresult = collection(f => f.items.Any(fb => fb.details.zone.name == "TestName")).ToList();
Expected that only items with the matching zone name is returned but I just returns everything in the db
to get parent documents that contain matching sub document, you need to use $elemMatch operator:
db.Parent.aggregate([
{
"$match": {
"items": {
"$elemMatch": {
"details.zone.name": "second zone"
}
}
}
}
])
if you want to get only the matching sub documents (items), you need $unwind and $project like so:
db.Parent.aggregate([
{
"$unwind": "$items"
},
{
"$project": {
"items": "$items",
"_id": NumberInt("0")
}
},
{
"$match": {
"items.details.zone.name": "second zone"
}
}
])
here's the c# code that generated above queries. code uses MongoDB.Entities for brevity. the querying part is the same for the official driver. just use collection.AsQueryable() in place of DB.Queryable<Parent>()
using MongoDB.Driver.Linq;
using MongoDB.Entities;
using System;
using System.Linq;
namespace StackOverflow
{
public class Program
{
public class Parent : Entity
{
public Item[] items { get; set; }
}
public class Item
{
public string id { get; set; }
public Detail details { get; set; }
}
public class Detail
{
public string id { get; set; }
public Zone zone { get; set; }
}
public class Zone
{
public string id { get; set; }
public string name { get; set; }
}
private static void Main(string[] args)
{
new DB("test");
(new[] {
new Parent {
items = new[]
{
new Item
{
id = Guid.NewGuid().ToString(),
details = new Detail
{
id = Guid.NewGuid().ToString(),
zone = new Zone
{
id = Guid.NewGuid().ToString(),
name = "first zone"
}
}
}
}
},
new Parent {
items = new[]
{
new Item
{
id = Guid.NewGuid().ToString(),
details = new Detail
{
id = Guid.NewGuid().ToString(),
zone = new Zone
{
id = Guid.NewGuid().ToString(),
name = "second zone"
}
}
}
}
}
}).Save();
//get all Parent entities that have 'second zone'
var result = DB.Queryable<Parent>()
.Where(p => p.items.Any(i => i.details.zone.name == "second zone"))
.ToArray();
//get only items that have 'second zone'
var items = DB.Queryable<Parent>()
.SelectMany(p => p.items)
.Where(i => i.details.zone.name == "second zone")
.ToArray();
}
}
}
update after comments:
in order to get parent and filter out non-matching child items, you need a projection stage with $elemMatch like so:
db.Parent.find({
"items": {
"$elemMatch": {
"details.zone.name": "second zone"
}
}
}, {
"_id": NumberInt("1"),
"items": {
"$elemMatch": {
"details.zone.name": "second zone"
}
}
})
c# MongoDB.Entities:
var result = DB.Find<Parent>()
.Match(p => p.items.Any(i => i.details.zone.name == "second zone"))
.Project(b =>
b.Include(p => p.ID)
.ElemMatch(p => p.items, i => i.details.zone.name == "second zone"))
.Execute();
c# official driver:
var projection = Builders<Parent>.Projection
.Include(p => p.ID)
.ElemMatch(p => p.items, i => i.details.zone.name == "second zone");
var result = collection.Find(p => p.items.Any(i => i.details.zone.name == "second zone"))
.Project(projection)
.ToList();
I am using c#,along with MongoDB.
I have a class that can be resembled by this.
Its a sample, that represents something, please dont comment on the class design
[CollectionName("Venues")]
public class Venue
{
public string Name { get; set; }
public dictionary<string,object> Properties { get; set; }
}
var venue = new Venue
{
Name = "Venue 1",
Properties = new Dictionary<string,object>
{
{ "Chairs", "18" },
{ "Tables", "4" },
{ "HasWaterfall", true }
}
}
Assuming I have an object in a collection, that looks like that.
I would like to find out of it is possible to do two things.
1: Load from the database, only a single item from the dictionary,
currently I can only see how this can be done, by loading the entire
record from the database and then manually getting the value by key.
2: Determine the average of a single item within the database.
For example, across all records I would like to work out the average
chairs, again without loading all records and then doing it in memory with
linq etc....
Basically your sample document gets stored as a below JSON:
{
"_id" : ObjectId("..."),
"Name" : "Venue 1",
"Properties" : {
"Chairs" : "18",
"Tables" : "4",
"HasWaterfall" : true
}
}
This gives you a possibility to define a projection using dot notation:
var filter = Builders<Venue>.Filter.Eq(f => f.Name, "Venue 1");
var projection = Builders<Venue>.Projection.Include("Properties.Chairs");
List<BsonDocument> data = Col.Find(filter).Project(projection).ToList();
which returns below following BsonDocument:
{ "_id" : ObjectId("..."), "Properties" : { "Chairs" : "18" } }
To get the average you need to use $toInt operator introduced in MongoDB 4.0 to convert your values from string to int. Try:
var project = new BsonDocument()
{
{ "chairs", new BsonDocument() { { "$toInt", "$Properties.Chairs" } } }
};
var group = new BsonDocument()
{
{ "_id", "null" },
{ "avg", new BsonDocument() { { "$avg", "$chairs" } } }
};
var avg = Col.Aggregate().Project(project).Group(group).First();
here's an alternative way of doing it using MongoDB.Entities convenience library.
using System.Collections.Generic;
using System.Linq;
using MongoDB.Entities;
namespace StackOverflow
{
class Program
{
[Name("Venues")]
public class Venue : Entity
{
public string Name { get; set; }
public Dictionary<string, object> Properties { get; set; }
}
static void Main(string[] args)
{
new DB("test");
var venue1 = new Venue
{
Name = "Venue 1",
Properties = new Dictionary<string, object> {
{ "Chairs", 28 },
{ "Tables", 4 },
{ "HasWaterfall", true }
}
};
venue1.Save();
var venue2 = new Venue
{
Name = "Venue 2",
Properties = new Dictionary<string, object> {
{ "Chairs", 38 },
{ "Tables", 4 },
{ "HasWaterfall", true }
}
};
venue2.Save();
var chairs = DB.Find<Venue, object>()
.Match(v => v.Name == "Venue 1")
.Project(v => new { ChairCount = v.Properties["Chairs"] })
.Execute();
var avgChairs = DB.Collection<Venue>()
.Average(v => (int)v.Properties["Chairs"]);
}
}
}
results in the following queries being made to the database:
getting chairs in venue 1:
db.runCommand({
"find": "Venues",
"filter": {
"Name": "Venue 1"
},
"projection": {
"Properties.Chairs": NumberInt("1"),
"_id": NumberInt("0")
},
"$db": "test"
})
getting average chair count across all venues:
db.Venues.aggregate([
{
"$group": {
"_id": NumberInt("1"),
"__result": {
"$avg": "$Properties.Chairs"
}
}
}
])
Can you help me to run correctly "Pull (remove)" with 2.0 driver.
I have a collection like this and I want to remove first follower named as fethiye by follower field.
{
"_id": ObjectId("554e05dfc90d3d4dfcaa2aea"),
"username": "bodrum",
"followerList": [
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "fethiye",
"avatar": "fethiye.png"
},
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "izmir",
"avatar": "izmir.png"
}
]
}
How can I fix this query?
var filter = new BsonDocument("username", "bodrum");
var update = Builders<Person>.Update.Pull("followerList:follower", "fethiye");
Person pr = collection.FindOneAndUpdateAsync(filter, update).Result;
Thanks.
When using a filter to remove array elements, you need to use the PullFilter builder instead of Pull (which matches whole elements).
var collection = db.GetCollection<Person>("people");
var filter = new BsonDocument("username", "bodrum");
var update = Builders<Person>.Update.PullFilter("followerList",
Builders<Follower>.Filter.Eq("follower", "fethiye"));
var result = collection.FindOneAndUpdateAsync(filter, update).Result;
Or somewhat more succinctly, using lambdas:
var update = Builders<Person>.Update.PullFilter(p => p.followerList,
f => f.follower == "fethiye");
var result = collection
.FindOneAndUpdateAsync(p => p.username == "bodrum", update).Result;
Assuming you have a collection name Person,
You can use PullFilter to remove the records from array
var filterBuilder = Builders<Person>.Filter.Eq(person => person.username, "bodrum");
var updateBuilder = Builders<Person>.Update.PullFilter(p => p.followerList,
Builders<Person>.Filter.Eq(per => per.follower, "fethiye"));
var updateResult = collection.UpdateOne(filterBuilder, updateBuilder);
Console.WriteLine(
$"MatchedCount: {updateResult.MatchedCount}, ModifiedCount: {updateResult.ModifiedCount}");
If we also need to remove array of values inside a filtered document we can replace the update builder with this line
var updateBuilder = Builders<Person>.Update.PullFilter(p => p.followerList,
Builders<Person>.Filter.In(per => per.follower, new List<string> {"fethiye", "izmir"}));
Also to save many document, updateOne can be replace with updateMany
var updateResult = collection.UpdateMany(filterBuilder, updateBuilder);
This is what i use to delete a nested array object
-parentpath: followerList
-propertie: follower
-value: fethiye.png
var filter = new BsonDocument("_id", ObjectId.Parse(id));
var updateValues = Builders<object>.Update.PullFilter(parentPath,
Builders<object>.Filter.Eq(propertie, value));
DatabaseCollection.FindOneAndUpdate(filter, updateValues);
Example to delete a deeper nested array object:
Let's delete the object with the name Doe
-parentPath: followerList.0.testArray
-propertie:name
-value:Doe
{
"_id": ObjectId("554e05dfc90d3d4dfcaa2aea"),
"username": "bodrum",
"followerList": [
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "fethiye",
"testArray": [
{
"name":"John"
},
{
"name":"Doe"
},
{
"name":"Jason"
}
]
},
{
"_id": ObjectId("554e0625a51586362c33c6df"),
"follower": "izmir",
"avatar": "izmir.png"
}
]
}
may i offer a much simpler solution using MongoDB.Entities. the person and followers are stored in their own collections and represents a one-to-many relationship.
using System.Linq;
using MongoDB.Entities;
namespace StackOverflow
{
public class Person : Entity
{
public string Username { get; set; }
public Many<Follower> FollowerList { get; set; }
public Person() => this.InitOneToMany(() => FollowerList);
}
public class Follower : Entity
{
public string Name { get; set; }
public string Avatar { get; set; }
}
class Program
{
static void Main(string[] args)
{
new DB("followers");
var person1 = new Person { Username = "bodrum"};
person1.Save();
var follower1 = new Follower { Name = "fethiye", Avatar= "fethiye.png" };
follower1.Save();
var follower2 = new Follower { Name = "izmir", Avatar = "izmir.png" };
follower2.Save();
person1.FollowerList.Add(follower1);
person1.FollowerList.Add(follower2);
var fathiye = person1.FollowerList.Collection()
.Where(p => p.Name == "fethiye")
.FirstOrDefault();
person1.FollowerList.Remove(fathiye);
}
}
}