MongoDB中更新和替换的影响

28
我阅读了这个相关问题,但下面的问题不同。 MongoDB C#驱动程序在文档集合类中有一个 ReplaceOne 方法(和一个异步对应方法),可用于替换符合过滤器参数的整个文档内容。另一种选择是使用 UpdateOne UpdateMany 方法(或异步对应方法),这需要构建 UpdateDefinition<TDocument>
我的问题与选择其中一种方法而非另一种方法(替换 vs. 更新)有关,在这种情况下,您拥有足够的输入数据以选择任何一种方法来实现相同的结果。换句话说,如果我只想更新其内容的一小部分并且拥有完整的原始文档。
我能想到的第一个因素是发送到数据库服务器的负载。虽然我没有阅读过任何mongodb c#驱动程序源代码,也找不到任何文件来验证这一点,但 ReplaceOne 可能需要随着更新操作发送更多的字节,尤其是对于较大的文档。 Update ... 方法似乎可以通过仅为需要修改的文档片段发送更新元数据(除了过滤条件外,两种方法都必须发送)来避免较小的负载。 任何人都可以验证这是否是准确的假设吗?
同事提出的另一个因素是选择方法(更新 vs. 替换)也会影响文档索引。这里的假设是使用 ReplaceOne 有可能导致数据库重建要更新的文档的所有索引,而 Update ... 方法具有足够的更改元数据信息来避免在不是元数据的字段上重建索引。可以有人验证mongodb是否在内部根据使用替换还是更新来处理文档索引构建的方式?我们注意到了一个第三个因素,涉及到 Update<TDefinition> 类中的 AddToSetPullFilter 方法。似乎 Update... 方法不允许同时向一个文档中的集合(例如json数组)添加和删除项;这些操作必须分别使用 2 个单独的调用 Update... 方法以及单独的 Update<TDefinition> 实例(虽然具有相同的过滤参数)。在这种情况下,ReplaceOne 方法似乎是使用 C# 驱动程序实现单个“事务”进行此类文档更改的唯一方法。目前我们正在使用 Update... 而非 ReplaceOne,因为我们不确定替代方法是否会像上面提到的那样对索引产生负面影响。
除了这些因素之外,还有什么其他的因素可以使人选择使用 ReplaceOne 方法家族而不是 Update... 方法家族,或者反之?假设您拥有足够的输入数据(即所有文档数据)来使用任一方法实现相同的结果,不介意直接改变状态(通过替换)并且不介意构建Mongo定义(通过更新)。

简而言之,实际上并没有名为“ReplaceOne”的方法。这只是一个围绕基本的“更新”操作的“API包装器”,使用了一个“约束条件”,即您不能在块中指定“更新运算符”($set$addToSet$pull等)。这实际上不是一个“服务器方法”,而只是一个API实现。没有任何修饰符的常规“更新”默认情况下“替换”。替换意味着“替换”。因此,如果您真的不需要“替换”整个内容,则使用原子修饰符,仅更新指定的属性,而不更新其他任何内容。 - Neil Lunn
@NeilLunn 谢谢您,这很有帮助,我认为至少回答了关于索引的问题。但是,如果使用替换更新较大文档的次要更新,更新负载发送到数据库服务器的潜力是否更大?如果您需要在单个更新中从同一集合中addToSetpull,那么使用 ReplaceOne 是否是一个好选择,因为更新API无法实现这一点? - danludwig
2个回答

3

由于Mongo数据是非结构化的,ReplaceOne与Update相比的主要优势在于保证您的所有字段都将被删除并替换为新文档,从而使查询更简单。

1)没错,但这只是其中最不需要担心的问题,在两者中搜索条件都相同,但ReplaceOne需要在查询中传递整个文档,当您拥有完整的新文档并希望确保它以这种方式结束时,仍然值得使用ReplaceOne。

2)替换后重新索引 -> 是的,ReplaceOne会替换整个文档而不是更新现有文档,如果您更新了索引上的元素,则索引将需要调整以进行更改,但使用替换始终需要进行调整。 _id索引不会受到这两个操作的影响(我认为)。

3)如果您已经拥有所需的值,则无需替换整个文档即可更改单个字段,这将使所有字段重新索引,您应该使用$set操作,可以将其视为针对单个字段而不是整个文档的ReplaceOne。

https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/index.html https://docs.mongodb.com/manual/reference/operator/update/set/


2
如果被整合到 REST 服务中,replaceOne 方法将与 PUT 操作很好地对齐,其中您将在请求正文中发送整个对象以及对象标识符,并将其全部保存在数据库中。PUT 操作的独特特征是它是幂等的(可以重复运行)。replaceOne 方法采用 upsert 选项,默认值为 false,但如果将其设置为 true(并且搜索条件在唯一索引字段上),则无论文档是否单独删除,都可以使操作可重复执行。
更新操作更类似于 POST 操作,因为它只更新文档的一部分而不是完全替换。它也采用 upsert 选项,但如果文档被另一个进程删除,则生成的文档可能格式错误,导致更新失败。

7
同意替换类似于HTTP PUT,但我认为更新更类似于HTTP PATCH -- 但你是对的,如果无法打补丁,则应该使用POST。 - danludwig

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接