如何确定$addToSet是否真的向MongoDB文档中添加了一个新项目,或者该项目是否已经存在
本文关键字:是否 新项目 一个 或者 存在 项目 真的 addToSet 何确定 MongoDB 添加 | 更新日期: 2023-09-27 17:57:34
我正在使用C#驱动程序(NuGet的v1.8.3),很难确定$addtoSet/upsert
操作是否真的向给定数组中添加了一个NEW项,或者该项是否已经存在。
添加新项可能分为两种情况,要么文档根本不存在,只是由追加销售创建的,要么文档存在,但数组不存在或不包含给定项。
我需要这样做的原因是,我有大量数据要加载到MongoDB中,这些数据可能(不应该,但可能)在处理过程中中断。如果发生这种情况,我需要能够从头开始备份,而不需要进行重复的下游处理(保持处理幂等性)。在我的流程中,如果一个项目被确定为新添加的,我会排队处理该给定项目的下游处理,如果它被确定为已经添加到文档中,那么就不需要再做下游工作了。我的问题是,结果总是返回说调用修改了一个文档,即使该项已经存在于数组中,并且实际上没有修改任何内容。
根据我对C#驱动程序api的理解,我应该能够使用WriteConcern.Acknowledged
进行调用,然后检查WriteConcernResult.DocumentsAffected
,看看它是否确实更新了文档。
我的问题是,在所有情况下,写问题结果都会返回1个文档已更新。:/
以下是我的代码调用$addToSet
的示例文档,它可能在"项目"列表中有也可能没有这个特定项目:
{
"_id" : "some-id-that-we-know-wont-change",
"items" : [
{
"s" : 4,
"i" : "some-value-we-know-is-static",
}
]
}
我的查询总是使用基于处理元数据已知的_id
值:
var query = new QueryDocument
{
{"_id", "some-id-that-we-know-wont-change"}
};
我的更新如下:
var result = mongoCollection.Update(query, new UpdateDocument()
{
{
"$addToSet", new BsonDocument()
{
{ "items", new BsonDocument()
{
{ "s", 4 },
{ "i", "some-value-we-know-is-static" }
}
}
}
}
}, new MongoUpdateOptions() { Flags = UpdateFlags.Upsert, WriteConcern = WriteConcern.Acknowledged });
if(result.DocumentsAffected > 0 || result.UpdatedExisting)
{
//DO SOME POST PROCESSING WORK THAT SHOULD ONLY HAPPEN ONCE PER ITEM
}
如果我在一个空集合上运行此代码一次,则会添加文档,并且响应是预期的(DocumentsAffected = 1
、UpdatedExisting = false
)。如果我再次运行它(任意次数),文档似乎不会更新,因为它保持不变,但现在的结果是出乎意料的(DocumentsAffected = 1
,UpdatedExisting = true
)。
如果文档没有更改,这不应该是返回DocumentsAffected = 0
吗?
由于我们每天需要进行数百万次这样的调用,如果可能的话,我很犹豫是否将此逻辑转换为每个项目的多次调用(首先检查该项目是否存在于给定的文档数组中,然后添加/排队或跳过)。
有什么方法可以在一个电话中实现这一点吗?
当然,您在这里所做的实际上是检查响应,它确实指示文档是否被更新或插入,或者实际上是否两个操作都没有发生。这是您的最佳指标,因为$addToSet
如果执行了更新,则文档将被更新。
$addToSet
运算符本身不能产生重复项,这就是运算符的性质。但你的逻辑可能确实有一些问题:
{
"$addToSet", new BsonDocument()
{
{ "items", new BsonDocument()
{
{ "id", item.Id },
{ "v", item.Value }
}
}
}
}
很明显,您显示了"集合"中的一个项目由两个字段组成,因此,如果内容以任何方式变化(即相同的id但不同的值),则该项目实际上是集合中的"唯一"成员,并将被添加。例如,$addToSet
运算符不可能不纯粹基于作为唯一标识符的"id"添加新值。你必须在代码中实际实现这一点。
重复表单的第二种可能性是,您的查询部分没有正确找到必须更新的文档。这样做的结果是创建一个新文档,该文档只包含"集合"中新指定的成员。所以一个常见的用法错误是这样的:
db.collection.update(
{
"id": ABC,
"items": { "$elemMatch": {
"id": 123, "v": 10
}},
{
"$addToSet": {
"items": {
"id": 123, "v": 10
}
}
},
{ "upsert": true }
)
这种操作的结果总是会创建一个新文档,因为现有文档在"集合"中不包含指定的元素。正确的实现是而不是检查"集合"成员的存在,并允许$addToSet
执行工作。
如果在子文档的所有元素完全相同的"集合"中确实出现了true重复条目,那么这是由当前或过去的某些其他代码引起的。
如果您确信正在创建新条目,请在代码中查找$push
的实例,或者查看代码中似乎作用于同一字段的数组操作。
但是,如果您正确地使用了运算符,则$addToSet
将执行它想要执行的操作。