使用 $addToSet 时 MongoDB 数组的最大大小

Max size of MongoDB array when using $addToSet

使用:

 db.collection('users').update(
     { "_id": user._id},
     { "$addToSet": { "keywords.RFD": keywords } },
     function(err, result) {
         if (err) {
             console.log(
               'failed to add keyword "%s" for user: %s', keywords, user.email);
                    throw err;
          }
          else {
              console.log('Keyword added: ' + keywords );
              res.end('{"success" : "Updated Successfully.", "status" : 200}');
          }
   }
);

我想将关键字的数量限制为 50 个,如果关键字超过 50 个则抛出错误。如果数据库中已经有 50 个,我可以用 MongoDB 查询吗?

告诉你什么。您没有正确阅读文档。所以 keywords 在这里是一个“用词不当”,因为它在这种情况下只能是单数字符串或其他对象。当然不是数组。

但是是的 MongoDB有办法处理这个问题。就像我给出的指针一样,您需要阅读 $each documentation, along with other modifiers:

db.collection('users').update(
  { "_id": user._id},
  { 
    "$addToSet": { 
      "keywords.RFD": {
         "$each": [keywords],
      }
  }
)

然后您实际上可以调用一个单独的更新语句 $slice with a blank call to $each。有趣的是,它工作得很好。

db.collection('users').update(
  { "_id": user._id},
  { 
    "$push": { 
      "keywords.RFD": {
         "push": {"$each": [] },
      },
      "$slice": -50
  }
)

这里主要的键修饰符是$slice,这是一种“限制”数组大小的形式。

请注意,您需要 MongoDB 2.6 或更高版本作为服务器版本,这些运算符才能在更新时有效。如果您需要这种交互,这是升级的一个令人信服的理由。

P.S.: 正如我之前所说,您示例中“关键字”的上下文是一个“用词不当”,因为它被认为是一个“标量”值。在 $each 修饰符下,这确实可以是一个数组。因此:

db.collection('users').update(
  { "_id": user._id},
  { 
    "$addToSet": { 
      "keywords.RFD": {
         "$each": { "$each": keywords },
      }
  }
)
db.collection('users').update(
  { "_id": user._id},
  { 
    "$push": { 
      "keywords.RFD": {
         "push": [],
      },
      "$slice": -50
  }
)

不是原子的。但尽可能接近,尤其是在使用 Bulk API.

我认为也许有一种方法可以使用 MongoDB 像封顶数组一样对数组本身添加严格限制,但我认为这是不可能的。我最终只是使用逻辑。

if (user.settings.max_keywords <= result.keywords.RFD.length){
        res.end('{"error" : "Too many keywords.", "status" : 401}');
      }
const MAX_KEYWORDS = 50;
const maxArrayPath = `keywords.RFD.${MAX_KEYWORDS - 1}`;

db.collection('users').updateOne(
  { _id: user._id,
    [maxArrayPath]: { $exists: false }
  },
  { 
    $addToSet: { 
      "keywords.RFD": keywords,
    }
  }
);