Mongoose - 使用findoneandupdate和$ push添加或更新子文档(当upsert为true时)

时间:2017-11-07 01:12:54

标签: node.js mongodb mongoose

我需要创建一个具有特定条件的子文档数组。如果ip agentlanguage字段完全匹配,那么就不需要添加新的子文档,如果其中任何一个文档出现故障,那么我需要创建一个新子文档。< / p>

但是当我尝试使用$push时,它会返回

Exception: StrictModeError: Path "ip" is not in schema, strict mode is `true`, and upsert is `true`.

findOneAndUpdate更新整个文档以及我们在数据库中显示的字段集。还尝试将其设置为在Schema中显示,但它返回相同的错误。

userSchema

中的数组
metadata: [mongoose.modelSchemas.UserMetaData],

userMetaDataSchema

  var userMetaDataSchema = new mongoose.Schema({

    ip: { type: String, default: '' },
    agent: { type: String, default: '' },
    language: { type: String, default: '' },
    userCreated: {
      id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
      name: { type: String, default: '' },
    }
  });
  app.db.model('UserMetaData', userMetaDataSchema);
};

如果我尝试在没有findOneAndUpdate的情况下直接使用UserMetaData$push集合,则可以正常使用。

控制台的结果:

{
        "_id" : ObjectId("5a00d35f03fc3eb63cbb1c6f"),
        "agent" : "Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0",
        "ip" : "::ffff:192.168.33.10",
        "language" : "en-US,en;q=0.5",
        "userCreated" : {
                "name" : "user2",
                "id" : ObjectId("5a00d27a7fdf8e07f40d2123")
        },
        "__v" : 0
}

无效的查询

    var fieldsToSet = {
      $push: {
        metadata: {
          agent: userAgent, 
          ip: userIp,
          language: languages, 
          userCreated: {
            name: req.user.username,
            id: req.user._id,
          }
        }
      }
    };
    var options = { upsert: true };

    User.findOneAndUpdate(conditions, fieldsToSet, options, function(err, user) {....

创建此类数组的首选方法是什么?谢谢。

1 个答案:

答案 0 :(得分:2)

此处的错误是由于尝试将findOneAndUpdate$push{upsert: true}选项一起使用而导致的,因为如果要插入或更新子文档,则应该执行此操作这有2个不同的运算符$set用于更新,$push用于插入。所以这里的解决方案是使用不同的设置进行更新和插入:

    var conditions = {
      _id: req.user._id,
      'metadata.ip': userIp,
      'metadata.agent': userAgent, 
      'metadata.language': userLanguage, 
      'metadata.userCreated.name': req.user.username
    };
    var fieldsToSet = {
      $set: {
        'metadata.$.agent': userAgent, 
        'metadata.$.ip': userIp,
        'metadata.$.language': userLanguage,
      }
    };
    var fieldsToPush = {
      $push: {
        metadata: {
          agent: userAgent,
          ip: userIp,
          language: userLanguage,
          userCreated: {
            id: req.user._id,
            name: req.user.username,
          }
        }
      }
    };
    var options = { upsert: true };
    // check if thare is a match and overwright
    User.findOneAndUpdate(conditions, fieldsToSet, options, function (err, setdocs) {

      //if there is no match then push a new subdocument
      if (setdocs == null) {
        User.findByIdAndUpdate(req.user._id, fieldsToPush, function (err, pushdocs) {
         //...
        });
      }
      else {
        //....
      }
    });
  });

这个解决方案归功于Neil Lunn我现在无法找到这个特定的解决方案