mongoose: How to insert a single subdocument - not

2020-07-17 16:37发布

问题:

In mongoose you are able to declare a schema for a subdocument as per:

        var childSchema = new mongoose.Schema({ name: String, age: String });
        var parentSchema = new mongoose.Schema({
            children: [childSchema]
        });
        var Parent = mongoose.model('Parent', parentSchema);
        var Child = mongoose.model('Child', childSchema);

        var child = new Child({name:'Joe', age: '21'});
        var parent = new Parent();

        parent.children = [child];

        parent.save(function(err, saved) {
            if(err) console.error(err);
        });

Seems the subdocument type needs to be an array. I need to be able to save the subdocument as a single instance, not an array ie:

        var childSchema = new mongoose.Schema({ name: String, age: String });
        var parentSchema = new mongoose.Schema({
            children: childSchema // not an array
        });
        var Parent = mongoose.model('Parent', parentSchema);
        var Child = mongoose.model('Child', childSchema);

        var child = new Child({name:'Joe', age: '21'});
        var parent = new Parent();

        parent.children = child; // not an array

        parent.save(function(err, saved) {
            if(err) console.error(err);
        });

So not an array or a ref, just a single instance subdocument. Is this possible? If not should I use:

var childInstance = child.toObject();

回答1:

Sometimes it is hard to see obvious. You do not need another schema to achieve what you want. You can simply define your sub document within your parent schema like this:

    var parentSchema = new mongoose.Schema({
        child: { 'name' : String, 'age' : Number }  // not an array, just a sub document
    });
    var Parent = mongoose.model('Parent', parentSchema);

    var parent = new Parent();
    parent.child.name = "Joe";
    parent.child.age  = 13;

    parent.save(function(err, saved) {
        if(err) console.error(err);
    });


回答2:

OOPS! Edit:

I totally misread your question.. So you want to store a single subdocument? Then why you had the property named as children..

You can use:

var ParentSchema    =   new schema({
    name : String,
    child: Object
});

// etc.

john.child = new Child({name: 'kiki'});
// which is actually the same as: john.child = {name:'kiki'};
john.save();

This way, mongoose converts the document into a simple object and stores it. But i don't see the use of doing it this way? It doesn't have the benefit of a Schema and is used a default object. Not putting it in an array also blocks you from adding more nested items.

Old:

Instead of injecting the child schema's directly to the parent schema, you need to link them first (and you want to store them separately right?).

So we get two collections: parents and children (=Parent && Child). All documents of collection children are linked to a specific parents collection. And one parent document has zero, one or multiple children documents linked.

In this way you maintain the ability to modify the schema (like attaching methods or statics) and keep the documents neatly separated AND you can have the 'child' effect you wanted:

//  get mongoose.
var mongoose = require('mongoose');

//  connect to your local pc on database myDB.
mongoose.connect('mongodb://localhost:27017/myDB');

//  parent schema.
var parentSchema = new mongoose.Schema({
  name     : String,
  //  the ObjectId's of the children links the two schema's.
  children   : [{type:mongoose.Schema.Types.ObjectId, ref:'Child'}]
});

//  child schema.
var childSchema = new mongoose.Schema({
  name   : String,
  //  the parent's ObjectId links to the owner.
  parent : {type:mongoose.Schema.Types.ObjectId, ref:'Parent'}
});

//  model the schema's.
var Child   = mongoose.model('Child', childSchema),
    Parent  = mongoose.model('Parent', parentSchema);

//  create a 'parent'.
//  we are not assigning the children yet.
var john     = new Parent({name:'John'});

//  create two children and save them. Link them to john.
var child1   = new Child({name:'Mimi', parent:john._id}),
    child2   = new Child({name:'Kiki', parent:john._id});

//  push child to children property of john.
john.children.push(child1);
john.children.push(child2);

//  save everything.
john.save();
child1.save();
child2.save();

This will return the following:

/**
Children: 
[ { name: 'Mimi',                                                                                                                                                                                                                            
    parent: 537258f63eb92b3201b65e56,                                                                                                                                                                                                        
    _id: 537258f63eb92b3201b65e57,                                                                                                                                                                                                           
    __v: 0 },                                                                                                                                                                                                                                
  { name: 'Kiki',                                                                                                                                                                                                                            
    parent: 537258f63eb92b3201b65e56,                                                                                                                                                                                                        
    _id: 537258f63eb92b3201b65e58,                                                                                                                                                                                                           
    __v: 0 } ]

Parent:
[ { name: 'John',                                                                                                                                                                                                                            
    _id: 537258f63eb92b3201b65e56,                                                                                                                                                                                                           
    __v: 0,                                                                                                                                                                                                                                  
    children: [ 537258f63eb92b3201b65e57, 537258f63eb92b3201b65e58 ] } ]
*/

You can also make a static function to collection parents: addChild(child, callback), so the code looks more clean (javaísh style).

pseudo-code:

// add custom static method.
parentSchema.statics.addChild = function(child, callback) {
   // implementation.
}

// the way you call this method:
parentModel.addChild(new Child.etc..);

Hope this helps and good luck (:



回答3:

if the relationship of 2 collections is 1 to 1. you can use 'ref'

var personSchema = Schema({
  _id     : Number,
  name    : String,
  age     : Number,
  stories : [{ type: Schema.Types.ObjectId, ref: 'Story' }]
});

var storySchema = Schema({
  _creator : { type: Number, ref: 'Person' },
  title    : String,
  fans     : [{ type: Number, ref: 'Person' }]
});

source: http://mongoosejs.com/docs/populate.html