英文:
aggregation with insertion of data from one collection into another
问题
我正在尝试完成以下操作:从特定用户的chat
集合中获取聊天列表,并为每个聊天添加来自message
集合中发送的最后一条消息。
目前有两种方法,它们的描述如下:
首先,我使用聊天成员的ID获取聊天列表,第二种方法使用聚合来查找每个聊天的最后一条消息,然后将消息与聊天的ID进行匹配。
chat
集合:
type Chat struct {
ID string `json:"id" bson:"id"`
Participants []string `json:"participants" bson:"participants"`
LastMessage *Message `json:"last_message,omitempty" bson:"last_message"`
...
}
P.S.
LastMessage
始终为nil,我只需要它来组合用户的响应。
message
集合:
type Message struct {
ID string `json:"id" bson:"id"`
ChatID string `json:"chat_id" bson:"chat_id"`
FromID string `json:"from_id" bson:"from_id"`
CreateDate int64 `json:"create_date" bson:"create_date"`
Body string `json:"body" bson:"body"`
UpdateAt int64 `json:"update_at" bson:"update_at"`
...
}
第一种方法: 我需要使用此方法获取特定聊天参与者的活跃聊天列表。
func ActiveChats(ctx context.Context, uid string) ([]*Chat, error) {
...
filter := bson.D{primitive.E{Key: "participants", Value: uid}}
cursor, err := r.col.Find(ctx, filter, nil)
if err != nil {...}
var ch []*chat
if err = cursor.All(ctx, &ch); err != nil {...}
if err = cursor.Close(ctx); err != nil {...}
...
}
第二种方法: 我需要使用此方法获取每个聊天的最后一条消息,输入是一个聊天ID的数组,对于每个聊天,我搜索最后一条消息(如果有)。为此,我使用了聚合。
func LastMessages(ctx context.Context, chatIds []string) (map[string]*Message, error) {
matchStage := bson.D{
primitive.E{
Key: "$match",
Value: bson.D{primitive.E{Key: "chat_id", Value: bson.D{primitive.E{Key: "$in", Value: chatIds}}}},
},
}
sortStage := bson.D{primitive.E{Key: "$sort", Value: bson.D{primitive.E{Key: "created", Value: -1}}}}
groupStage := bson.D{
primitive.E{
Key: "$group",
Value: bson.D{
primitive.E{Key: "_id", Value: bson.D{primitive.E{Key: "chat_id", Value: "$chat_id"}}},
primitive.E{Key: "message", Value: bson.D{primitive.E{Key: "$first", Value: "$$ROOT"}}},
},
},
}
cursor, err := r.colMessage.Aggregate(ctx, mongo.Pipeline{matchStage, groupStage, sortStage})
if err != nil {...}
var res []*aggregationResultGenerated
if err = cursor.All(ctx, &res); err != nil {...}
...
}
我知道这是一个很糟糕的解决方案,但这是我目前能想到的唯一办法,非常遗憾(不起作用)。我尝试修复这个问题。
db.chat.aggregate([
{
$match: {
participants: "participant_id",
},
},
{
$lookup: {
from: "message", // 其他表名
localField: "id", // 聊天表字段的名称
foreignField: "chat_id", // 消息表字段的名称
as: "msg",
},
},
{
$unwind: "$msg",
},
{
$match: {
chat_id: {
$in: ["$$ROOT._id"],
},
},
},
{
$sort: {
"created": -1,
},
},
{
$group: {
"_id": {
"chat_id": "$chat_id",
},
"doc": {
"$last": "$$ROOT",
},
},
},
{
$project: {
last_message: "$msg",
},
},
])
我的问题是:如何使用聚合来获取特定用户的聊天列表,并为每个聊天在chat
对象的last_message
字段中添加来自message
集合的最后一条消息?
目前的工作方式:
{
"chats": [
{
"id": "4hWsHam3ZZpoyIw44q3D",
"title": "Chat example",
"create-date": 1674476855918,
"participants": [
"63ce54460aeee5e72c778d90",
"63ce54460aeee5e72c778d92"
],
"owner_id": "63ce54460aeee5e72c778d90",
"last_message": {
"id": "tzwekCiCLSXJ4tfdQuHH",
"chat_id": "4hWsHam3ZZpoyIw44q3D",
"from_id": "63ce54460aeee5e72c778d92",
"create_date": 1674557062031,
"body": "text",
"update_at": 0,
"viewed": false
},
"unread": 5
},
{
"id": "Anjrr9RCWFzq030Cwz7S",
"title": "New chat One",
"create-date": 1674476909054,
"participants": [
"63ce54460aeee5e72c778d90",
"63ce54460aeee5e72c778d96"
],
"owner_id": "63ce54460aeee5e72c778d90",
"last_message": {
"id": "7YqhhS1-EfMRSZtGCH0Z",
"chat_id": "Anjrr9RCWFzq030Cwz7S",
"from_id": "63ce54460aeee5e72c778d96",
"create_date": 1674575017115,
"body": "text",
"update_at": 0
},
"unread": 1
}
]
}
英文:
I'm trying to do the following, get a list of chats from the chat
collection of a particular user, and add to that list for each chat the last message from the message
collection that was sent.
How this works now, I have two methods, they are described below
First I just get a list of chats using the chat member id, and the second method uses aggregation to look for the last message for each chat, then I just match the messages with the id of the chat
collection chat:
type Chat struct {
ID string `json:"id" bson:"id"`
Participants []string `json:"participants" bson:"participants"`
LastMessage *Message `json:"last_message,omitempty" bson:"last_message"`
...
}
P.S.
LastMessage
- is always nil, I only need it to compose the response for the user.
Collection message
:
type Message struct {
ID string `json:"id" bson:"id"`
ChatID string `json:"chat_id" bson:"chat_id"`
FromID string `json:"from_id" bson:"from_id"`
CreateDate int64 `json:"create_date" bson:"create_date"`
Body string `json:"body" bson:"body"`
UpdateAt int64 `json:"update_at" bson:"update_at"`
...
}
First method: I need this method to get a list of active chats of a particular chat participant.
func ActiveChats(ctx context.Context, uid string) ([]*Chat, error) {
...
filter := bson.D{primitive.E{Key: "participants", Value: uid}}
cursor, err := r.col.Find(ctx, filter, nil)
if err != nil {...}
var ch []*chat
if err = cursor.All(ctx, &ch); err != nil {...}
if err = cursor.Close(ctx); err != nil {...}
...
}
Second method: I need this method to get the last message for each chat, the input is an array of chat IDs, and for each I search for the last message if there is one. To do this I use aggregation.
func LastMessages(ctx context.Context, chatIds []string) (map[string]*Message, error) {
matchStage := bson.D{
primitive.E{
Key: "$match", Value: bson.D{
primitive.E{
Key: "chat_id", Value: bson.D{
primitive.E{Key: "$in", Value: chatIds},
},
},
},
}}
sortStage := bson.D{primitive.E{Key: "$sort", Value: bson.D{primitive.E{Key: "created", Value: -1}}}}
groupStage := bson.D{primitive.E{
Key: "$group", Value: bson.D{
primitive.E{
Key: "_id", Value: bson.D{
primitive.E{Key: "chat_id", Value: "$chat_id"},
},
},
primitive.E{
Key: "message", Value: bson.D{
primitive.E{Key: "$first", Value: "$$ROOT"},
},
},
},
}}
cursor, err := r.colMessage.Aggregate(ctx, mongo.Pipeline{matchStage, groupStage, sortStage})
if err != nil {...}
var res []*aggregationResultGenerated
if err = cursor.All(ctx, &res); err != nil {...}
...
}
I know it's a very bad solution, but it's all I've been able to come up with so far, much to my regret(not workig). I try fix this
db.chat.aggregate([
{
$match: {
participants: "participant_id",
},
{
$lookup: {
from: "message", // other table name
localField: "id", // name of chat table field
foreignField: "chat_id", // name of message table field
as: "msg",
}
},
{
$unwind: "$msg",
},
{
$match: {
chat_id : {
$in: ["$$ROOT._id"],
},
},
},
{
$sort: {
"created": -1,
},
},
{
$group: {
"_id": {
"chat_id": "$chat_id"
},
"doc": {
"$last": "$$ROOT"
}
}
},
{
$project: {
last_message: "$msg",
}
}
])
My question is: How can I use aggregation to get a list of chats of a particular user, and for each chat add from the collection message
the last message in the field last_message in the object chat
?
How it works now:
{
"chats": [
{
"id": "4hWsHam3ZZpoyIw44q3D",
"title": "Chat example",
"create-date": 1674476855918,
"participants": [
"63ce54460aeee5e72c778d90",
"63ce54460aeee5e72c778d92"
],
"owner_id": "63ce54460aeee5e72c778d90",
"last_message": {
"id": "tzwekCiCLSXJ4tfdQuHH",
"chat_id": "4hWsHam3ZZpoyIw44q3D",
"from_id": "63ce54460aeee5e72c778d92",
"create_date": 1674557062031,
"body": "text",
"update_at": 0,
"viewed": false
},
"unread": 5
},
{
"id": "Anjrr9RCWFzq030Cwz7S",
"title": "New chat One",
"create-date": 1674476909054,
"participants": [
"63ce54460aeee5e72c778d90",
"63ce54460aeee5e72c778d96"
],
"owner_id": "63ce54460aeee5e72c778d90",
"last_message": {
"id": "7YqhhS1-EfMRSZtGCH0Z",
"chat_id": "Anjrr9RCWFzq030Cwz7S",
"from_id": "63ce54460aeee5e72c778d96",
"create_date": 1674575017115,
"body": "text",
"update_at": 0,
},
"unread": 1
},
]
}
答案1
得分: 1
编辑:正如评论中的OP所提到的,不需要更新/$merge
到集合中。
您可以在$lookup
的子管道中简单地进行$sort
+ $limit
的操作。对查找结果进行$unwind
以整理到last_message
字段中。最后,使用$merge
进行更新回chat
集合。
db.chat.aggregate([
{
$match: {
participants: "63ce54460aeee5e72c778d90",
}
},
{
$lookup: {
from: "message",
localField: "id",
foreignField: "chat_id",
pipeline: [
{
$sort: {
created: -1
}
},
{
$limit: 1
}
],
as: "last_message",
}
},
{
$unwind: {
path: "$last_message",
preserveNullAndEmptyArrays: true
}
},
{
$project: {
last_message: "$last_message"
}
}
])
这是一个旧的Mongo Playground,其中使用$merge
更新到一个集合中。
英文:
Edit: As mentioned by OP in the comment, update/$merge
to collection is not necessary.
You can simply do a $sort
+ $limit
approach in the sub-pipeline of a $lookup
. Do a $unwind
to wrangle the lookup result to the last_message
field. Finally, do a $merge
to update back to the chat
collection.
db.chat.aggregate([
{
$match: {
participants: "63ce54460aeee5e72c778d90",
}
},
{
$lookup: {
from: "message",
localField: "id",
foreignField: "chat_id",
pipeline: [
{
$sort: {
created: -1
}
},
{
$limit: 1
}
],
as: "last_message",
}
},
{
$unwind: {
path: "$last_message",
preserveNullAndEmptyArrays: true
}
},
{
$project: {
last_message: "$last_message"
}
}
])
Here is an old Mongo Playground with $merge
to update to a collection.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论