Ronen Teva
Ronen Teva

Reputation: 1427

Sails.js model - create associations to self?

Using sails.js v0.10.0-rc7, I would like to save a user and his friends.

I guess I need to somehow create a many-to-many association from a model to itself? Is it possible?

User.js:

module.exports = {
    attributes: {
        name: {
            type: 'string'
        },
        friends: {
            collection: 'user',
            via: ?
        }
    }
};

I'm using sails-mysql if it matters. I found this question, which is relevant but didn't solve my problem: https://github.com/balderdashy/waterline/issues/410

Thanks!

Update: So far I found two ways of doing this, but both uses redundant data:

Option 1

As suggested by hansmei:

module.exports = {
  attributes: {
    id:{
      type: 'integer',
      autoIncrement: true,
      primaryKey: true
    },
    name: {
      type: 'string'
    },
    friends: {
      collection: 'user',
      via: 'id'
    }
  }
}

Which requires me to save each friendship twice:

User.findOne(1).exec(function (err, user) {
    user.friends.add(2);
...
User.findOne(2).exec(function (err, user) {
    user.friends.add(1);
...

Option 2

attributes: {
    name: {
        type: 'string'
    },
    friends: {
        collection: 'user',
        via: 'friendOf',
        dominant: true
    },
    friendOf:{
        collection:'user',
        via:'friends'
    }
}

Which is also redundant, because friendship is always mutual.
(If user A is friend of user B, then user B must be a friend of user A)

Any suggestions?

Upvotes: 2

Views: 2747

Answers (2)

Ian Haggerty
Ian Haggerty

Reputation: 1751

This is a problem I wanted to solve in my Backbone.Sails plugin (until Waterline sorts it out).

I have completely rewritten the Sails blueprints, including support for self referencing many to many associations. In the model definition, you simply need to adopt the conventions required:

/api/models/Person.coffee

module.exports =
    attributes:
        friends:
            collection: "person"
            via: "_friends"
            dominant: true
        _friends:
            collection: "person"
            via: "friends"

Above, the friends attribute is the collection to be persisted. When persisting via the blueprints, the _friends collection will mirror that of the friends collection, mimicing any add or remove requests made - which means the friends collection will be a mutual relation without any extra code needed client side.

You can download the blueprints here. They'll work fine without the front-end part of this plugin (though you will need to compile them or npm install coffee-script). Just include them in your /api/blueprints folder and sails (0.10) will pick them up.

Whilst I appreciate this solution is far from ideal, it seems to be the best one for the time being (especially if you use blueprints for all your persistence). A better workaround would be the life-cycle callbacks - but they don't apply to the .add(id) and .remove(id) functions.

Upvotes: 0

hansmei
hansmei

Reputation: 701

I've managed to make associations to self in this way (at least for local disk storage). It should work for MySQL as well.

User model:

module.exports = {
  attributes: {
    id:{
      type: 'integer',
      autoIncrement: true,
      primaryKey: true
    },
    name: {
      type: 'string'
    },
    friends: {
      collection: 'user',
      via: 'id'
    }
  }
}

I'm not sure if it's a valid many-to-many relationship, but I think you can tweak it to make it work for you:)

Upvotes: 4

Related Questions