MongoDB允许多个加入和离开日期
感谢StackOverflow上的社区,我现在有一个问题 用户可以与多达25人进行一对一对话,以及群组聊天。我数据库背后的想法是保存一个对话文档和一个消息文档,它们使用对话中的_id相互链接。这是我的对话文档:MongoDB允许多个加入和离开日期,mongodb,performance,mongodb-query,Mongodb,Performance,Mongodb Query,感谢StackOverflow上的社区,我现在有一个问题 用户可以与多达25人进行一对一对话,以及群组聊天。我数据库背后的想法是保存一个对话文档和一个消息文档,它们使用对话中的_id相互链接。这是我的对话文档: { "_id" : ObjectId("5e35f2c840713a43aeeeb3d9"), "n" : "Example Group Chat", "members" : [ { "uID" : "1",
{
"_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),一对一聊天不能。每个会话都有一个成员数组,其中存储用户ID(uID)、加入日期(j)、离开日期(l)、邀请用户ID字段(i)、角色字段(r)和活动字段(a)。我可能不需要“active”字段,因为我有一个join/left时间戳,但仍然需要。我以后可能会删除它,所以该字段可能不会被包括在内
接下来,我的消息文档如下:
{
"_id" : ObjectId("5e4917bca59ce44ef2770086"),
"c_ID" : ObjectId("5e35f2c840713a43aeeeb3d9"),
"msg" : "Whats good?",
"fromID" : "1",
"__v" : 0,
"t" : 1582369525,
"d" : {
"4" : 1582369525
},
"r" : {
"4" : 1582369525
}
}
{
"_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
}
]
}
它保存消息本身(msg)、发送它的用户(fromID)、UNIX Epoch中的时间戳(t)以及传递(d)和读取(r)的子集合,当然还有会话对象ID(c_ID)
现在,问题是:
想象一下,有一个20人的集体聊天。聊天的名字是“时髦的星期五”。userID 4加入该群组聊天(我将j
(加入)字段设置为1582475543
(时间戳),参与两周,然后离开(我将l
(左)字段设置为1583685143
(时间戳))。这一切都可以正常工作。但是,我如何在一周后再次将userID 4添加到同一群组聊天中(时间戳1584289943
)并确保userID 4可以看到最后一条消息,无论是在他第一次加入/离开之间还是在他再次加入后发布的**
我希望能够将同一用户多次添加到members数组中,但使用不同的j
(和l
)字段,然后查询其中一个字段之间的最后一条消息,这将允许我按照上述操作
因此,基本上我希望我的对话
文档如下:
{
"_id" : ObjectId("5e4917bca59ce44ef2770086"),
"c_ID" : ObjectId("5e35f2c840713a43aeeeb3d9"),
"msg" : "Whats good?",
"fromID" : "1",
"__v" : 0,
"t" : 1582369525,
"d" : {
"4" : 1582369525
},
"r" : {
"4" : 1582369525
}
}
{
"_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
}
]
}
所以userid4已经加入,参与了聊天,离开了,后来又加入了。我希望他能够在所有对话的概览中看到聊天
有什么办法可以做到这一点吗
编辑:根据Joe的建议,我想使用下面的内容。有什么意见吗?我想知道如何使用下面的结构仅显示上次离开时间戳之前的最新消息(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:我主要关心的是查询某人加入某个组的日期和某人离开某个组的日期。我将在下面给出一个示例。
{
"_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
}
]
}
对话十
- 上午10:00:人员A加入对话,但尚未看到任何消息
- 上午10:05:B个人发布消息
- 上午10:07:C个人发布消息
- 上午10:10:某人A发布消息
- 上午10:12:B个人发布消息
- 上午10:15:A个人离开对话
- 上午10:20:C个人发布消息
此时,我希望人员A仍能在其概览中看到对话X,并将人员B(上午10:12)的消息作为最后一条消息。当我进入对话时,我希望将上午10:00至上午10:15之间的所有消息显示给人员A。因此,仅显示人员C(上午10:20)的最后一条消息不应显示。
{
"_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
}
]
}
- 上午10:30:人员B发布消息
- 上午10:32:人员A再次加入对话
- 上午10:35:C个人发布消息
- 上午10:37:A个人发布消息
- 上午10:38:C个人发布消息
此时,我希望人员A在其概述中看到人员C的最新消息。在对话本身中,我希望显示上午10:00到10:15之间以及上午10:32到10:38之间的消息。
{
"_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
}
]
}
我只是想知道如何组织我的数据,以及如何查询概览(基于我上面的查询)来实现这一点。我将在稍后处理对话并为某个用户显示所有相关消息。您可以尝试下面的聚合
{
"_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
}
]
}
在$lookup
阶段,拉入与会话id匹配的消息,跨越多个加入和离开时间线,然后按$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"
}}
])
我在读这篇文章的时候发现,我把RDBMS概念叫做“左外连接”,混淆了你的字段名<代码>左和<代码>连接< /代码>。为了清楚起见,你可能想考虑改变字段名称(仅针对这篇文章)。如果你实际使用了“活动”,这可能会更简单一些。位,即让j
和l
成为数组,当用户加入时,将新日期推到j
并将a
设置为1,当他们离开时,将新日期推到l
并将a
设置为0,则查询只需匹配{a:1}查看要包含的内容。@Joe您所描述的内容正是我在添加活动字段时所想的。但是,我不确定是否希望联接/左时间戳位于数组中,因为我希望始终保持它们之间的关系。否则很难匹配多个句点,没有?有关于如何设置的示例或想法吗该查询?存储它的最佳方式实际上取决于您打算如何使用数据。与其将它们存储在单独的字段中,不如存储一个事件数组,如[{type:“join”,date:ISODate()},{type:“leave”,date:ISODate()(