Nested collections/subdocuments inside List ?

2,465 views
Skip to first unread message

Oleksandr Bezhan

unread,
Dec 9, 2013, 12:10:48 PM12/9/13
to keyst...@googlegroups.com
Is it possible to have a nested collection inside a List object ? Similar to mongoose sub documents http://mongoosejs.com/docs/subdocs.html
For example I want to have an Order list which consists of order date and array of order items:

Order.add({
    date: { type: Types.Date, default: now() },
    orderItems: [{
            product: { type: Types.Relationship },
            price: { type: Types.Money }
        }]
})
Then it would be convenient to have these orderItems be manageable straight inside order object in admin panel.

Sure, I could make orderItems as a relationship and manage it as a seperate list, smth like this:

Order.add({
    date: { type: Types.Date, default: now() },
    totalCost: { type: Types.Money },
    orderItems: { type: Types.Relationship, ref: 'OrderItem', many: true}
})

OrderItem.add({
    order: { type: Types.Relationship, ref: 'Order' },
    product: { type: Types.Relationship, ref: 'Product' },
    price: { type: Types.Money }
})

But if i had order item in separate list it would mean that I could accidentally move it to another order, what would be definitely a mistake. Order item may be created inside specified order or deleted, but cannot be moved to another order.
This approach would also simplify content management I think.

How do you manage in such cases and how do you think what is a best approach ?

Btw, thanks for what you are doing, it's amazing :)

j...@keystonejs.com

unread,
Dec 10, 2013, 3:32:24 AM12/10/13
to keyst...@googlegroups.com
Hey Oleksandr,

It is possible to add nested schemas to lists, but they're not supported in the Admin UI. It's something that I'd like to add though! You're right, it'll make for clearer schemas and simplify content management.

In the meantime, you can use a nested mongoose schema by adding it to the List.schema like this:

Order.add({
    date: { type: Types.Date, default: now() },
    totalCost: { type: Types.Money }
});

var OrderItem = new mongoose.schema({
    order: { type: ObjectId, ref: 'Order' },
    product: { type: ObjectId, ref: 'Product' },
    price: { type: Number }
})

Order.schema.add({
    orderItems: [OrderItem]
});

Unfortunately you won't be able to use the Admin UI to manage the Order Items (yet), you'll have to create your own interface for that, but that may help...

Alternatively using a separate list for OrderItems isn't such a bad idea, it will let you report + filter on them easily, but you're right - we'd want to stop the Order from being changed after the OrderItem had been created. I have some ideas on how we could make that much easier without too much work in the Admin UI, and make it possible to create OrderItems from the Order screen as well.

If you're happy to go with it, you could set up OrderItems as a separate list and hopefully we can get those features built before it becomes a problem for you :)





j...@keystonejs.com

unread,
Dec 10, 2013, 3:36:47 AM12/10/13
to keyst...@googlegroups.com


On Tuesday, December 10, 2013 4:10:48 AM UTC+11, Oleksandr Bezhan wrote:

Btw, thanks for what you are doing, it's amazing :)

Thanks :) it's great to hear! 

lmu...@trinistorm.org

unread,
Jan 25, 2014, 11:53:16 PM1/25/14
to keyst...@googlegroups.com
Hello,

How would one go about implementing this (nested schema)?

j...@keystonejs.com

unread,
Jan 30, 2014, 2:55:41 AM1/30/14
to keyst...@googlegroups.com
Hi,

I'd recommend the mongoose docs for SubDocuments for a good overview on how to set up nested schemas in your application.

Remember when you create a Keystone List, you can use any mongoose functionality you like by accessing MyList.schema before you call the register method.

So to rewrite the first example in the mongoose docs I linked above using KeystoneJS, it would be:

var keystone = require('keystone'),
mongoose = keystone.mongoose;

var ChildList = new mongoose.Schema({ name: 'string' });

var ParentList = new keystone.List();
parentList.schema.add({
  children: [childSchema]
});

Vincent

unread,
Jul 11, 2014, 1:19:26 PM7/11/14
to keyst...@googlegroups.com
Hello,

I've started a tentative for adding a new Type in Keystone supporting list of subdocuments:


Currently it only displays the item edit form for an existing full-document in the collection. Saving is ongoing but I feel I'm getting lost (free-falling?) in Keystone's internals.


````
// Modelling subdocuments:
var Component = new keystone.List('Component');
Component.add({
    occurrence: Number,
    element: String,  // Could be a keystone Type like a Relationship, or a subdocument, e.g., {technicalName: String, commonName: String}
});

// Modelling main documents
var Car = new keystone.List('Car');

Car.add({
    name: { type: String, required: true },
   
    components: { type: Array, model: Component}
});


Car.register();
// don't register Component



// Let's insert a first item by hand and use keystone to display the related form

var newCar = Car.model({
    name: 'K2000',
    components: [
        {
            occurrence: 4,
            element: 'Wheel'
        },
        {
            occurrence: 2531,
            element: 'Screw'
        }
    ]
});

newCar.save(function(err, d) {
    console.log('saved', err);
    console.log('d', d);
});
````

What could be inspiring: nested schemas of forms-angular:

Help and comments are welcome :-)

Vincent

Ryan Cole

unread,
Jan 20, 2015, 4:38:34 PM1/20/15
to keyst...@googlegroups.com
OK I know this thread is old, but maybe someone can help me out.

So I have 3 models that are linked together with relationships.

User model: the users

Userday model: available hours(simple int), user ref,  

Day model: date, array of userdays

Now when I go to save a new Day, do the userday docs already need to exist? Can I create them by saving the day and passing in an array of objects that match the Userday model? Maybe I don't fully understand how to do this subdocument thing. 

I've read the mongoose bit on subdocuments but it's hard for me to see the right way to do this with the Keystone wrapper...

Halp!

Reply all
Reply to author
Forward
0 new messages