MongoDB позволяет несколько дат присоединения и оставленные даты - PullRequest
1 голос
/ 25 февраля 2020

В настоящее время у меня есть запрос, благодаря сообществу здесь, на StackOverflow.

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

{
    "_id" : ObjectId("5e35f2c840713a43aeeeb3d9"),
    "n" : "Example Group Chat",
    "members" : [ 
        {
            "uID" : "1",
            "j" : 1580580922,
            "i" : "1",
            "r" : "admin",
            "a" : 1
        }, 
        {
            "uID" : "4",
            "j" : 1580580922,
            "l" : 1581863346,
            "i" : "1",
            "r" : "member",
            "a" : 0
        }, 
        {
            "uID" : "5",
            "j" : 1580581922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "9",
            "j" : 1580593922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "3",
            "j" : 1580594920,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "8",
            "j" : 1580594999,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }
    ]
}

{
    "_id" : ObjectId("5e39d5d740713a43aeef5b26"),
    "members" : [ 
        {
            "uID" : "1",
            "j" : 1580580922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "2",
            "j" : 1580580922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }
    ]
}

Вы можете видеть групповой чат (первый) и чат один на один (второй). Групповые чаты могут иметь имена (n), индивидуальные чаты - нет. Каждый диалог имеет массив участников, в котором хранятся идентификатор пользователя (uID), дата присоединения (j), оставленная дата (l), поле приглашенного userID (i), поле роли (r) и активное поле ( а). Мне, вероятно, не понадобится поле "active", так как у меня есть отметка времени соединения / оставления, но все же. Я, вероятно, удалю его позже, так что это поле, вероятно, не будет включено.

Далее, у меня есть messages документ следующим образом:

{
    "_id" : ObjectId("5e4917bca59ce44ef2770086"),
    "c_ID" : ObjectId("5e35f2c840713a43aeeeb3d9"),
    "msg" : "Whats good?",
    "fromID" : "1",
    "__v" : 0,
    "t" : 1582369525,
    "d" : {
        "4" : 1582369525
    },
    "r" : {
        "4" : 1582369525
    }
}

Это содержит само сообщение (msg), пользователь, который его отправил (fromID), отметка времени в UNIX Epoch (t) и подколлекции доставок (d) и reads (r) и, конечно, беседа ObjectID (c_ID).

db.conversations.aggregate([
  {
    $match: {
      "members.uID": "4"
    }
  },
  {
    $addFields: {
      user: {
        $arrayElemAt: [
          {
            $filter: {
              input: "$members",
              as: "member",
              cond: {
                $eq: [
                  "$$member.uID",
                  "4"
                ]
              }
            }
          },
          0
        ]
      }
    }
  },
  {
    $lookup: {
      from: "messages",
      let: {
        user: "$user",
        conversatoinId: "$_id"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $and: [
                {
                  $eq: [
                    "$c_ID",
                    "$$conversatoinId"
                  ]
                },
                {
                  $cond: {
                    if: {
                      $lt: [
                        {
                          $ifNull: [
                            "$$user.l",
                            0
                          ]
                        },
                        "$$user.j"
                      ]
                    },
                    then: true,
                    else: {
                      $lt: [
                        "$t",
                        "$$user.l"
                      ]
                    }
                  }
                },
                {
                  $gt: [
                    "$t",
                    "$$user.j"
                  ]
                }
              ]
            }
          },

        },
        {
          $sort: {
            "t": -1
          },

        }
      ],
      as: "messages"
    }
  },
  {
    $project: {
      lastMessage: {
        $arrayElemAt: [
          "$messages",
          0
        ]
      },
      n: 1,
      members: 1
    }
  },
  {
    $sort: {
      "lastMessage.t": 1
    }
  },
  {
    $project: {
      members: {
        $filter: {
          input: "$members",
          as: "member",
          cond: {
            $and: [
              {
                $ne: [
                  "$$member.uID",
                  "4"
                ]
              },
              {
                $or: [
                  {
                    $eq: [
                      "$$member.l",
                      undefined
                    ]
                  },
                  {
                    $lt: [
                      "$$member.l",
                      "$$member.j"
                    ]
                  }
                ]
              }
            ]
          }
        }
      },
      memberCount: {
        $size: {
          $filter: {
            input: "$members",
            as: "member",
            cond: {
              $and: [
                {
                  $ne: [
                    "$$member.uID",
                    "4"
                  ]
                },
                {
                  $or: [
                    {
                      $eq: [
                        "$$member.l",
                        undefined
                      ]
                    },
                    {
                      $lt: [
                        "$$member.l",
                        "$$member.j"
                      ]
                    }
                  ]
                }
              ]
            }
          }
        }
      },
      n: 1,
      lastMessage: 1
    }
  },
  {
    $match: {
      lastMessage: {
        $exists: true
      }
    }
  },
  {
    $limit: 10
  }
])

Теперь проблема:

Представьте, что существует групповой чат с 20 участниками. Название чата - «Веселые пятницы». userID 4 присоединяется к этому групповому чату (я установил для поля j (join) значение 1582475543 (отметка времени), участвует в течение двух недель, а затем уходит (я установил для поля l (left) значение 1583685143 (отметка времени)). Это все будет работать нормально. Однако, как я могу снова добавить userID 4 в тот же групповой чат через 1 неделю (отметка времени 1584289943) и убедиться, что userID 4 может видеть lastMessage, ЕСЛИ это либо между первым подключением / оставил ИЛИ, если он будет опубликован после того, как он снова присоединился? **

Я хотел бы иметь возможность добавлять одного и того же пользователя более одного раза в массив members, но с разными полями jl) и затем запросите последнее сообщение между одним из них, что позволило бы выполнить то, что я хочу сделать, как описано выше.

Таким образом, я хотел бы получить документ conversations следующим образом:

{
    "_id" : ObjectId("5e35f2c840713a43aeeeb3d9"),
    "n" : "Example Group Chat",
    "members" : [ 
        {
            "uID" : "1",
            "j" : 1580580922,
            "i" : "1",
            "r" : "admin",
            "a" : 1
        }, 
        {
            "uID" : "4",
            "j" : 1580580922,
            "l" : 1581863346,
            "i" : "1",
            "r" : "member",
            "a" : 0
        }, 
        {
            "uID" : "5",
            "j" : 1580581922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "9",
            "j" : 1580593922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "3",
            "j" : 1580594920,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "8",
            "j" : 1580594999,
            "i" : "1",
            "r" : "member",
            "a" : 1
        },
        {
            "uID" : "4",
            "j" : 1581982392,
            "i" : "1",
            "r" : "member",
            "a" : 0
        }, 
    ]
}

{
    "_id" : ObjectId("5e39d5d740713a43aeef5b26"),
    "members" : [ 
        {
            "uID" : "1",
            "j" : 1580580922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "2",
            "j" : 1580580922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }
    ]
}

Итак, userID 4 присоединился, принял участие в чате, ушел и позже присоединился снова. Я хочу, чтобы он мог видеть чат в обзоре всех своих бесед.

Любые идеи о том, как я могу достичь ve this?

РЕДАКТИРОВАТЬ: Основываясь на предложении Джо, я хотел бы go с чем-то вроде ниже. Есть мнения? Мне интересно, как бы я использовал приведенную ниже структуру, чтобы показать только последнее сообщение до последней отметки времени выхода (1581963346), а не любое сообщение, опубликованное после них. Или, если есть несколько дат присоединения / выхода, я хотел бы показать последнее соответствующее сообщение. Позже я использовал эти даты, чтобы показать сообщения, которые пользователь может видеть. Так что только те, кто присутствовал в групповом чате.

{
    "_id" : ObjectId("5e35f2c840713a43aeeeb3d9"),
    "n" : "Example Group Chat",
    "members" : [ 
        {
            "uID" : "1",
            "j" : 1580580922,
            "i" : "1",
            "r" : "admin",
            "a" : 1
        }, 
        {
            "uID" : "4",
            "events" : [
                { type: "join", date: 1581863346 },
                { type: "leave", date: 1581963346 }
            ],
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "5",
            "j" : 1580581922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "9",
            "j" : 1580593922,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }, 
        {
            "uID" : "3",
            "j" : 1580594920,
            "i" : "1",
            "r" : "member",
            "a" : 1
        }
    ]
}

РЕДАКТИРОВАТЬ 2: Мой основной источник беспокойства - это запрос между датой вступления в группу и датой ухода из группы. группа. Я приведу пример ниже.

Диалог X

  • 10: 00 AM: Человек А присоединяется к беседе и пока не видит сообщений
  • 10: 05 AM: человек B отправляет сообщение
  • 10: 07 AM: человек C отправляет сообщение
  • 10: 10 AM: человек A отправляет сообщение
  • 10: 12 AM: человек B отправляет сообщение
  • 10: 15 AM: человек A покидает разговор
  • 10: 20 AM: человек C отправляет сообщение

На данный момент я хочу, чтобы человек А мог видеть беседу Х все еще в своем обзоре с сообщением от человека Б (10:12 утра) в качестве последнего сообщения. Когда я go участвую в разговоре, я хочу, чтобы все сообщения с 10:00 до 10:15 показывались человеку А. Поэтому не должно отображаться только последнее сообщение от пользователя C (10:20). .

  • 10: 30 AM: человек B отправляет сообщение
  • 10: 32 AM: человек A снова присоединяется к беседе
  • 10: 35 AM : Person C отправляет сообщение
  • 10: 37 AM: Person A отправляет сообщение
  • 10: 38 AM: Человек C отправляет сообщение

На этом этапе я хочу, чтобы человек А увидел последнее сообщение от человека C в своем обзоре. В самой беседе я хочу показать сообщения с 10:00 до 10:15, а также с 10:32 до 10:38.

Мне просто интересно, как я могу структурировать свои данные И как запросить обзор (на основе моего запроса выше), чтобы добиться этого. Позже я позабочусь о беседах и покажу все соответствующие сообщения для определенного пользователя.

1 Ответ

1 голос
/ 22 марта 2020

Вы можете попробовать ниже агрегации.

На этапе $lookup введите сообщения, совпадающие с идентификатором разговора, по нескольким временным линиям соединения и оставьте временные шкалы, за которыми следуют $sort по убыванию и $limit, чтобы выбрать обзорное сообщение.

db.conversations.aggregate([
  {"$match":{"members.uID":"4"}},
  {"$lookup":{
    "from":"messages",
    "let":{
      "members":{
         "$filter":{
          "input":"$members",
         "cond":{"$eq":["$$this.uID","4"]}
         }
       },
      "conversation_id":"$_id"
    },
    "pipeline":[
      {"$match":{
        "$expr":{
          "$and":[
            {"$eq":["$c_ID","$$conversation_id"]},
            {"$ne":["$fromID","4"]},
            {"$gt":[
              {"$size":{
                "$filter":{
                  "input":"$$members",
                  "cond":{
                    "$cond":[
                      {"$gt":["$$this.l",null]},
                      {"$and":[
                        {"$gte":["$t","$$this.j"]},
                        {"$lte":["$t","$$this.l"]}
                      ]},
                      {"$gte":["$t","$$this.j"]}
                    ]
                  }
                }
              }},
              0
            ]}
          ]
        }
      }},
      {"$sort":{"t":-1}},
      {"$limit":1}
    ],
    "as":"overviewMessage"
  }}
])

Если вы хотите использовать

events:[{ "join": 1581863346, "left": 1581863348},{ "join": 1581863349, "left": 1581863355}...]

Вы можете использовать следующий запрос

db.conversations.aggregate(
[
  {"$match":{"members.uID":"4"}},
  {"$lookup":{
    "from":"messages",
    "let":{
      "member":{
        "$arrayElemAt":[
          {
           "$filter":{
            "input":"$members",
           "cond":{"$eq":["$$this.uID","4"]}
           }
          },
          0
        ]
      },
      "conversation_id":"$_id"
    },
    "pipeline":[
      {"$match":{
        "$expr":{
          "$and":[
            {"$eq":["$c_ID","$$conversation_id"]},
            {"$ne":["$fromID","4"]},
            {"$gt":[
              {"$size":{
                "$filter":{
                  "input":"$$member.events",
                  "cond":{
                    "$cond":[
                      {"$gt":["$$this.left",null]},
                      {"$and":[
                        {"$gte":["$t","$$this.join"]},
                        {"$lte":["$t","$$this.left"]}
                      ]},
                      {"$gte":["$t","$$this.join"]}
                    ]
                  }
                }
              }},
              0
            ]}
          ]
        }
      }},
      {"$sort":{"t":-1}},
      {"$limit":1}
    ],
    "as":"overviewMessage"
  }}
])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...