Вы просыпаетесь с идеей своей мечты. Вы спешите к компьютеру и решаете, как будет работать ваше революционное приложение, меняющее парадигму. Ваши волшебные каркасы уже заманивают ваш разум мечтами о славе и богатстве. Затем вы садитесь за очень гламурную задачу проектирования схемы и управления базой данных… и тут начинается самое интересное…

Мы все были там. Дизайн схемы для среднего разработчика может быть зверем. Вы думаете, что разобрались, только для того, чтобы понять, что ваши искаженные вызовы в базу данных приносят вам тарелки брошенных спагетти. Вы переделываете дизайн, добавляете сводные таблицы и безудержно плачете.

Программисты знают, когда нужно искать лучшие решения, и отличным решением для управления базами данных является Sequelize, многодиалектный ORM для Node.JS. Если вы не знакомы с ним, вы можете найти их документы здесь:

http://docs.sequelizejs.com/

Распространенная жалоба на Sequelize заключается в том, что их документы очень похожи на спагетти, о которых я упоминал ранее. Другими словами, они беспорядок. Я не собираюсь бросать здесь камни. Я уверен, что создание самого Sequelize было непростой задачей, и у этой команды определенно есть дела поважнее, чем постоянно улучшать документацию. Они слишком заняты улучшением самой библиотеки. Люблю вас ребята!

Они ушли? Фу. Теперь вернемся к уничтожению их документов

Вы можете справиться с основными задачами определения модели и проверки, но колеса начинают отрываться, когда вы начинаете возиться с ассоциациями. И это, дамы и господа, и привело нас сюда сегодня. Я собираюсь немного поговорить об ассоциациях и магических методах. Каждый раз, когда я смотрю их в документах, я в конечном итоге сбит с толку. Поиск Google выдает сообщения, в которых люди в основном вырезали и вставляли документы и использовали свои собственные тупые слова, чтобы объяснить их. Так что, если вы окажетесь в том же месте, надеюсь, этот пост завершит ваши поиски.

Давайте возьмем пример базы данных со следующим:

const User = sequelize.define(‘user’, {
  name: Sequelize.STRING
})
const Project = sequelize.define(‘project’, {
  title: Sequelize.STRING
})
const WorkSite = sequelize.define(‘site’, {
  name: Sequelize.STRING,
  location: Sequelize.STRING
})

Теперь давайте разберемся с нашими ассоциациями. Какие у нас варианты? Есть «один к одному», «один ко многим» и «многие ко многим». Об этом легче говорить, когда мы думаем о моделях как о Целях и Источниках. В Sequelize это переводится как:

Один к одному:

Model.belongsTo

Source.belongsTo(Target) // targetID will be added to Source

Model.hasOne

Source.hasOne(Target) // sourceId will be added to Target

Подождите... эти два взаимозаменяемы! Что я использую, когда? Дело в том, что вы должны использовать то, что имеет для вас больше смысла. Это действительно сводится к семантике. В наших примерах было бы более разумно сказать, что проект имеет рабочий сайт, а не рабочий сайт, принадлежащий проекту. Чтобы было еще понятнее: у человека есть голова или голова принадлежит человеку? Хотя оба они верны, утверждение, что голова принадлежит человеку, звучит странно. Как будто у нее голова в рюкзаке, а не на шее. Однако эти примеры показывают, что если вы имеете дело с более абстрактными моделями, ваш выбор может помочь определить взаимосвязь для читателя. Если вы имеете дело с bar и baz, а не с головами и людьми, ваше решение может помочь читателю немного лучше осмыслить отношения.

Теперь мы получили доступ к некоторым магическим методам: get и set. Они добавляют еще один уровень абстракции. Вместо:

WorkSite.findOne({
  where: {
    projectId: req.body.id
  }
 })
  .catch(err => console.error(err))

ты можешь сделать:

Project.findById(req.body.id)
  .then((project) => project.getWorkSite())
  .catch(err => console.error(err))

Боже мой. Это немного опрятнее, не так ли?

Часто, если у вас есть ассоциация, вам понадобятся данные в связанном экземпляре.

Вот тут-то и начинается нетерпеливая загрузка. Не нужно делать дополнительный вызов, вам просто нужно воспользоваться опцией «включает»:

Project.findAll({
  include: [{
      model: User,
      where: { name: req.body.name }
  }]
})

или мой любимый:

include: { all: true }

Вот это вкусные спагетти!

Один ко многим и многие ко многим:

Model.hasMany

Скажем, мы работаем с соглашением, в котором у одного пользователя будет много проектов. Чтобы просто добавить идентификатор пользователя в наши проекты, мы можем сказать:

User.hasMany(Project)

Как и выше, теперь мы можем получать проекты и устанавливать проекты по-своему:

user.setProjects([project1, project2])

Вуаля!

Model.belongsToMany

Ты все еще со мной? Потому что все вот-вот сойдет с ума… Столы дешевы, друзья мои, и если вы обнаружите, что делаете прогибы назад, чтобы ваши столы работали, вероятно, пришло время для сводного стола или стола соединения. Пришло время приготовить вкусные спагетти с API…

В духе сотрудничества мы решили создать несколько команд. Так что теперь у наших Проектов будет много Пользователей. Когда вы используете ownToMany, вы строите таблицу соединения со своей ассоциацией, используя «сквозной»:

Project.belongsToMany(User, {through: ‘UserProject’});
User.belongsToMany(Project, {through: ‘UserProject’});

Просто назвав ассоциацию, вы создали таблицу UserProject, которая содержит только идентификаторы связанных экземпляров. Имейте в виду, что строки в этой таблице соединений не имеют своих индивидуальных идентификаторов, а модель проекта не имеет идентификатора пользователя или наоборот.

Если вы хотите, чтобы ваша таблица соединений имела индивидуальные идентификаторы или вы хотите добавить уникальные поля информации, вам нужно сначала создать таблицу соединений отдельно, а затем сделать свои ассоциации:

const UserProjects = sequelize.define(‘userProjects’, {
  id: Sequelize.INTEGER,
  status: Sequelize.STRING
})
Project.belongsToMany(User, {through: ‘UserProject’});
User.belongsToMany(Project, {through: ‘UserProject’});

У вас есть полный волшебный набор инструментов. Теперь вы можете getUsers, setUsers, addUser, addUsers, getProject, setProject, addProject, addProjects. Теперь возможности безграничны, и, к сожалению, пришло время вернуть вас к:

http://docs.sequelizejs.com/manual/tutorial/associations.html

В разделах «Связывание объектов» и «Создание с помощью ассоциаций» вы можете увидеть примеры истинного использования возможностей ассоциаций. С помощью инструментов вы сможете делать выборочные вызовы к базе данных, поэтому вам не придется пробираться через столько вложенных данных или еще раз: спагетти API. Ням!

Так это банда. Надеюсь, я немного демистифицирую ассоциации. Я знаю, что это был один из самых сложных аспектов работы с Sequelize, и я до сих пор не претендую на звание мастера. Этот титул я оставлю Мику Хансену, Яну Мейеру, и остальным членам команды Sequelize. Я не имел в виду неуважение к документам. Sequelize — фантастическая библиотека, которая позволяет нам, простым смертным, справляться с SQL гораздо проще. Спасибо команде Sequelize и продолжайте в том же духе!