Вернувшийся пользователь, если оба следуют друг за другом, используя агрегацию

(Во-первых, объяснение сложных вещей на английском - не моя сильная сторона. Я постарался быть как можно более подробным, чтобы вы могли понять мою проблему).


Недавно я начал копаться в Mongoose в поисках веб-приложения на основе node.js, над которым я работаю. Обычная, включая пользовательскую систему, где каждый пользователь может следовать за другими. Если userA следует за пользователемB, он добавляет новый документ в коллекцию Connections, содержащий userA «_id» как «idA» и userB «_id» как «idB».

Коллекция Пользователи

{
    "_id" : ObjectId("HarrysID"),
    "name" : "Harry"
    "password": "..."
}

{
    "_id" : ObjectId("TomsID"),
    "name" : "Tom"
    "password": "..."
}

Коллекция Подключения

{
    "_id" : ObjectId("5ac130e9d6239151a86035c7"),
    "idA" : ObjectId("TomsID"),
    "idB" : ObjectId("HarrysID"),
}

{
    "_id" : ObjectId("5ac13102d6239151a86035c8"),
    "idA" : ObjectId("HarrysID"),
    "idB" : ObjectId("TomsID"),
}

Чего я пытаюсь достичь

Не вдаваясь в подробности, вы можете знать о системе Twitter, где вы можете написать сообщение пользователю, только если этот пользователь подписался на вас в ответ. Очевидно, это легко сделать с помощью двух запросов:
Гарри следует за Томом? Если это правда, следует ли пользователь Том за Гарри? => Гарри может связаться с Томом !.

Теперь я хочу сделать это в большем масштабе. Мне нужна функция, которая возвращает список пользователей, с которыми можно связаться.
Я уже сделал это с помощью рекурсивной функции, которая сначала находит каждый документ в Connections, где idA - текущий пользователь. Затем он просматривает возвращенный список соединений и проверяет, существует ли документ, в котором «idB» следует за «idA» - если нет, он удаляет документ из окончательного результирующего массива. Я бы не стал считать себя профессионалом в области баз данных, но эта система кажется действительно неэффективной во многих отношениях и в значительной степени является магнитом для всех видов ошибок.

Есть ли лучший способ сделать это? Я уверен, что aggregate (), project () и / или group () будут полезны, но как?

Как это должно выглядеть в итоге?
Поскольку Гарри следует за Томом, а Том следует за Гарри, в контактах Гарри должен быть Том (следующий).

listContacts(ObjectId("HarrysID"), function(err, result) {
    console.info("Harry can message these users = ", result);
    /* Harry can message these users = 
        [{
            "_id" : ObjectId("5ac13102d6239151a86035c8"),
            "idA" : ObjectId("HarrysID"),
            "idB" : ObjectId("TomsID"),
        }]
    */
});

Спасибо, что нашли время!

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
34
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Другой способ: 1 добавить новое поле friend в схему пользователя и ввести массив. Если пользователь добавит друга b и b, разрешите этой системе запросов вставлять a и b в поля своих друзей. Пользователь a также может отправлять сообщения b и b. Другой способ 2 создать схему друзей. Если пользователь a добавляет друга b и b, разрешает этой системе запросов вставлять a и b в друзей. Пользователь a также может отправлять сообщения b и b.

Ответ принят как подходящий

Один из способов приблизиться к этому - создать новую коллекцию, содержащую информацию, необходимую для ответа на этот вопрос, может ли Пользователь Б связаться с пользователем Б? Я не знаю хорошего названия коллекции, но одна возможность - contactable.

Это может помочь вам ответить на вопрос с помощью только одного запроса, если каждый документ внутри представляет одного пользователя, а документы содержат список других идентификаторов пользователей, принадлежащих тем, с кем можно связаться, например:

Контактная коллекция

{
    "_id" : ObjectId("HarrysID"),
    "can_contact" : [ObjectId("TomsID"), ObjectId("AlicesID"), ...]
}
{
    "_id" : ObjectId("TomsID"),
    "can_contact": [ObjectID("HarrysID")]
}

Сложная часть использования этого подхода - поддерживать эту коллекцию в актуальном состоянии, что означает, что каждый раз, когда пользователь следует за другим пользователем, есть вероятность, что вам потребуется изменить два документа (если это следующее действие создает взаимное соединение, при котором оба пользователя подписываются друг на друга). Точно так же действие отписаться, например, когда пользователь B отменяет подписку на пользователя A, потребует от вас удаления идентификатора пользователя A из списка внутри списка can_contact пользователя B, и наоборот для списка пользователя A.

Now I want to do this on a bigger scale. I need a function which returns a list of users someone can contact.

Чтобы вернуть полный список пользователей, а не только идентификаторы пользователей, это можно расширить до поддержки списка объектов, представляющих пользователей. Однако эта денормализация создаст аналогичную проблему с ремонтопригодностью, потому что каждый раз, когда пользователь обновляет какую-либо информацию своего профиля, которую вы хотите сохранить в этом списке, вам придется выполнить запрос, чтобы определить, какие документы в коллекции необходимо обновить. На самом деле, чтобы упростить запросы, может быть лучше сохранить этот исходный список идентификаторов пользователей в виде строковых значений, потому что таким образом ваш запрос может быть простым, как в этот пример.

В среде JavaScript с mongoose у вас может быть функция, которая принимает userId и объект user в качестве аргументов и выполняет необходимое обновление следующим образом:

contactableSchema = {
    id : ObjectId,
    can_contact : [Object],
    can_contact_user_ids : [String]
}

var Contactable = mongoose.model('Contactable', contactableSchema);

function updateContactableCollection(userId, updatedUser) {
  Contactable.find({can_contact_user_ids: userId}, (err, contactables) => {
    if (contactables) {
      contactables.forEach(contactable => {
        // now must update the right element within this contactable's "can_contact" list

      })
    }
  })
}

Другие вопросы по теме