Due to the filter I'm applying to my changestream (discussed at SO: How do you filter updates to specific fields from ChangeStream in MongoDB), I am getting a BsonDocument back instead of a ChangeStreamDocument object. The only thing different about this BsonDocument from a ChangeStreamDocument is that it contains an extra element called "tmpfields".
In my scenario, I still need the ResumeToken and other elements in the document, so I'd like to convert this BsonDocument to a ChangeStreamDocument object. My first attempt was to use BsonSerializer.Deserialize<ChangeStreamDocument<BsonDocument>>( doc)
where doc was the BsonDocument I got back. However, since it had the extra tmpfields element, this isn't allowed.
I attempted to register a BsonClassMap since the ChangeStreamDocument class is part of the C# driver and I couldn't add the [BsonIgnoreExtraElements] attribute to the class, but I wasn't successful:
BsonClassMap.RegisterClassMap<ChangeStreamDocument<BsonDocument>>(cm =>
{
cm.AutoMap();
cm.SetIgnoreExtraElements(true);
});
The AutoMap()
didn't work though and I got an exception about "no matching creator found". I tried to cm.MapCreator(...)
, but wasn't succesffuly there either. I took the AutoMap()
call out (only leaving the SetIgnoreExtraElements line) and got errors about it not being able to match the properties (_id, etc). So I tried lines like cm.MapProperty(c => c.DocumentKey).SetElementName("documentKey")
for each of the properties, but they were never set when I used the Deserialize()
method - they were left as null.
For now, I've reverted to using doc["field"].AsXYZ
method to get the values that I need from the BsonDocument, but I'd like to learn a better way to do this.
Is using the RegisterClassMap the correct approach? If so, what did I miss?
I couldn't add the [BsonIgnoreExtraElements] attribute to the class
If you just want to ignore the extra field. You could just add an extra aggregation pipeline $project
to remove the field.
For example
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var addFields = new BsonDocument { { "$addFields", new BsonDocument { { "tmpfields", new BsonDocument { { "$objectToArray", "$updateDescription.updatedFields" } } } } } };
var match = new BsonDocument { { "$match", new BsonDocument { { "tmpfields.k", new BsonDocument { { "$nin", new BsonArray{"a", "b"} } } } } } };
// Remove the unwanted field.
var project = new BsonDocument { {"$project", new BsonDocument { {"tmpfields", 0 } } } };
var pipeline = new[] { addFields, match, project };
var cursor = collection.Watch<ChangeStreamDocument<BsonDocument>>(pipeline, options);
var enumerator = cursor.ToEnumerable().GetEnumerator();
while(enumerator.MoveNext())
{
ChangeStreamDocument<BsonDocument> doc = enumerator.Current;
Console.WriteLine(doc.DocumentKey);
}