首页 文章

附加在Schemaless Array中

提问于
浏览
1

我是 MongoDB 的新手,到目前为止一直在玩它,面对一个问题,这里我很难在Schema-Less Array中追加多个对象 . 到目前为止我试过 $push 在数组中追加多个对象但是得到了a Mongo Error .

[MongoError: Can't use $push/$pushALL within non-array

当使用$ push with array时,我不知道为什么我会收到此错误

架构:

EventTypeSchema = new Schema(){
type: String,
eventID: {
type: Schema.Types.ObjectId,
ref: 'User'
}
} 

PersonSchema = new Schema(){
PersonID: {
type: Schema.Types.ObjectId,
ref: 'User'
}
Invitation: [  ]    //Schema-less
}

在Controller中,我可以访问EventType和Person Model Controller:

exports.update = function(req,res){
var event = new EventType();
event.type = 'EVENT';
event.eventID = req.body.eventid;
var query = {'PersonID': req.body.personid};
var update = {$push:{'Invitation': event}};
Person.update(query,update,function(err,user){...})
};

出于调试目的,我试图为数组提供 Mixed type Schema ,但没有让它工作

PersonSchema = new Schema(){
PersonID: {
type: Schema.Types.ObjectId,
ref: 'User'
}
Invitation: [ {
type: Schema.Types.Mixed
} ]    
}

当我在更新时删除 $push 然后只有整个事件对象进入 Invitation ,我创建无架构数组的原因是因为我正在处理不同类型的邀请,这里我刚刚描述了事件邀请,否则有不同类型的邀请我正在处理类似的, User Invitation for requestConference invitation, 所以会有 different objectId's 的组合,我认为应该有一种方法可以在mongoDB中追加无模式数组 .

EDIT:

以下是我提出的建议 . 但是不能让它工作 .

function PortalTypes() {
    Schema.apply(this,arguments);
    this.add({
        object_type: String,
    });
}

util.inherits( PortalTypes, Schema );

var userType = new PortalTypes({
    ID : {
        type: Schema.Types.ObjectId,
        ref : 'User'
    }
});

var eventType = new PortalTypes({
    ID : {
        type: Schema.Types.ObjectId,
        ref : 'events'
    }
});



var user = new userType({ID:'dsaj3232--objectID','object_type':'user'});
user.save();

var event = new eventType({ID:'dasddehiqe98--objectID','object_type':'event'});
event.save();

Networks.Invitation.push(user,event);

我怎么能这样做?

1 回答

  • 1

    尽管您的模式顶部的错误意味着集合中存在匹配的文档,但没有将此字段设置为数组,但它与另一种类型一起存在 . 可能只是一个字符串或对象 .

    这是一个小的,人为的示例列表,用于演示:

    var async = require('async'),
        mongoose = require('mongoose'),
        Schema = mongoose.Schema;
    
    var personSchema = new Schema({
      invitation: []
    });
    
    var Person = mongoose.model( 'Person', personSchema );
    
    mongoose.connect('mongodb://localhost/test');
    
    async.waterfall(
      [
        function(callback) {
          Person.remove({},function(err,num) {
            callback(err);
          });
        },
    
        function(callback) {
          console.log( "Creating" );
          var person = new Person();
          person.save(function(err,person) {
            if (err) callback(err);
            console.log(person);
            callback(err,person);
          });
        },
    
        function(person,callback) {
          console.log( "Updating" );
          Person.findOneAndUpdate(
            { "_id": person._id },
            { "$push": { "invitation": "something" } },
            function(err,doc) {
              if (err) callback(err);
              console.log(doc);
              callback(err);
            }
          );
        },
    
        function(callback) {
          console.log( "Upserting" );
          Person.findOneAndUpdate(
            { "name": "bob" },
            { "$set": { "invitation": {} } },
            { "upsert": true },
            function(err,doc) {
              if(err) callback(err);
              console.log(doc);
              callback(err,doc);
            }
          );
        },
    
        function(bob,callback) {
          console.log( "Failing" );
          Person.findOneAndUpdate(
            { "name": "bob" },
            { "$push": { "invitation": "else" } },
            function(err,doc) {
              if (err) callback(err);
              console.log(doc);
              callback(err);
            }
          );
        }
    
      ],
      function(err) {
        if (err) throw err;
        console.log( "Done" );
        mongoose.disconnect();
    
      }
    );
    

    这应该给出这样的结果:

    Creating
    { __v: 0, _id: 54a18afb345b4efc02f21020, invitation: [] }
    Updating
    { _id: 54a18afb345b4efc02f21020,
      __v: 0,
      invitation: [ 'something' ] }
    Upserting
    { _id: 54a18afb9997ca0c4a7eb722,
      name: 'bob',
      __v: 0,
      invitation: [ {} ] }
    Failing
    
    /home/neillunn/scratch/persons/node_modules/mongoose/lib/utils.js:413
        throw err;
              ^
    MongoError: exception: The field 'invitation' must be an array but is of type Object
    in document   {_id: ObjectId('54a18afb9997ca0c4a7eb722')}
    

    错误消息有点不同,因为它们在MongoDB 2.6及其以上(此错误字符串来自)的位置有所改进,以便更准确地解决实际问题 . 所以在现代版本中,你会被告知到底出了什么问题 .

    尽管存在模式,但像 .update() (为方便起见,我使用了 .findOneAndUpdate() )这样的方法稍微绕过了mongoose模式定义并直接进入数据库 . 因此,可以这样做,也可能只是已经有一个文档,或者在不同的模式定义到位时创建 .

    所以这是第一个问题 .


    您似乎要问的其余部分是数组中的“多态”关联类型,以及您不希望将整个创建的对象“嵌入”在数组中而只是对它的引用 .

    Mongoose有"discriminators"来允许这种事情,允许对象的不同模型类型存储在同一个集合中,但是解析为它们自己的对象和模式"type" .

    按照当前的文档示例,下面是一个示例列表:

    var util = require('util'),
        async = require('async'),
        mongoose = require('mongoose'),
        Schema = mongoose.Schema;
    
    function logger(label,content) {
      console.log(
        "%s:\n%s\n", label, JSON.stringify( content, undefined, 4 ) );
    }
    
    function BaseSchema() {
    
      Schema.apply(this,arguments);
    
      this.add({
        name: String,
        createdAt: { type: Date, default: Date.now }
      });
    
    }
    
    util.inherits( BaseSchema, Schema );
    
    
    var personSchema = new BaseSchema(),
        bossSchema = new BaseSchema({ department: String });
    
    var companySchema = new Schema({
      people: [{ type: Schema.Types.ObjectId, ref: 'Person' }]
    });
    
    
    var Person = mongoose.model( 'Person', personSchema ),
        Boss = Person.discriminator( 'Boss', bossSchema ),
        Company = mongoose.model( 'Company', companySchema );
    
    mongoose.connect('mongodb://localhost/test');
    
    async.waterfall(
      [
        function(callback) {
          Company.remove({},function(err,num) {
            callback(err);
          });
        },
        function(callback) {
          Person.remove({},function(err,num) {
            callback(err);
          });
        },
        function(callback) {
          var person = new Person({ name: "Bob" });
          person.save(function(err,person) {
            logger("Person", person);
            callback(err,person);
          });
        },
        function(person,callback) {
          var boss = new Boss({ name: "Ted", department: "Accounts" });
          boss.save(function(err,boss) {
            logger("Boss", boss);
            callback(err,person,boss);
          });
        },
        function(person,boss,callback) {
          var company = new Company();
          company.people.push(person,boss);
          company.save(function(err,company) {
            logger("Stored",company);
            callback(err,company);
          });
        },
        function(company,callback) {
          Company.findById(company.id)
            .populate('people')
            .exec(function(err,company) {
              logger("Polulated",company);
              callback(err);
            });
        }
      ],
      function(err) {
        if (err) throw err;
        mongoose.disconnect();
      }
    );
    

    哪个会产生这样的输出:

    Person:
    {
        "__v": 0,
        "name": "Bob",
        "createdAt": "2014-12-29T17:53:22.418Z",
        "_id": "54a1951210a7a1b603161119"
    }
    
    Boss:
    {
        "__v": 0,
        "name": "Ted",
        "department": "Accounts",
        "__t": "Boss",
        "createdAt": "2014-12-29T17:53:22.439Z",
        "_id": "54a1951210a7a1b60316111a"
    }
    
    Stored:
    {
        "__v": 0,
        "_id": "54a1951210a7a1b60316111b",
        "people": [
            "54a1951210a7a1b603161119",
            "54a1951210a7a1b60316111a"
        ]
    }
    
    Polulated:
    {
        "_id": "54a1951210a7a1b60316111b",
        "__v": 0,
        "people": [
            {
                "_id": "54a1951210a7a1b603161119",
                "name": "Bob",
                "__v": 0,
                "createdAt": "2014-12-29T17:53:22.418Z"
            },
            {
                "_id": "54a1951210a7a1b60316111a",
                "name": "Ted",
                "department": "Accounts",
                "__v": 0,
                "__t": "Boss",
                "createdAt": "2014-12-29T17:53:22.439Z"
            }
        ]
    }
    

    如您所见,对于如何保存 PersonBoss ,存在不同的结构,特别是 _t 属性以及不同对象的其他已定义属性 . 然而,两者实际上都存储在相同的"people"集合中,并且可以这样查询 .

    将它们存储在 Company 对象上时,只有"reference id"值存储在数组中 . 对你可能想要的东西有争议,但这是"referenced"和"embedded"架构模型之间的区别 . 但是,无论何时调用 .populate() 方法,都可以看到对象从引用的集合中读取时将恢复为完整形式 .


    因此,请检查您的集合中是否存在与模式定义不同的现有文档,并将所示方法视为表示对象的不同“类型”的“多态”关联 .

    请注意,这种分辨率仅在"referenced"架构设计下受支持,这也可能有它的缺点 . 如果您希望将对象存储为单个 Company 集合中的"embedded"(例如),则不会自动获取由mongoose完成的不同模式类型的对象解析类型 . 解析不同类型的对象必须在您的代码中手动完成,或者提供插件或者您这样做 .

    更多

    由于基于标准文档示例的某些内容似乎存在一些混淆,因此具体针对所有目的,这里是一个评论较多的列表:

    var util = require('util'),
        async = require('async'),
        mongoose = require('mongoose'),
        Schema = mongoose.Schema;
    
    // Utility
    function logger(label,content) {
      console.log(
        "%s:\n%s\n", label,
        util.inspect( content, false, 8, false ) );
    }
    
    /*
     * Schemas:
     *
     * you can use a base schema for common fields or just a plain
     * definition
     */
    
    var portalSchema = new Schema(),
    
        userSchema = new Schema({
          "name": String,
          "age": Number
        }),
    
        eventSchema = new Schema({
          "place": String,
          "eventDate": { type: Date, default: Date.now }
        });
    
    /*
     * Models
     *
     * there is only one "model" defined and therefore one collection only
     * as everything is comes from a stored __v field with the "Model" name
     * defined in the discriminator
     */
    
    var Portal = mongoose.model( 'Portal', portalSchema ),
        User = Portal.discriminator( 'User', userSchema ),
        Event = Portal.discriminator( 'Event', eventSchema );
    
    /*
     * Then there is the thing that is going to consume the references to the
     * 'Portal' model. The array here references the "base" model.
     */
    
    var otherSchema = new Schema({
      "afield": String,
      "portals": [{ type: Schema.Types.ObjectId, ref: "Portal" }]
    });
    
    var Other = mongoose.model( 'Other', otherSchema );
    
    /*
     * Meat:
     *
     * Let's start doing things
     */
    
    mongoose.connect('mongodb://localhost/test');
    
    // Just because we're passing around objects without globals or other scoping
    async.waterfall(
      [
        // Start fresh by removing all objects in the collections
        function(callback) {
          Other.remove({},function(err,num) {
            callback(err);
          });
        },
        function(callback) {
          Portal.remove({},function(err,num) {
            callback(err);
          });
        },
    
        // Create some portal things
        function(callback) {
          var eventObj = new Event({ "place": "here" });
          eventObj.save(function(err,eventObj) {
            logger("Event", eventObj);
            callback(err,eventObj);
          });
        },
        function(eventObj,callback) {
          var userObj = new User({ "name": "bob" });
          userObj.save(function(err,userObj) {
            logger("User", userObj);
            callback(err,eventObj,userObj);
          });
        },
    
        // Store the references in the array for the Other model
        function(eventObj,userObj,callback) {
          var other = new Other({
            "afield": "something"
          });
          other.portals.push(eventObj,userObj);
          other.save(function(err,other) {
            logger("Other Stored",other);
            callback(err,other);
          });
        },
    
        // See how it's all really stored
        function(other,callback) {
          Portal.find({},function(err,portals) {
            logger("Portals",portals);
            callback(err,other);
          });
        },
    
        // But watch the magic here
        function(other,callback) {
          User.find({},function(err,portals) {
            logger("Just Users!",portals);
            callback(err,other);
          });
        },
    
    
        // And constructed as one object by populate
        function(other,callback) {
          Other.findById(other.id)
            .populate('portals')
            .exec(function(err,other) {
              logger("Other populated",other);
              console.log("%s: %s",
                "1st Element", other.portals[0].constructor.modelName );
              console.log("%s: %s",
                "2nd Element", other.portals[1].constructor.modelName );
    
              callback(err);
            });
        }
    
      ],
      function(err) {
        // It's just a script, so clean up
        if (err) throw err;
        mongoose.disconnect();
      }
    );
    

    这应该解释一些事情以及"discriminators"是什么 . 所有东西都存储在绑定到基本模型的"one"集合中 . 其他所有内容都是使用该基地的 .discriminator() 定义的 . 的"name""class model"或"discriminator"存储在对象上 . 但请注意,它仅存储在集合中,而不是存储在它们被引用的位置,因为它只存储 _id 值 . 仔细看看输出:

    Event:
    { __v: 0,
      place: 'here',
      __t: 'Event',
      _id: 54a253ec456b169310d131f9,
      eventDate: Tue Dec 30 2014 18:27:40 GMT+1100 (AEDT) }
    
    User:
    { __v: 0,
      name: 'bob',
      __t: 'User',
      _id: 54a253ec456b169310d131fa }
    
    Other Stored:
    { __v: 0,
      afield: 'something',
      _id: 54a253ec456b169310d131fb,
      portals: [ 54a253ec456b169310d131f9, 54a253ec456b169310d131fa ] }
    
    Portals:
    [ { _id: 54a253ec456b169310d131f9,
        place: 'here',
        __v: 0,
        __t: 'Event',
        eventDate: Tue Dec 30 2014 18:27:40 GMT+1100 (AEDT) },
      { _id: 54a253ec456b169310d131fa,
        name: 'bob',
        __v: 0,
        __t: 'User' } ]
    
    Just Users!:
    [ { _id: 54a253ec456b169310d131fa,
        name: 'bob',
        __v: 0,
        __t: 'User' } ]
    
    Other populated:
    { _id: 54a253ec456b169310d131fb,
      afield: 'something',
      __v: 0,
      portals:
       [ { _id: 54a253ec456b169310d131f9,
           place: 'here',
           __v: 0,
           __t: 'Event',
           eventDate: Tue Dec 30 2014 18:27:40 GMT+1100 (AEDT) },
         { _id: 54a253ec456b169310d131fa,
           name: 'bob',
           __v: 0,
           __t: 'User' } ] }
    
    1st Element: Event
    2nd Element: User
    

    因此,所有"portal"类型只有一个集合,但如图所示有一些魔法 . "others"集合仅将 _id 值存储在"portals"的数组中 . 这就是mongoose引用的工作方式,其中"model"和附加的模式不存储在数据中,而是作为代码定义的一部分 .

    "discriminator"部分将此"model name"存储在该字段上,因此可以将其解析为正确的类型,但它仍然存在于同一个集合中,并且部分 User 模型魔术已经演示 .

    为什么?这是 .populate() 的工作方式 . 在引擎盖下, $in 运算符与数组内容一起使用,因此预计它们都在一个地方 . 但您仍然可以解析类型,如图所示 .

    如果您希望使用单独的集合,那么您将手动执行所有操作并存储模型名称并自行查询其他集合以供参考 .

相关问题