0

Using feathers + sequelize (newbee) I want to build a many to many relation for Tag model where Tag can have many parents and many children.

    tags.belongsToMany(tags, {
      as: 'parents',
      through: tags_tags,
      foreignKey: 'parentId',
      otherKey: 'id',
      onDelete: 'RESTRICT',
      onUpdate: 'CASCADE',
    });

    tags.belongsToMany(tags, {
      as: 'children',
      through: tags_tags,
      foreignKey: 'id',
      otherKey: 'parentId',
      onDelete: 'CASCADE',
      onUpdate: 'CASCADE',
    });

Models seem to be fine and the database builds fine.

Now, I'm looking on how I should add a relation from within a feathers service. Getting really frustrated as this should be simple but I can't seem to find anything that helps me out. Am I missing out something obvious ?

app.services.tags
  .create({
    name: 'siteRoot',
  })
  .then(siteRoot => {
    // something like siteRoot.addChild() ?
    // app.services.tags.Model has .children
    // but how can I use it ?
  })

in models/tags.model.ts

// See http://docs.sequelizejs.com/en/latest/docs/models-definition/
// for more of what you can do here.
import { Sequelize, DataTypes, Op } from 'sequelize';
import { Application } from '../declarations';

export default function (app: Application) {
  const sequelizeClient: Sequelize = app.get('sequelizeClient');
  const tags = sequelizeClient.define(
    'tags',
    {
      id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
        autoIncrement: true,
      },
      deleted: {
        type: DataTypes.BOOLEAN,
        allowNull: false,
        defaultValue: false,
      },
      name: {
        type: DataTypes.STRING,
        allowNull: false,
      },
    },
    {
      hooks: {
        beforeCount(options: any) {
          options.raw = true;
        },
      },
      // timestamps: false,
      // tableName: 'tag',
      // underscored: true,
      indexes: [
        {
          fields: ['name'],
        },
    }
  );

  // eslint-disable-next-line no-unused-vars
  (tags as any).associate = function (models: any) {
    // Define associations here
    // See http://docs.sequelizejs.com/en/latest/docs/associations/

    const { tags } = models;

    tags.belongsTo(tags, {
      foreignKey: 'siteBaseTagId',
      as: 'siteBaseTag',
      onDelete: 'RESTRICT',
      onUpdate: 'CASCADE',
    });

    tags.hasMany(tags, {
      foreignKey: 'siteBaseTagId',
      as: 'siteTags',
    });
  };

  return tags;
}

and in models/tags-tags.model.ts


// See http://docs.sequelizejs.com/en/latest/docs/models-definition/
// for more of what you can do here.
import { Sequelize, DataTypes } from 'sequelize';
import { Application } from '../declarations';

export default function (app: Application) {
  const sequelizeClient: Sequelize = app.get('sequelizeClient');
  const tagsTags = sequelizeClient.define(
    'tags_tags',
    {
      id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
      },
      parentId: {
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true,
      },
      template: {
        type: DataTypes.STRING,
        allowNull: true,
      },
      url: {
        type: DataTypes.STRING,
        allowNull: true,
      },
    },
    {
      hooks: {
        beforeCount(options: any) {
          options.raw = true;
        },
      },
      timestamps: false,
    }
  );

  // eslint-disable-next-line no-unused-vars
  (tagsTags as any).associate = function (models: any) {
    // Define associations here
    // See http://docs.sequelizejs.com/en/latest/docs/associations/

    const { tags, tags_tags } = models;

    tags.belongsToMany(tags, {
      as: 'parents',
      through: tags_tags,
      foreignKey: 'parentId',
      otherKey: 'id',
      onDelete: 'RESTRICT',
      onUpdate: 'CASCADE',
    });

    tags.belongsToMany(tags, {
      as: 'children',
      through: tags_tags,
      foreignKey: 'id',
      otherKey: 'parentId',
      onDelete: 'CASCADE',
      onUpdate: 'CASCADE',
    });

    tags_tags.belongsTo(tags, {
      foreignKey: 'parentId',
    });
    tags.hasMany(tags_tags, {
      foreignKey: 'parentId',
    });

    tags_tags.belongsTo(tags, {
      foreignKey: 'id',
    });
    tags.hasMany(tags_tags, {
      foreignKey: 'id',
    });
  };

  return tagsTags;
}

Antony Gibbs
  • 1,321
  • 14
  • 24

1 Answers1

0

belongsToMany is one of the toughest associations to handle on the feathers side. Limitations are definitely not on the feathers end instead its how sequelize deals with them.

I am just a bit confused on your question. Like to help if you can clarify exactly where the help is needed.

Side Note: Perhaps you should rename your models a little diff. tags || tags_tags seem to close to each-other and will//may confuse at some point. Mean time like to point you to this thread which has been active for a while as everyone is handling the belongsToMany relation differently. Hope you can get some pointers from it. https://github.com/feathersjs/feathers/issues/852#issuecomment-406413342

If all you need is M:N then I would simply do this:

// tag model
tag.associate = models => {
  tag.belongsToMany(models.tag_tag, {
    through: 'parent_child_table', // name this table to your liking.
    foreignKey: 'tag_id'
  });
};

// tag_tag model
tag_tag.associate = models => {
  tag_tag.belongsToMany(models.tag, {
    through: 'parent_child_table', // table name MUST be the same as above
    foreignKey: 'tag_tag_id'
  });
};

The above will create another table parent_child_table where you will keep track of your associations. You will have to create a separate service to do CRUD on this table. This should get you started.

Ash
  • 585
  • 7
  • 15
  • Thanks :) But association M:N is between tags `tags.belongToMany(tags,{...})` tags_tags is the association table. There I'm setting up a tree structure where tags can have many parents too. – Antony Gibbs May 01 '20 at 19:49
  • So I have my table structure and database build fine. I was stuck in feathers on how to include remote models in query. Finally I posted an other question with a simpler situation here https://stackoverflow.com/questions/61419970/feathers-sequelize-add-include-remote-model/61492878 . And I did find how to include remote models via a hook, now I'm investigating on how to use where conditions onto those remote models. – Antony Gibbs May 01 '20 at 19:54