node.js - 为什么 mongodb 中的 addToSet 在数组字段中不起作用?
问题描述
集合中的文档有一个子文档的数组字段,每个字段都有一个计数器,应该增加到三个,但是如果数组没有具有给定键的子文档,它应该使用默认值创建它.
$addToSet的文档说:
行为
$addToSet 仅确保没有重复项添加到集合中,并且不会影响现有的重复元素。$addToSet 不保证修改集中元素的特定顺序。
缺少字段
如果您在要更新的文档中不存在的字段上使用 $addToSet,则 $addToSet 会创建以指定值作为其元素的数组字段。
问题是如果数组字段不存在,则不会在文档中创建它,如文档中所述。
这是我目前用来完成操作的:
// increase counter in element of array if element exist and counter is less than 3
collection.updateOne({
key_1,
"array.key_2": key_2,
"array.counter": {$lt: 3}
}, {
$inc: {"array.$.counter": 1}
})
.then(res => {
console.log("!!!!1:", res.modifiedCount, res.upsertedId, res.upsertedCount, res.matchedCount);
if (res.matchedCount) return res.matchedCount;
// create element in array with the default values,
// also create the array field if it doesn't exist
collection.updateOne({
key_1
}, {
$addToSet: {
array: {key_2, counter: 1}
}
})
.then(res => {
console.log("!!!!2:", res.modifiedCount, res.upsertedId, res.upsertedCount, res.matchedCount);
return res.matchedCount;
})
.catch(e => console.log(e))
})
.catch(e => console.log(e))
在第二个查询中使用upsert
,如果它不存在则创建数组字段,但是当array.counter
在后续调用中达到 3 以增加其值时,该操作会在数组中创建一个新的子文档,其值与array.key_2
and相同array.date
,有效复制该条目虽然array.counter
设置为 1 而不是 3。
我正在使用 mongo 版本 4.2.1
更新:
在尝试对第二个子文档运行操作之前,下面有一个示例文档:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 1
}, {
"key_2": 2,
"counter" 3
}
]
}
这是我在使用时得到的结果upsert
:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 1
}, {
"key_2": 2,
"counter" 3
}, {
"key_2": 2,
"counter" 1
}
]
}
该操作正在复制数组中的第二个子文档,但如果upsert
未使用,则如果它不在父文档中,则不会创建数组字段,这与文档中所说的预期行为相反$addToSet
。
更新 2
这些是重现问题的步骤:
运行key_1
设置为 1 并upsert
禁用的操作。没有任何查询修改文档。array
未创建该字段。
{
"key_1": 1
}
再次启用upsert
并运行该操作。该array
字段是在第二个查询中创建的:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 1
}
]
}
再次运行该操作两次。第一个查询修改了文档两次:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 3
}
]
}
再次运行该操作。第一个查询不会修改文档。第二个查询创建一个副本:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 3
}, {
"key_2": 1,
"counter" 1
}
]
}
解决方案
请试试这个:
var key_2Value = 2;
var firstFilterQuery = {
key_1: 1,
array: {
$elemMatch: {
"key_2": key_2Value,
"date": 'someDate',
"conter": { $lte: 3 }
}
}
}
var secondFilterQuery = {
key_1: 1,
"array.key_2": {$ne: key_2Value}
}
var defaultDoc = {key_2 : key_2Value, "date": 'someDefaultDate',counter: 1}
询问 :
collection.bulkWrite([
{
updateOne:
{
"filter": firstFilterQuery,
"update": { $inc: { "array.$.conter": 1 } }
}
}, {
updateOne:
{
"filter": secondFilterQuery,
"update": { $push: { array: defaultDoc }
}
}
}
])
使用上面的查询,您可以在一个 DB 调用中实现您想要的(在任何给定情况下,只有一个“updateOne”应该更新 DB),输出应该类似于:
{
"acknowledged" : true,
"deletedCount" : 0.0,
"insertedCount" : 0.0,
"matchedCount" : 1.0,
"upsertedCount" : 0.0,
"insertedIds" : {},
"upsertedIds" : {}
}
推荐阅读
- javascript - 它是一种从js中的xmlhttp2请求中提取var的方法吗?
- google-cloud-platform - 使用后端存储桶和 Cloudflare 从 Google 负载均衡器获取原始 IP
- r - 使用每行的多个参数在每一行数据帧上调用自定义函数
- python - 更改 xpath
- php - 有没有其他方法可以解决这个问题?
- javascript - 如何更新 CharJS 折线图中 2 条线的 2 个数据集?
- gradle - 如何在 Gradle 中发布特定的工件子集
- swift - 使用 Firebase 安全规则,如何在不公开整个节点的情况下检查可用用户名?
- postgresql - Postgres全文搜索不适用于某些术语
- python - 如何使用 Facebook Prophet 选择初始、周期、范围和截止点?