Permalink
Browse files

Document eager-fetched associations

  • Loading branch information...
1 parent cb570f0 commit dcd94f8917cdcdf7701bc2b0cb820f13494103a8 mde committed Apr 2, 2013
Showing with 199 additions and 125 deletions.
  1. +199 −125 README.md
View
324 README.md
@@ -183,6 +183,182 @@ Model-item instances emit these events:
* beforeUpdate
* update
+## Associations
+
+Model has support for associations: including hasMany/belongsTo and
+hasOne/belongsTo. For example, if you had a `User` model with a single
+`Profile`, and potentially many `Accounts`:
+
+```javascript
+var User = function () {
+ this.property('login', 'string', {required: true});
+ this.property('password', 'string', {required: true});
+ this.property('confirmPassword', 'string', {required: true});
+
+ this.hasOne('Profile');
+ this.hasMany('Accounts');
+};
+```
+
+A `Book` model that belongs to an `Author` would look like this:
+
+```javascript
+var Book = function () {
+ this.property('title', 'string');
+ this.property('description', 'text');
+
+ this.belongsTo('Author');
+};
+```
+
+Add the `hasOne` relationship by calling 'set' plus the name of the owned
+model in singular (in this case `setProfile`). Retrieve the associated item by
+using 'get' plus the name of the owned model in singular (in this case
+`getProfile`). Here's an example:
+
+```javascript
+var user = User.create({
+ login: 'asdf'
+, password: 'zerb'
+, confirmPassword: 'zerb'
+});
+user.save(function (err, data) {
+ var profile;
+ if (err) {
+ throw err;
+ }
+ profile = Profile.create({});
+ user.setProfile(profile);
+ user.save(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ user.getProfile(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ console.log(profile.id ' is the same as ' + data.id);
+ });
+ });
+});
+```
+
+Set up the `hasMany` relationship by calling 'add' plus the name of the
+owned model in singular (in this case `addAccount`). Retrieve the associated
+items with a call to 'get' plus the name of the owned model in plural (in
+this case `getAccounts`). An example:
+
+```javascript
+var user = User.create({
+ login: 'asdf'
+, password: 'zerb'
+, confirmPassword: 'zerb'
+});
+user.save(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ user.addAccount(Account.create({}));
+ user.addAccount(Account.create({}));
+ user.save(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ user.getAccounts(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ console.log('This number should be 2: ' + data.length);
+ });
+ });
+});
+```
+
+A `belongsTo` relationship is created similarly to a `hasOne`: by calling 'set'
+plus the name of the owner model in singular (in this case `setAuthor`).
+Retrieve the associated item by using 'get' plus the name of the owner model
+in singular (in this case `getAuthor`). Here's an example:
+
+```javascript
+var book = Book.create({
+ title: 'How to Eat an Entire Ham'
+, description: 'Such a poignant book. I cried.'
+});
+book.save(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ book.setAuthor(Author.create({
+ familyName: 'Neeble'
+ , givenName: 'Leonard'
+ }));
+ book.save(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ book.getAuthor(function (err, data) {
+ if (err) {
+ throw err;
+ }
+ console.log('This name should be "Neeble": ' + data.familyName);
+ });
+ });
+});
+```
+
+### 'Through' associations
+
+'Through' associations allow a model to be associated with another *through* a
+third model. A good example would be a Team linked to Players through
+Memberships.
+
+```javascript
+var Player = function () {
+ this.property('familyName', 'string', {required: true});
+ this.property('givenName', 'string', {required: true});
+ this.property('jerseyNumber', 'string', {required: true});
+
+ this.hasMany('Memberships');
+ this.hasMany('Teams', {through: 'Memberships'});
+};
+
+var Team = function () {
+ this.property('name', 'string', {required: true});
+
+ this.hasMany('Memberships');
+ this.hasMany('Players', {through: 'Memberships'});
+};
+
+var Membership = function () {
+ this.hasMany('Memberships');
+ this.hasMany('Teams');
+};
+```
+
+The API for this is the same as with normal associations, using the `set`/`add`
+and `get`, with the appropriate association name (not the model name). For
+example, in the case of the Team adding Players, you'd use `addPlayer` and
+`getPlayer`.
+
+### Named associations
+
+Sometimes you need mutliple associations to the same type of model (e.g., I have
+lots of Friends and Relatives who are all Users). You can accomplish this in
+Model using named associations:
+
+```javascript
+var User = function () {
+ this.property('familyName', 'string', {required: true});
+ this.property('givenName', 'string', {required: true});
+
+ this.hasMany('Kids', {model: 'Users'});
+};
+```
+
+The API for this is the same as with normal associations, using the `set`/`add`
+and `get`, with the appropriate association name (not the model name). For
+example, in the case of `Kids`, you'd use `addKid` and `getKids`.
+
## Querying
Model uses a simple API for finding and sorting items. Again, it should look
@@ -330,149 +506,47 @@ sort-direction is ascending ('asc'), so you can specify a property to sort on
```
-## Associations
-
-Model has basic support for associations: including hasMany/belongsTo and
-hasOne/belongsTo. For example, if you had a `User` model with a single
-`Profile`, and potentially many `Accounts`:
-
-```javascript
-var User = function () {
- this.property('login', 'string', {required: true});
- this.property('password', 'string', {required: true});
- this.property('confirmPassword', 'string', {required: true});
-
- this.hasOne('Profile');
- this.hasMany('Accounts');
-};
-```
-
-A `Book` model that belongs to an `Author` would look like this:
-
-```javascript
-var Book = function () {
- this.property('title', 'string');
- this.property('description', 'text');
+## Eager loading of associations (SQL adpaters only)
- this.belongsTo('Author');
-};
-```
+You can use the 'includes' option to specify second-order associations that
+should be eager-loaded in a particular query (avoiding the so-called N + 1 Query
+Problem). This will also work for 'through' associations.
-Add the `hasOne` relationship by calling 'set' plus the name of the owned
-model in singular (in this case `setProfile`). Retrieve the associated item by
-using 'get' plus the name of the owned model in singular (in this case
-`getProfile`). Here's an example:
+For example, with a Team that `hasMany` Players through Memberships, you might
+want to display the roster of player for every team when you display teams in a
+list. You could do it like so:
```javascript
-var user = User.create({
- login: 'asdf'
-, password: 'zerb'
-, confirmPassword: 'zerb'
-});
-user.save(function (err, data) {
- var profile;
+Team.all({}, {includes: ['player']}, function (err, data) {
+ var teams;
if (err) {
throw err;
}
- profile = Profile.create({});
- user.setProfile(profile);
- user.save(function (err, data) {
- if (err) {
- throw err;
- }
- user.getProfile(function (err, data) {
- if (err) {
- throw err;
- }
- console.log(profile.id ' is the same as ' + data.id);
+ teams = data;
+ teams.forEach(function (team) {
+ console.log(team.name);
+ team.players.forEach(function (player) {
+ console.log(player.familyName + ', ' + player.givenName);
});
});
});
```
-Set up the `hasMany` relationship by calling 'add' plus the name of the
-owned model in singular (in this case `addAccount`). Retrieve the associated
-items with a call to 'get' plus the name of the owned model in plural (in
-this case `getAccounts`). An example:
+The eagerly fetched association will be in a property on the top-level item with
+the same name as the association (e.g., Players will be in `players`).
-```javascript
-var user = User.create({
- login: 'asdf'
-, password: 'zerb'
-, confirmPassword: 'zerb'
-});
-user.save(function (err, data) {
- if (err) {
- throw err;
- }
- user.addAccount(Account.create({}));
- user.addAccount(Account.create({}));
- user.save(function (err, data) {
- if (err) {
- throw err;
- }
- user.getAccounts(function (err, data) {
- if (err) {
- throw err;
- }
- console.log('This number should be 2: ' + data.length);
- });
- });
-});
-```
-
-A `belongsTo` relationship is created similarly to a `hasOne`: by calling 'set'
-plus the name of the owner model in singular (in this case `setAuthor`).
-Retrieve the associated item by using 'get' plus the name of the owner model
-in singular (in this case `getAuthor`). Here's an example:
+If you have an item, and you're not certain whether an association is already
+loaded, you can check for the existence of this property before doing a per-item
+fetch:
```javascript
-var book = Book.create({
- title: 'How to Eat an Entire Ham'
-, description: 'Such a poignant book. I cried.'
-});
-book.save(function (err, data) {
- if (err) {
- throw err;
- }
- book.setAuthor(Author.create({
- familyName: 'Neeble'
- , givenName: 'Leonard'
- }));
- book.save(function (err, data) {
- if (err) {
- throw err;
- }
- book.getAuthor(function (err, data) {
- if (err) {
- throw err;
- }
- console.log('This name should be "Neeble": ' + data.familyName);
- });
+if (!someTeam.players) {
+ someTeam.getPlayers(function (err, data) {
+ console.dir(data);
});
-});
-```
-
-### Named associations
-
-Sometimes you need mutliple associations to the same type of model (e.g., I have
-lots of Friends and Relatives who are all Users). You can accomplish this in
-Model using named associations:
-
-```javascript
-var User = function () {
- this.property('familyName', 'string', {required: true});
- this.property('givenName', 'string', {required: true});
-
- this.hasMany('Friends', {model: 'Users'});
- this.hasMany('Relatives', {model: 'Users'});
-};
+}
```
-The API for this is the same as with normal associations, using the `set`/`add`
-and `get`, with the appropriate association name (not the model name). For
-example, in the case of `Friends`, you'd use `addFriend` and `getFriends`.
-
- - -
Model JavaScript ORM copyright 2112 mde@fleegix.org.

0 comments on commit dcd94f8

Please sign in to comment.