将Mongoose文档中的字段设置为数组长度

9

我是一名Mongoose文档 (Mongoose 5.4.13, mongoDB 4.0.12):

var SkillSchema = new mongoose.Schema({
    skill: { type: String },
    count: { type: Number, default: 0 },
    associatedUsers: [{ type : mongoose.Schema.Types.ObjectId, ref: 'User' }]
});

我将其翻译如下:

我更新的内容如下:

var query = { skill: req.body.skill };
var update = { $addToSet: { associatedUsers: req.params.id } };
            
var options = { upsert: true, new: true, setDefaultsOnInsert: true };

await skillSchema.findOneAndUpdate(query, update, options);

在此更新期间,我希望同时更新count的值,使其等于associatedUsers的长度。
理想情况下,我希望这与更新其他字段同时发生(即不是在随后的更新中),可以通过预钩子或在findOneAndUpdate中完成。
我尝试在模式定义后使用预钩子:
SkillSchema.pre('findOneAndUpdate', async function(){
    console.log("counting associated users");
    this.count = this.associatedUsers.length;
    next();
});

除了在我的UPDATE路由中使用聚合外:

await skillSchema.aggregate([{ $project: { count: { $size: "$associatedUsers" } } } ])

但我无法让它们两个都正常工作。

请问有什么建议可以帮助我实现这个功能


以下的答案有帮助到您吗? - O'Dane Brissett
4个回答

6

在4.2版本中,您可以使用$set来支持更新聚合管道。

第一个$set阶段基于先前和新值计算一个associatedUsers$setUnion用于保留不同的associatedUsers值。

第二个$set阶段根据上一阶段计算出的associatedUsers计算tally$size用于计算associatedUsers值的长度。

var query = {skill: req.body.skill};
var update = [{ $set: { "associatedUsers":{"$setUnion":[{"$ifNull":["$associatedUsers",[]]}, [req.params.id]] }}}, {$set:{tally:{ $size: "$associatedUsers" }}}];
var options = { upsert: true, new: true, setDefaultsOnInsert: true };
await skillSchema.findOneAndUpdate(query, update, options)

如果任何参数解析为null值或引用缺失的字段,则$setUnion返回null。因此,我们只需要使用$ifNull保护我们的操作。


在进行upsert时,若$size参数不是一个数组,则会出现错误提示。 - sushant mehta
@sushantmehta 谢谢。已更新答案以考虑集合操作中的空值。这是与upsert无关的集合运算符的行为。 - s7vr
这对我不起作用:var update = [{ $set: { "associatedUsers":{"$setUnion":[{"$ifNull":["$associatedUsers",[] ]}, [req.params.id]] }}}, { $set: { count: { $size: "$associatedUsers" }}}]; 注意:我已将模式字段 tally 更改为 count。将更新问题以反映此变更。 - fugu
Mongoose 5.4.13,mongoDB 4.0.12。我使用的是它们的免费版,看起来我只能使用4.0.12版本。之前没有收到错误提示,但更新后计数器并未改变。今天会再试一次。感谢您的帮助。 - fugu
1
对不起,表达式更新是4.2中的新功能,无法与旧版本兼容。您可能需要通过多个查询来完成。 - s7vr
显示剩余2条评论

2

About tally and associatedUsers.length

// define your schema object
var schemaObj = {
  skill: { type: String },
  associatedUsers: { type: Array }
};

// get the length of users
var lengthOfAsUsers = schemaObj.associatedUsers.length;

// add tally to schema object and set default to the length of users
schemaObj.tally = { type: Number, default: lengthOfAsUsers };

// and pass your schema object to mongoose.Schema
var SkillSchema = new mongoose.Schema(schemaObj);

module.exports = SkillSchema;

编辑:您可以随后更新Tally,但推荐的解决方案是使用此方法https://mongoosejs.com/docs/populate.html

最初的回答。

const id = "nameSomeId";

SkillSchema.find({ _id: id }).then(resp => {
  const tallyToUpdate = resp.associatedUsers.length;
  SkillSchema.findOneAndUpdate({ _id: id }, { tally: tallyToUpdate }).then(
    resp => {
      console.log(resp);
    }
  );
});

1
但是这个是否是动态的呢?也就是说,当我添加“associatedUsers”时,计数会增加吗? - fugu
好的,您随时可以随后更新总数。 - JozeV
当然可以,但这需要我发出两个请求,这不是很高效。 - fugu

1
我所提供的解决方案仅适用于MongoDB v 4.2,因为它具有使用聚合操作在更新中的选项,并且只需要一个查询:
skillSchemafindOneAndUpdate(


 {skill:"art"},
   [
     { $set: { 
       associatedUsers:{
         $cond:{
            if: {$gte: [{$indexOfArray: ["$associatedUsers", mongoose.Types.ObjectId(req.params.id)]}, 0]},
              then: "$associatedUsers",
              else: { $cond:{
                if: { $isArray: "$associatedUsers" },
                then: {$concatArrays:["$associatedUsers",[mongoose.Types.ObjectId(req.params.id)]]},
                else: [mongoose.Types.ObjectId(req.params.id)]
           }}
           }
      }}},
      {$set:{
        associatedUsers:"$associatedUsers",
        tally:{$size:"$associatedUsers"},
      }}
   ],
   {upsert:true,new:true}
)

参考: https://docs.mongodb.com/manual/reference/method/db.collection.update/#update-with-aggregation-pipeline


0

模式中没有出现“Group”字段。在MongoDB Shell上,这些代码可以工作。

然而,Mongoose也会报错,因为模式被验证了。

“Group”字段是动态字段吗?我认为这个模式的问题将会得到解决。

var mongoose = require("mongoose");

var SkillSchema = new mongoose.Schema({
    skill: { type: String },
    tally: { type: Number, default: 0 },
    associatedUsers: { type: Array },
    group: { type: Array }
 });

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