Mongoose:无法使用$ addToSet或$ push将新对象添加/推送到数组

时间:2015-08-14 05:12:14

标签: node.js mongodb mongoose

我使用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客户端。这不是一个可行的解决方案。

2 个答案:

答案 0 :(得分:2)

我正在打电话" bug"就此而言。 Mongoose显然做了错误的事情,如后面所示的日志记录中可以证明的那样。但是这里有一个列表,它使用您尝试执行的相同更新从本机驱动程序调用{​​{1}}:

.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();
  }
);

你需要小心,因为本机驱动程序方法不像mongoose方法那样知道连接状态。所以你需要确保一个" mongoose"之前触发的方法,或者将您的应用程序包装在类似的连接事件中:

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
}

关于" bug",请查看此列表中的日志记录输出:

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

带有mongoose日志记录的记录输出:

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 } 的电话?这不是问的问题。此外,当然数据库中没有任何改变,因为进行了错误的调用。所以即使.findOne()这里也是多余的。

这种情况发生在"混合"模式类型。

我个人不会在#34; Objects"像这样,只需制作你的对象键"标准数组的一部分作为附加属性。 MongoDB和mongoose对此都非常满意,查询具有这种结构的信息要容易得多。

{ "new": true }

输出:

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();
  }
);

这样工作正常,你不需要挖掘原生方法只是为了让它工作。

数组的属性使查询和过滤非常容易,以及"聚合"数据中的信息,对于所有MongoDB都喜欢的"严格的路径"参考所有信息。否则,你只会&"特定的键",如果不提及每个可能的"组合键,就不能编入索引或真正搜索#34;。

这样的属性是更好的方法。这里没有错误。

答案 1 :(得分:1)

Mongoose失去了自动检测并保存对混合类型所做更改的功能,因此您需要通过调用 .markModified(path) 方法“告诉”混合类型的值已更改将路径传递给您刚刚更改的混合类型的文档:

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 
});