C# 如何从MongoDB中的ChangeStream过滤特定字段的更新
我正在设置ChangeStream,以便在集合中的文档发生更改时通知我,以便我可以将该文档的“LastModified”元素向上插入到事件发生时。由于此更新将导致ChangeStream上发生新事件,因此我需要过滤掉这些更新以防止无限循环(更新LastModified元素,因为LastModified元素刚刚更新…) 指定确切字段时,我有以下代码:C# 如何从MongoDB中的ChangeStream过滤特定字段的更新,c#,mongodb,mongodb-.net-driver,C#,Mongodb,Mongodb .net Driver,我正在设置ChangeStream,以便在集合中的文档发生更改时通知我,以便我可以将该文档的“LastModified”元素向上插入到事件发生时。由于此更新将导致ChangeStream上发生新事件,因此我需要过滤掉这些更新以防止无限循环(更新LastModified元素,因为LastModified元素刚刚更新…) 指定确切字段时,我有以下代码: ChangeStreamOptions options = new ChangeStreamOptions(); options.ResumeAft
ChangeStreamOptions options = new ChangeStreamOptions();
options.ResumeAfter = resumeToken;
string filter = "{ $and: [ { operationType: { $in: ['replace','insert','update'] } }, { 'updateDescription.updatedFields.LastModified': { $exists: false } } ] }";
var pipeline = new EmptyPipelineDefinition<ChangeStreamDocument<BsonDocument>>().Match(filter);
var cursor = collection.Watch(pipeline, options, cancelToken);
但这并没有像预期的那样起作用(我仍然得到了上次修改的更改的更新事件)
我最初使用的是过滤器生成器:
FilterDefinitionBuilder<ChangeStreamDocument<BsonDocument>> filterBuilder = Builders<ChangeStreamDocument<BsonDocument>>.Filter;
FilterDefinition<ChangeStreamDocument<BsonDocument>> filter = filterBuilder.In("operationType", new string[] { "replace", "insert", "update" }); //Only include the change if it was one of these types. Available types are: insert, update, replace, delete, invalidate
filter &= filterBuilder.Nin("updateDescription.updatedFields", ChangedFieldsToIgnore); //If this is an update, only include it if the field(s) updated contains 1+ fields not in the ChangedFieldsToIgnore list
此外,C#驱动程序似乎没有包含一个帮助$addFields和$objectToArray操作的生成器。我只能使用new BsonDocument{…}
方法来构建管道变量
ChangedFieldsToIgnore是一个列表,其中包含我不想获取事件的字段名
如果要基于多个键进行筛选(无论updatedFields
是否包含某些字段),则首先将键转换为值会更容易
您可以使用聚合运算符将updatedFields
中包含的文档转换为值。例如:
pipeline = [{"$addFields": {
"tmpfields":{
"$objectToArray":"$updateDescription.updatedFields"}
}},
{"$match":{"tmpfields.k":{
"$nin":["LastModified", "AnotherUnwantedField"]}}}
];
上面的聚合管道添加了一个名为tmpfields
的临时字段。这个新字段将围绕updateDescription的内容。updatedFields
将{name:value}
转换为[{k:name,v:value}]
。一旦我们将这些键作为值,我们就可以使用$nin
作为过滤器数组
已更新
您得到tmpfields
无效异常的原因是,结果被强制转换到没有可识别字段tmpfields
的模型中
在这种情况下,当不同的操作没有fieldupdateDescription.updatedFields
时,tmpfields
的值就是null
下面是MongoDB ChangeStream.Net/C使用的示例,以及修改输出更改流的聚合管道
此示例不是类型安全的,将返回BsonDocument
:
var database = client.GetDatabase("database");
var collection = database.GetCollection<BsonDocument>("collection");
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
// Aggregation Pipeline
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{"LastModified", "Unwanted"} }
} } } } };
var pipeline = new[] { addFields, match };
// ChangeStreams
var cursor = collection.Watch<BsonDocument>(pipeline, options);
foreach (var change in cursor.ToEnumerable())
{
Console.WriteLine(change.ToJson());
}
var-database=client.GetDatabase(“数据库”);
var collection=database.GetCollection(“collection”);
var options=new ChangeStreamOptions{FullDocument=ChangeStreamFullDocumentOption.UpdateLookup};
//聚合管道
var addFields=新的BsonDocument{
{“$addFields”,新的BsonDocument{
{“tmpfields”,新的B文件{
{“$objectToArray”,
“$updateDescription.updatedFields”}
} }
} } };
var match=新的BsonDocument{
{“$match”,新的B文件{
{“tmpfields.k”,新的B文件{
{“$nin”,新的BsonArray{“LastModified”,“不需要的”}
} } } } };
var pipeline=new[]{addFields,match};
//变更流
var cursor=collection.Watch(管道、选项);
foreach(cursor.ToEnumerable()中的变量更改)
{
WriteLine(change.ToJson());
}
我写了下面这段代码,因为我遇到了与您相同的问题。无需处理BSONObject
//The operationType can be one of the following: insert, update, replace, delete, invalidate
//ignore the field lastrun as we would end in an endles loop
var pipeline = new EmptyPipelineDefinition<ChangeStreamDocument<ATask>>()
.Match("{ operationType: { $in: [ 'replace', 'update' ] } }")
.Match(@"{ ""updateDescription.updatedFields.LastRun"" : { $exists: false } }")
.Match(@"{ ""updateDescription.updatedFields.IsRunning"" : { $exists: false } }");
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var changeStream = Collection.Watch(pipeline, options);
while (changeStream.MoveNext())
{
var next = changeStream.Current;
foreach (var obj in next)
yield return obj.FullDocument;
}
//操作类型可以是以下类型之一:插入、更新、替换、删除、无效
//忽略lastrun字段,因为我们将以endles循环结束
var pipeline=new EmptyPipelineDefinition()
.Match(“{operationType:{$in:['replace','update']}}”)
.Match(@“{”updateDescription.updatedFields.LastRun”“:{$exists:false}}”)
.Match(@“{”updateDescription.updatedFields.IsRunning“:{$exists:false}”);
var options=new ChangeStreamOptions{FullDocument=ChangeStreamFullDocumentOption.UpdateLookup};
var changeStream=Collection.Watch(管道、选项);
while(changeStream.MoveNext())
{
var next=changeStream.Current;
foreach(下一步中的var obj)
生成返回对象的完整文档;
}
是否不希望看到包含updateDescription.updatedFields.UnwantedField
的整个更新事件?因为updatedFields
可能包含多个字段,例如,它可能包含LastModified
和在同一操作中更新的另一个字段。我的应用程序是唯一一个应该更新LastModified字段,所以在我的情况下,这是可以的。但是,如果您有一个过滤器的建议,该过滤器将只排除包含要忽略的字段列表子集的更新,我很乐意看到它。我想知道是否有一种方法可以使用$redact聚合运算符来实现这一点。我想这已经接近我的目标了需要,但我遇到了一个关于tmpfields是无效字段名的新异常..我怀疑是当文档不是opType“update”,而是“insert”或“replace”时。我用您的答案编辑了OP。@JerrenSaunders,我用示例代码更新了我的答案。这不是因为不同的操作类型,而是因为模型转换。例如,ChangeStreamDocument
。谢谢。这让我离需要的地方足够近了。我仍然需要将此BsonDocument转换为ChangeSt我已经设法做到了这一点,但我相信有一种更好的方法,我现在把它放在一起……不过我会在一个新的问题中问这个问题。
pipeline = [{"$addFields": {
"tmpfields":{
"$objectToArray":"$updateDescription.updatedFields"}
}},
{"$match":{"tmpfields.k":{
"$nin":["LastModified", "AnotherUnwantedField"]}}}
];
var database = client.GetDatabase("database");
var collection = database.GetCollection<BsonDocument>("collection");
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
// Aggregation Pipeline
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{"LastModified", "Unwanted"} }
} } } } };
var pipeline = new[] { addFields, match };
// ChangeStreams
var cursor = collection.Watch<BsonDocument>(pipeline, options);
foreach (var change in cursor.ToEnumerable())
{
Console.WriteLine(change.ToJson());
}
//The operationType can be one of the following: insert, update, replace, delete, invalidate
//ignore the field lastrun as we would end in an endles loop
var pipeline = new EmptyPipelineDefinition<ChangeStreamDocument<ATask>>()
.Match("{ operationType: { $in: [ 'replace', 'update' ] } }")
.Match(@"{ ""updateDescription.updatedFields.LastRun"" : { $exists: false } }")
.Match(@"{ ""updateDescription.updatedFields.IsRunning"" : { $exists: false } }");
var options = new ChangeStreamOptions { FullDocument = ChangeStreamFullDocumentOption.UpdateLookup };
var changeStream = Collection.Watch(pipeline, options);
while (changeStream.MoveNext())
{
var next = changeStream.Current;
foreach (var obj in next)
yield return obj.FullDocument;
}