Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mongoose: How to insert a single subdocument - not an array

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();
like image 784
Samuel Goldenbaum Avatar asked May 13 '14 09:05

Samuel Goldenbaum


People also ask

What is a subdocument in Mongoose?

Subdocuments are documents embedded in other documents. In Mongoose, this means you can nest schemas in other schemas. Mongoose has two distinct notions of subdocuments: arrays of subdocuments and single nested subdocuments.

How do you use Find one and delete in Mongoose?

Mongoose | findOneAndDelete() Function The findOneAndDelete() function is used to find a matching document, remove it, and passes the found document (if any) to the callback. Installation of mongoose module: You can visit the link to Install mongoose module. You can install this package by using this command.

What is __ V in MongoDB?

Posted On: Jun 24, 2021. In Mongoose the “_v” field is the versionKey is a property set on each document when first created by Mongoose. This is a document inserted through the mongo shell in a collection and this key-value contains the internal revision of the document.


2 Answers

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);
    });
like image 67
salihcenap Avatar answered Oct 14 '22 03:10

salihcenap


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 (:

like image 34
Kai Avatar answered Oct 14 '22 04:10

Kai