Edit this page

Relationships

Keystone enhances MongoDB's ability to store the ObjectIDs of related documents in a field (or many related ObjectIDs in an Array) with support for Relationship fields and Definitions in Models.

Relationship Fields

ObjectId or Array — Displayed as an auto-suggest field in the Admin UI

Stores references to ObjectIDs from another Model in an ObjectID field or array to create one-many or many-many relationships.

Specify the related Model using the ref option. For a many-many relationship, set the many option to true.

For example, if you wanted to link a Post model to a single Author and many PostCategories, you would do it like this:

Post.add({
  author: { type: Types.Relationship, ref: 'User' },
  categories: { type: Types.Relationship, ref: 'PostCategory', many: true }
});

Relationship Filters

You can filter a relationship field using the filters option.

The filters option is an object of key/value pairs, in which the keys correspond to the fields of the related model to be filtered, and the values will either be literals or field names in the current model, the value of which will be used to filter the relationship.

In the example below, the author field will only allow selection of a User whose group field is equal to 'admin'.

Post.add({
  title: { type: String, required: true },
  category: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
  author: { type: Types.Relationship, ref: 'User', filters: { group: 'admin' } }
});

You can also filter by the value of another field on the model. You do this setting the value of the filter to the name of the field, prefixed by a colon (:).

In the example below, the author field will only allow selection of a User whose group field is equal to the value of the category field of the Post model.

Post.add({
  title: { type: String, required: true },
  category: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
  author: { type: Types.Relationship, ref: 'User', filters: { group: ':category' } }
});

Finally, you can also filter by the current model's _id field.

In the example below, the bestPost field will only allow selection of a Post whose author field is equal to the _id of the current document.

User.add({
  name: { type: String, required: true },
  group: { type: Types.Select, options: 'user, editor, admin', default: 'user' },
  bestPost: { type: Types.Relationship, ref: 'Post', filters: { author: ':_id' } }
});

NOTE You can only set filters on one-many relationships (i.e. when the many option is NOT set to true).

Populating related data in queries

You can populate related data for relationship fields thanks to Mongoose's populate functionality. To populate the author and category documents when loading a Post from the example above, you would do this:

Post.model.findOne().populate('author categories').exec(function(err, post) {
  // the author is a fully populated User document
  console.log(post.author.name);
});

NOTE Note that if no ObjectId is stored, or an invalid ObjectId is stored (e.g. a document has been deleted), author will be undefined in the example above.

Relationship Definitions

What if, in the example above, you wanted to see a list of the Posts by each Author? Because the relationship field is on the Post, you need to tell the Author (and the PostCategory) Model that it is being referred to. Doing so allows the Admin UI to represent the relationship from both sides.

You do this by calling the relationship method on the Model like this:

User.relationship({ path: 'posts', ref: 'Post', refPath: 'author' });

Options

path

the path of the relationship reference on the Model

ref

the key of the referred Model (the one that has the relationship field)

refPath

the path of the relationship being referred to in the referred Model

As you can see, the options provided to the relationship method mirror those of the relationship field it refers to.

NOTE Relationship definitions are optional; if you leave them out, the relationships simply won't be displayed in the Admin UI from the other side of the relationship. The relationship field will still work as expected.

Loading related items

Filtering one-to-many related items is easy; simply specify the ID of the item you wish to filter on like any other value:

Post.model.find().where('author', author.id).exec(function(err, posts) {
  // ...
});

To filter many-to-many related items, use an in condition and specify one (or more) ids as an array:

Post.model.find().where('categories').in([category.id]).exec(function(err, posts) {
  // ...
});