【问题标题】:Mongoose: Not able to add/push a new object to an array with $addToSet or $pushMongoose:无法使用 $addToSet 或 $push 将新对象添加/推送到数组
【发布时间】:2015-11-07 06:36:03
【问题描述】:

我使用 Nodejs、Hapijs 和 Mongoose。

我有一个架构和模型如下。

var schema = {
    name: {
        type: String,
        required: true
    },
    lectures: {}
};

var mongooseSchema = new mongoose.Schema(schema, {
    collection: "Users"
});
mongoose.model("Users", mongooseSchema);

出于某种原因,我需要保持“讲座” 作为混合型。

在保存/创建文档时,我创建了一个嵌套属性 lectures.physics.topic[],其中 topic 是一个数组。

现在,我正在尝试使用 $addToSet 或 $push 将新对象添加/推送到“lectures.physics.topic”。

userModel.findByIdAndUpdateAsync(user._id, {
    $addToSet: {
        "lectures.physics.topic": {
            "name": "Fluid Mechanics",
            "day": "Monday",
            "faculty": "Nancy Wagner"
        }
    }
});

但是文档根本没有得到更新。我也尝试使用 $push 。没有任何效果。可能是什么问题?

我尝试了另一种使用 mongoclient 的方法,直接更新数据库。它有效,请找到以下有效的代码

db.collection("Users").update({
   "_id": user._id
 }, {
 $addToSet: {
        "lectures.physics.topic": {
            "name": "Fluid Mechanics",
            "day": "Monday",
            "faculty": "Nancy Wagner"
        }
    }
 }, function(err, result) {
   if (err) {
       console.log("Superman!");
       console.log(err);
       return;
   }
 console.log(result);     
 });

每次请求命中都要启动mongo客户端。这不是一个可行的解决方案。

【问题讨论】:

  • findByIdAndUpdateAsync 是您的自定义方法吗?你能展示它的代码吗?

标签: node.js mongodb mongoose


【解决方案1】:

我会在这方面称其为“错误”。 Mongoose 显然做错了事,这可以在稍后显示的日志记录中得到证明。但这里有一个清单,它从本机驱动程序调用 .findOneAndUpdate(),并具有您尝试执行的相同更新:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/school');
mongoose.set('debug',true);

var userSchema = new Schema({
  name: {
    type: String,
    required: true
  },
  lectures: { type: Schema.Types.Mixed }
});

var User = mongoose.model( "User", userSchema );

function logger(data) {
  return JSON.stringify(data, undefined, 2);
}

async.waterfall(
  [
    function(callback) {
      User.remove({},function(err) {
        callback(err);
      });
    },
    function(callback) {
      console.log("here");
      var user = new User({ "name": "bob" });
      user.save(function(err,user) {
        callback(err,user);
      });
    },
    function(user,callback) {
      console.log("Saved: %s", logger(user));
      User.collection.findOneAndUpdate(
        { "_id": user._id },
        {
          "$addToSet": {
            "lectures.physics.topic": {
              "name": "Fluid Mechanics",
              "day": "Monday",
              "faculty": "Nancy Wagner"
            }
          }
        },
        { "returnOriginal": false },
        function(err,user) {
          callback(err,user);
        }
      );
    }
  ],
  function(err,user) {
    if (err) throw err;
    console.log("Modified: %s", logger(user));
    mongoose.disconnect();
  }
);

这与结果完美配合:

Saved: {
  "__v": 0,
  "name": "bob",
  "_id": "55cda1f5b5ee8b870e2f53bd"
}
Modified: {
  "lastErrorObject": {
    "updatedExisting": true,
    "n": 1
  },
  "value": {
    "_id": "55cda1f5b5ee8b870e2f53bd",
    "name": "bob",
    "__v": 0,
    "lectures": {
      "physics": {
        "topic": [
          {
            "name": "Fluid Mechanics",
            "day": "Monday",
            "faculty": "Nancy Wagner"
          }
        ]
      }
    }
  },
  "ok": 1
}

您需要在这里小心,因为本机驱动程序方法不像猫鼬方法那样知道连接状态。因此,您需要确保已通过较早触发的“mongoose”方法建立连接,或者将您的应用程序包装在连接事件中,如下所示:

mongoose.connection.on("connect",function(err) {
   // start app in here
});

至于“错误”,请查看此列表中的日志记录输出:

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/school');
mongoose.set('debug',true);

var userSchema = new Schema({
  name: {
    type: String,
    required: true
  },
  lectures: { type: Schema.Types.Mixed }
});

var User = mongoose.model( "User", userSchema );

function logger(data) {
  return JSON.stringify(data, undefined, 2);
}

async.waterfall(
  [
    function(callback) {
      User.remove({},function(err) {
        callback(err);
      });
    },
    function(callback) {
      console.log("here");
      var user = new User({ "name": "bob" });
      user.save(function(err,user) {
        callback(err,user);
      });
    },
    function(user,callback) {
      console.log("Saved: %s", logger(user));
      User.findByIdAndUpdate(
        user._id,
        {
          "$addToSet": {
            "lectures.physics.topic": {
              "name": "Fluid Mechanics",
              "day": "Monday",
              "faculty": "Nancy Wagner"
            }
          }
        },
        { "new": true },
        function(err,user) {
          callback(err,user);
        }
      );
    }
  ],
  function(err,user) {
    if (err) throw err;
    console.log("Modified: %s", logger(user));
    mongoose.disconnect();
  }
);

以及使用猫鼬日志记录的记录输出:

Mongoose: users.remove({}) {}
here
Mongoose: users.insert({ name: 'bob', _id: ObjectId("55cda2d2462283c90ea3f1ad"), __v: 0 })
Saved: {
  "__v": 0,
  "name": "bob",
  "_id": "55cda2d2462283c90ea3f1ad"
}
Mongoose: users.findOne({ _id: ObjectId("55cda2d2462283c90ea3f1ad") }) { new: true, fields: undefined }
Modified: {
  "_id": "55cda2d2462283c90ea3f1ad",
  "name": "bob",
  "__v": 0
}

那么在真正的“What the Fudge?” 风格中,有一个电话到.findOne()?这不是被问到的。此外,数据库中当然没有任何改变,因为调用错误。所以即使是这里的{ "new": true } 也是多余的。

这发生在所有级别的“混合”架构类型中。

我个人不会像这样嵌套在“对象”中,而只是将您的“对象键”作为附加属性作为标准数组的一部分。 MongoDB 和 mongoose 都乐此不疲,这样的结构查询信息也容易很多。

var async = require('async'),
    mongoose = require('mongoose'),
    Schema = mongoose.Schema;

mongoose.connect('mongodb://localhost/school');
mongoose.set('debug',true);

var lectureSchema = new Schema({
  "subject": String,
  "topic": String,
  "day": String,
  "faculty": String
});

var userSchema = new Schema({
  name: {
    type: String,
    required: true
  },
  lectures: [lectureSchema]
});

var User = mongoose.model( "User", userSchema );

function logger(data) {
  return JSON.stringify(data, undefined, 2);
}

async.waterfall(
  [
    function(callback) {
      User.remove({},function(err) {
        callback(err);
      });
    },
    function(callback) {
      console.log("here");
      var user = new User({ "name": "bob" });
      user.save(function(err,user) {
        callback(err,user);
      });
    },
    function(user,callback) {
      console.log("Saved: %s", logger(user));
      User.findByIdAndUpdate(
        user._id,
        {
          "$addToSet": {
            "lectures": {
              "subject": "physics",
              "topic": "Fluid Mechanics",
              "day": "Monday",
              "faculty": "Nancy Wagner"
            }
          }
        },
        { "new": true },
        function(err,user) {
          callback(err,user);
        }
      );
    }
  ],
  function(err,user) {
    if (err) throw err;
    console.log("Modified: %s", logger(user));
    mongoose.disconnect();
  }
);

输出:

Mongoose: users.remove({}) {}
here
Mongoose: users.insert({ name: 'bob', _id: ObjectId("55cda4dc40f2a8fb0e5cdf8b"), lectures: [], __v: 0 })
Saved: {
  "__v": 0,
  "name": "bob",
  "_id": "55cda4dc40f2a8fb0e5cdf8b",
  "lectures": []
}
Mongoose: users.findAndModify({ _id: ObjectId("55cda4dc40f2a8fb0e5cdf8b") }) [] { '$addToSet': { lectures: { faculty: 'Nancy Wagner', day: 'Monday', topic: 'Fluid Mechanics', subject: 'physics', _id: ObjectId("55cda4dc40f2a8fb0e5cdf8c") } } } { new: true, upsert: false, remove: false }
Modified: {
  "_id": "55cda4dc40f2a8fb0e5cdf8b",
  "name": "bob",
  "__v": 0,
  "lectures": [
    {
      "faculty": "Nancy Wagner",
      "day": "Monday",
      "topic": "Fluid Mechanics",
      "subject": "physics",
      "_id": "55cda4dc40f2a8fb0e5cdf8c"
    }
  ]
}

这样就可以正常工作了,您无需为了使其正常工作而深入研究本机方法。

数组的属性使得查询和过滤以及跨数据的“聚合”信息变得非常容易,对于所有这些 MongoDB 来说,它都喜欢使用“严格路径”来引用所有信息。否则,您只会区分“特定键”,如果不提及所有可能的“键组合”,就无法索引或真正搜索这些键。

这样的属性是更好的选择。这里没有错误。

【讨论】:

  • 感谢哥们的冗长回答,我会试一试。如果它解决了我的问题,会告诉你的。!!!干杯人..
  • @AmruthRao 这就是为什么这里的列表是自包含的并显示结果的原因。这意味着您也可以复制、粘贴和运行,并亲自查看这些示例是否按照我所说的那样做。还请您特别考虑最后一点,因为这是一种比您目前正在做的更好的结构方式。嵌套对象键和“分离”数据会导致问题。这比诉诸极易出现问题的“检索->修改->保存”过程要好得多。
【解决方案2】:

Mongoose 无法自动检测和保存对 Mixed 类型所做的更改,因此您需要通过调用将路径传递给刚刚更改的 Mixed 类型的文档:

doc.mixed.type = 'changed';
doc.markModified('mixed.type');
doc.save() // changes to mixed.type are now persisted 

在您的情况下,您可以使用 findById() 方法进行更改,方法是在主题数组上调用 addToSet() 方法,然后触发 save() 持久化更改的方法:

userModel.findById(user._id, function (err, doc){
    var item = {
        "name": "Fluid Mechanics",
        "day": "Monday",
        "faculty": "Nancy Wagner"
    };
    doc.lectures.physics.topic.addToSet(item);
    doc.markModified('lectures');
    doc.save() // changes to lectures are now persisted 
});

【讨论】:

    猜你喜欢
    • 2018-07-20
    • 2015-03-01
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    • 2020-02-05
    • 1970-01-01
    • 2015-03-08
    相关资源
    最近更新 更多