Created
September 14, 2023 21:53
-
-
Save leandromoh/a77eb7433f2c7088486f2b751fa880a7 to your computer and use it in GitHub Desktop.
C# extensions for MongoDB BsonDocument
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class MongoExtensions | |
{ | |
public static readonly Regex Indexer = new Regex(@"\[\d+\]", RegexOptions.Compiled); | |
public static void FullMerge(this BsonDocument b1, BsonDocument b2) | |
{ | |
for (int i = 0; i < b2.ElementCount; i++) | |
{ | |
var e2 = b2.ElementAt(i); | |
if (b1.TryGetElement(e2.Name, out var e1)) | |
{ | |
if (e2.Value.IsBsonDocument && e1.Value.IsBsonDocument) | |
{ | |
FullMerge(e1.Value.AsBsonDocument, e2.Value.AsBsonDocument); | |
} | |
else if (e2.Value.IsBsonArray && e1.Value.IsBsonArray) | |
{ | |
var a1 = e1.Value.AsBsonArray; | |
var a2 = e2.Value.AsBsonArray; | |
for (int j = 0; j < a2.Count; j++) | |
{ | |
if (j < a1.Count) | |
{ | |
FullMerge(a1[j].AsBsonDocument, a2[j].AsBsonDocument); | |
} | |
else | |
{ | |
a1[j] = a2[j]; | |
} | |
} | |
} | |
else | |
{ | |
b1[e2.Name] = e2.Value; | |
} | |
} | |
else | |
{ | |
b1[e2.Name] = e2.Value; | |
} | |
} | |
} | |
public static void RecursivelyRemove(this BsonDocument bson, string path) | |
{ | |
var cleaned = Indexer.Replace(path, "[]"); | |
var parts = cleaned.Split("."); | |
var last = parts[parts.Length - 1]; | |
var current = bson; | |
var j = 0; | |
foreach (var member in parts.SkipLast(1)) | |
{ | |
j++; | |
var isCollection = member.EndsWith("]"); | |
if (isCollection) | |
{ | |
var field = member.Split('[')[0]; | |
var array = current[field].AsBsonArray; | |
var subpath = string.Join(".", parts.Skip(j)); | |
for (var i = 0; i < array.Count; i++) | |
{ | |
RecursivelyRemove(array[i].AsBsonDocument, subpath); | |
} | |
return; | |
} | |
else | |
{ | |
current = current[member].AsBsonDocument; | |
} | |
} | |
current.Remove(last); | |
} | |
public static void Iterate(this BsonValue bson, Action<BsonValue, string> action) | |
{ | |
Iterate(bson, string.Empty, action); | |
} | |
private static void Iterate(BsonValue bson, string builder, Action<BsonValue, string> action) | |
{ | |
if (bson.IsBsonDocument) | |
{ | |
var document = bson.AsBsonDocument; | |
foreach (var el in document.Elements) | |
{ | |
if (el.Value.IsBsonDocument) | |
{ | |
Iterate(el.Value.AsBsonDocument, path(builder, el.Name), action); | |
} | |
else if (el.Value.IsBsonArray) | |
{ | |
var array = el.Value.AsBsonArray; | |
for (int i = 0; i < array.Count; i++) | |
{ | |
Iterate(array[i], path(builder, $"{el.Name}[{i}]"), action); | |
} | |
} | |
else | |
{ | |
Iterate(el.Value, path(builder, el.Name), action); | |
} | |
} | |
} | |
else | |
{ | |
action(bson, builder); | |
} | |
static string path(string source, string add) | |
{ | |
return source.Length == 0 | |
? add | |
: $"{source}.{add}"; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MongoDB.Bson; | |
using MongoDB.Bson.Serialization; | |
public class MongoExtensionsTests | |
{ | |
[Fact] | |
public void Iterate_should_reach_all_leaf_nodes() | |
{ | |
// Arrange | |
var xpto = Fixture.Create<XPTO>(); | |
var expected = | |
JObject | |
.FromObject(xpto) | |
.Descendants() | |
.OfType<JValue>() | |
.Select(jv => jv.Path) | |
.ToHashSet(); | |
// dont know why but JObject consider this get only property | |
expected.Remove(nameof(XPTO.IsFinished)); | |
// Act | |
var values = new HashSet<string>(); | |
order | |
.ToBsonDocument() | |
.Iterate((_, path) => values.Add(path.Replace("_id", "Id"))); | |
// Assert | |
// BeEquivalentTo method dont tell us what is missing/leftover | |
// so we check it manually | |
values.Except(expected).Should().BeEmpty(); | |
expected.Except(values).Should().BeEmpty(); | |
} | |
[Fact] | |
public void Simple_prop() | |
{ | |
// Arrange | |
var bson = new | |
{ | |
A = 1, | |
B = 2, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
var expected = new | |
{ | |
A = 1, | |
//B = 2, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
// Act | |
bson.RecursivelyRemove("B"); | |
// Assert | |
bson.Should().BeEquivalentTo(expected); | |
} | |
[Fact] | |
public void Simple_Nested_leaf_prop() | |
{ | |
// Arrange | |
var bson = new | |
{ | |
A = 1, | |
B = new | |
{ | |
D = new | |
{ | |
E = 5 | |
}, | |
F = 6, | |
}, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
var expected = new | |
{ | |
A = 1, | |
B = new | |
{ | |
D = new | |
{ | |
//E = 5 | |
}, | |
F = 6, | |
}, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
// Act | |
bson.RecursivelyRemove("B.D.E"); | |
// Assert | |
bson.Should().BeEquivalentTo(expected); | |
} | |
[Fact] | |
public void Simple_Nested_non_leaf_prop() | |
{ | |
// Arrange | |
var bson = new | |
{ | |
A = 1, | |
B = new | |
{ | |
D = new | |
{ | |
E = 5 | |
}, | |
F = 6, | |
}, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
var expected = new | |
{ | |
A = 1, | |
B = new | |
{ | |
//D = new | |
//{ | |
// E = 5 | |
//}, | |
F = 6, | |
}, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
// Act | |
bson.RecursivelyRemove("B.D"); | |
// Assert | |
bson.Should().BeEquivalentTo(expected); | |
} | |
[Fact] | |
public void Simple_Nested_array_leaf_prop() | |
{ | |
// Arrange | |
var bson = new | |
{ | |
A = 1, | |
B = new | |
{ | |
D = new[] | |
{ | |
new { E = 5, F = 6, G = 7 }, | |
new { E = 5, F = 6, G = 7 }, | |
}, | |
}, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
var expected = new | |
{ | |
A = 1, | |
B = new | |
{ | |
D = new[] | |
{ | |
new { E = 5, /*F = 6,*/ G = 7 }, | |
new { E = 5, /*F = 6,*/ G = 7 }, | |
}, | |
}, | |
C = 3, | |
} | |
.ToBsonDocument(); | |
// Act | |
bson.RecursivelyRemove("B.D[].F"); | |
// Assert | |
bson.Should().BeEquivalentTo(expected); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment