public
Last active

Many-to-Many in resourceful

  • Download Gist
README.md
Markdown

One-to-Many relationship

In some web applications, we need to define a one-to-many relationship between two entities. For example, let's take github.

Example: One-to-Many relationship exists between user and repository. User pksunkara have 2 repositories octonode and hub

The entities are defined as the following:

var resourceful = require('resourceful');

module.exports = resourceful.define('user', function () {
  this.string('name');
});
var resourceful = require('resourceful');

module.exports = resourceful.define('repository', function () {
  this.string('name');

  this.parent('User');
});

The documents are saved in couchdb as following:

[
  {
    _id: 'user/pksunkara',
    name: 'pksunkara',
    resource: 'User',
    repository_ids: ['octonode', 'hub']
  },
  {
    _id: 'repository/user/pksunkara/octonode',
    name: 'octonode',
    resource: 'Repository',
    user_id: 'pksunkara'
  },
  {
    _id: 'repository/user/pksunkara/hub',
    name: 'hub',
    resource: 'Repository',
    user_id: 'pksunkara'
  }
]

The following queries are supported:

var User = require('./user')
  , Repository = require('./repository');

// Get children using parent id
User.repositories('pksunkara', callback);
Repository.byUser('pksunkara', callback);

// Get children using parent obj
User.get('pksunkara', function (err, user) {
  user.repositories(callback);
});

// Create a child using parent id
User.createRepository('pksunkara', { id: 'police', name: 'police' }, callback);

// Create a child using parent obj
User.get('pksunkara', function (err, user) {
  user.createRepository({ id: 'police', name: 'police' }, callback);
});

// Get parent using child obj
Repository.get('user/pksunkara/hub', function (err, repository) {
  repository.user(callback);
});

Many-to-Many relationship

In some web applications, we need to define a many-to-many relationship between two entities. For examples, let's take github.

Example: Many-to-Many relationship exists between user and organization. Organization nodejitsu have 2 users pksunkara and marak whereas organization nodeapps have only one user marak.

Note: Organization and User are first layer resources, meaning they should never have a parent defined in their resource definition

In standard SQL databases, this relationship is represented using an intermediate member entity with two foreign keys user_id and organization_id.

This many-to-many in resourceful works by defining 2 intermediate entities named member and membership and creating 2 one-to-many relationships instead of 1 many-to-many relationship. user is one-to-many with membership and organization is one-to-many with member.

The entities are defined as the following:

var resourceful = require('resourceful');

module.exports = resourceful.define('user', function () {
  this.string('name'):
});
var resourceful = require('resourceful');

module.exports = resourceful.define('organization', function () {
  this.string('name');
});
var resourceful = require('resourceful');

module.exports = resourceful.define('member', function () {
  this.parent('Organization');
});
var resourceful = require('resourceful');

module.exports = resourceful.define('membership', function () {
  this.parent('User');
});

The documents are saved in couchdb as following:

[
  {
    _id: 'user/pksunkara',
    name: 'pksunkara',
    resource: 'User',
    membership_ids: ['nodejitsu']
  },
  {
    _id: 'user/marak',
    name: 'marak',
    resource: 'User',
    membership_ids: ['nodejitsu', 'nodeapps']
  },
  {
    _id: 'organization/nodejitsu',
    name: 'nodejitsu',
    resource: 'Organization',
    member_ids: ['marak', 'pksunkara']
  },
  {
    _id: 'organization/nodeapps',
    name: 'nodeapps',
    resource: 'Organization',
    member_ids: ['marak']
  },
  {
    _id: 'membership/user/marak/nodejitsu',
    resource: 'Membership',
    user_id: 'marak'
  },
  {
    _id: 'membership/user/pksunkara/nodejitsu',
    resource: 'Membership',
    user_id: 'pksunkara'
  },
  {
    _id: 'membership/user/marak/nodeapps',
    resource: 'Membership',
    user_id: 'marak'
  },
  {
    _id: 'member/organization/nodejitsu/marak',
    resource: 'Member',
    organization_id: 'nodejitsu'
  },
  {
    _id: 'member/organization/nodejitsu/pksunkara',
    resource: 'Member',
    organization_id: 'nodejitsu'
  },
  {
    _id: 'member/organization/nodeapps/marak',
    resource: 'Member',
    organization_id: 'nodeapps'
  }
]

The following queries are supported:

var User = require('./user')
  , Organization = require('./organization')
  , Membership = require('./membership')
  , Member = require('./member');

// Get users' names in an organization given by id
Organization.get('nodejitsu', function (err, org) {
  console.log(org.membership_ids);
  // => ['marak', 'pksunkara']
});

// Get organizations' names for an user given by id
User.get('marak', function (err, user) {
  console.log(org.member_ids);
  // => ['nodejitsu', 'nodeapps']
});

// An User joins an Organization
User.createMembership('pksunkara', { id: 'nodeapps' }, function () {
  Organization.createMember('nodeapps', { id: 'pksunkara' }, callback);
});

// An User leaves an Organization
Membership.destroy('user/pksunkara/nodeapps', function () {
  Member.destroy('organization/nodeapps/pksunkara', callback);
});

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.