附加在Schemaless Array内

我是MongoDB的新手,到目前为止玩弄它,遇到了一个问题,在这里我试图在Schema-Less数组中添加多个对象时遇到困难。到目前为止,我尝试$ push追加数组内的多个对象,但是,有一个Mongo错误

[MongoError: Can't use $push/$pushALL within non-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 } 

在控制器我有访问到EventType和人员模型控制器:

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

为了debugging的目的,我试图给Array的混合types模式 ,但没有得到它的工作

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

当我删除$推更新然后只有整个事件对象进入邀请 ,我创build无模式数组的原因是因为我正在处理不同types的邀请,这里我只是描述事件邀请,否则有不同types的我正在处理的邀请喜欢, 请求的用户邀请会议的邀请,所以会有不同的objectId的组合,我认为应该有方式附加到无模式数组在mongoDB。

编辑:

以下是我想出来的。 不能让它工作。

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

我怎么能做这样的事情?

尽pipe你的模式,在顶部的错误意味着在集合中有一个匹配的文档没有这个字段设置为一个数组,但它是另一种types。 可能只是一个string或对象。

这是一个小小的,人为的例子列表来演示:

 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及更高版本(这个错误string来自哪里)有所改进,对实际的问题更精确一些。 所以在现代版本中,你会被告知到底什么是错的。

尽pipe模式,像.update()方法.findOneAndUpdate()为方便起见,我使用了.findOneAndUpdate() )稍微绕过mongoose模式定义,并直接进入数据库。 所以有可能做到这一点,也有可能你已经有了一个文档,或者在不同的模式定义已经到位时创build。

所以这是第一个问题


你似乎要问的其余部分是数组中的“多态”types的关联,也是你不希望将整个创build的对象“embedded”到数组中,而仅仅是对它的引用。

Mongoose具有“鉴别器”来允许这样的事情,允许对象的不同模型types被存储在相同的集合中,但是parsing为它们自己的对象和模式“types”。

在当前的文档示例之后,下面是一个可能的样例列表:

 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属性以及不同对象的其他定义属性。 然而,两者实际上都存储在相同的“人员”集合中,并且可以被这样查询。

将这些存储在Company对象上时,只有“引用id”值存储在数组中。 可以讨论你可能想要的,但这是“引用”和“embedded”模式模型之间的区别。 然而,当.populate()方法的时候,你可以看到对象被从被引用的集合中读取时恢复到完整forms。


因此,请检查您的集合中是否存在与架构定义不同的现有文档,并考虑显示的方法来表示不同“对象types”的“多态”关联。

请注意,这种types的分辨率只有在“引用”模式devise下才有支持,这也可能有其缺点。 如果您希望在单个Company集合中存储为“embedded式”的对象(例如),那么您将不会自动获得通过mongoose完成的各种架构types的对象parsingtypes。 解决不同types的对象将不得不在你的代码中手动完成,或者提供插件或者你做的。

更多

特定于所有目的,因为基于标准文档示例的东西似乎存在一些混淆,下面列出了更多的注释:

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

这应该解释一些事情和“鉴别者”是什么。 一切都存储在绑定到基本模型的“一个”集合中。 其他的一切都是使用该基地的.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 

所以所有的“门户”types只有一个集合,但是这里有一些魔法。 “其他”集合仅将_id值存储在“门户”数组中。 这就是mongoose参考的工作原理,“模型”和附加的模式不存储在数据中,而是作为代码定义的一部分。

“鉴别器”部分将这个“模型名称”存储在该字段中,以便可以将其parsing为正确的types,但它仍然在同一个集合中,并且是User模型魔术的一部分。

为什么? 这就是.populate()工作原理。 底层的数组内容使用$in操作符,因此所有的操作都在一个地方。 但是您仍然可以按照所示parsingtypes。

如果您希望使用单独的集合,那么您正在手动完成所有工作,并存储模型名称,并查询其他集合以供您自己引用。