Встраивание дубликатов вложенных документов «дружба» для моделирования границ взаимной дружбы в MongoDB - PullRequest
0 голосов
/ 16 февраля 2019

Я пытаюсь смоделировать двунаправленную дружбу в MongoDB.«Двунаправленный» означает, что, как и в Facebook, но в отличие от Twitter, если вы дружите с Сэмом, то Сэм тоже должен дружить с вами.В MongoDB обычно рекомендуемое решение ( пример ) выглядит примерно так:

  • Создайте коллекцию User (или узлы, чтобы использовать правильный термин теории графов)) и коллекцию Friendship (или ребра)
  • Каждый документ User содержит встроенный массив friends, каждый элемент которого содержит ObjectID каждого друга.Каждый элемент может также кэшировать информацию о друге, доступную только для чтения (например, имя, URL-адрес фотографии), что позволяет избегать перекрестных запросов для общих случаев использования, таких как «отображение списка моих друзей».
  • Добавление дружеских связей включает вставкуновый Friendship документ, а затем $push -внедрение идентификатора объекта Friendship в массив friends у обоих пользователей, а также кэшированная информация только для чтения о друзьях (например, имя), чтобы избежать многодокументных запросовпри отображении списков друзей.

Я рассматриваю другой дизайн, в котором вместо отдельной коллекции Friendship данные края будут храниться (дублироваться) в обоих узлах двунаправленной связи.Например:

  {
    _id: new ObjectID("111111111111111111111111"),
    name: "Joe", 
    pictureUrl: "https://foo.com/joe.jpg",
    invites: [
      ... // similar schema to friends array below
    ],
    friends: [
      {
        friendshipId: new ObjectID("123456789012345678901234"),
        lastMeeting: new Date("2019-02-07T20:35:55.256+00:00"),
        user1: {
          userId: new ObjectID("111111111111111111111111"),
          name: "Joe",  // cached, read-only data to avoid multi-doc reads
          pictureUrl: "https://foo.com/joe.jpg",
        },
        user2: {
          userId: new ObjectID("222222222222222222222222"),
          name: "Bill",  // cached, read-only data to avoid multi-doc reads
          pictureUrl: "https://foo.com/bill.jpg",
        },
      }
    ]
  },
  {
    _id: new ObjectID("222222222222222222222222"),
    name: "Bill",
    pictureUrl: "https://foo.com/bill.jpg",
    invites: [
      ... // similar schema to friends array below
    ],
    friends: [
      {
        friendshipId: new ObjectID("123456789012345678901234"),
        lastMeeting: new Date("2019-02-07T20:35:55.256+00:00"), // shared data about the edge
        user1: {  // data specific to each friend
          userId: new ObjectID("111111111111111111111111"),
          name: "Joe",  // cached, read-only data to avoid multi-doc reads
          pictureUrl: "https://foo.com/joe.jpg",
        },
        user2: { // data specific to each friend
          userId: new ObjectID("222222222222222222222222"),
          name: "Bill",  // cached, read-only data to avoid multi-doc reads
          pictureUrl: "https://foo.com/bill.jpg",
        },
      }
    ]
  }

Вот как я планирую справиться со следующим:

  • Чтения - все операции чтения для общих операций происходят только из отдельных User документов.Информация высокого уровня о друзьях (например, имя, URL-адрес изображения) кэшируется в массиве friends.
  • Приглашение друга - добавьте новый документ в массив invites, встроенный в обоих пользователей (не показан выше), который похож по структуре и функциональности на коллекцию friends, показанную выше
  • Приглашениепринято - с помощью updateMany добавьте новый идентичный внедренный документ в массив friends обоих пользователей и $pull элемент из массива invites обоих пользователей.Первоначально я буду использовать транзакции с несколькими документами для этих обновлений, но поскольку добавление дружеских отношений не является критичным по времени, это можно адаптировать для использования возможной согласованности.
  • Дружба аннулирована - используйте updateMany с фильтром для {'friends.friendshipId': new ObjectID("123456789012345678901234")} до $pull поддокумента дружбы из friends массивов обоих пользователей.Как и выше, вначале могут использоваться многодокументные транзакции, а затем возможная согласованность позднее, если это необходимо для масштабирования.
  • Обновление кэшированных данных - если пользователь изменяет информацию, кэшированную в friends (например, имя или URL-адрес изображения), это необычная операция, которая может выполняться медленно и по одному документу за раз, поэтомувозможна согласованность.

У меня есть две основные проблемы, о которых я хотел бы услышать ваш совет:

  • Каковы проблемы и недостатки описанного подходавыше?Я знаю об очевидных вещах: дополнительное хранилище, более медленные обновления, необходимость добавления логики очередей и повторных попыток для поддержки возможной согласованности, а также риск несинхронизации пограничных данных между двумя их копиями.Я думаю, что я в порядке с этими проблемами.Но есть ли другие, неочевидные проблемы, с которыми я, вероятно, столкнусь?

  • Вместо того, чтобы иметь поля user1 и user2 для каждого узла ребра, было бы этолучше использовать 2-элементный массив вместо?Почему или почему нет?Вот пример того, что я имею в виду:

    friends: [
      {
        friendshipId: new ObjectID("123456789012345678901234"),
        lastMeeting: new Date("2019-02-07T20:35:55.256+00:00"),
        users: [
          {
            userId: new ObjectID("111111111111111111111111"),
            name: "Joe",  // cached, read-only data to avoid multi-doc reads
            pictureUrl: "https://foo.com/joe.jpg",
          },
          {
            userId: new ObjectID("222222222222222222222222"),
            name: "Bill",  // cached, read-only data to avoid multi-doc reads
            pictureUrl: "https://foo.com/bill.jpg",
          },
        ],
      }
    ]
    

Кстати, я знаю, что графические базы данных и даже реляционные базы данных лучше в моделировании отношений по сравнению с MongoDB.Но по ряду причин я остановился на MongoDB на данный момент, поэтому, пожалуйста, ограничьте ответы на решения MongoDB, вместо того, чтобы указывать мне на использование графической или реляционной базы данных.Спасибо!

...