Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/mongodb/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何从MongoDB中的ChangeStream过滤特定字段的更新_C#_Mongodb_Mongodb .net Driver - Fatal编程技术网

C# 如何从MongoDB中的ChangeStream过滤特定字段的更新

C# 如何从MongoDB中的ChangeStream过滤特定字段的更新,c#,mongodb,mongodb-.net-driver,C#,Mongodb,Mongodb .net Driver,我正在设置ChangeStream,以便在集合中的文档发生更改时通知我,以便我可以将该文档的“LastModified”元素向上插入到事件发生时。由于此更新将导致ChangeStream上发生新事件,因此我需要过滤掉这些更新以防止无限循环(更新LastModified元素,因为LastModified元素刚刚更新…) 指定确切字段时,我有以下代码: ChangeStreamOptions options = new ChangeStreamOptions(); options.ResumeAft

我正在设置ChangeStream,以便在集合中的文档发生更改时通知我,以便我可以将该文档的“LastModified”元素向上插入到事件发生时。由于此更新将导致ChangeStream上发生新事件,因此我需要过滤掉这些更新以防止无限循环(更新LastModified元素,因为LastModified元素刚刚更新…)

指定确切字段时,我有以下代码:

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
的模型中

在这种情况下,当不同的操作没有field
updateDescription.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;
}