将一个集合中的数据插入到另一个集合中的聚合操作

huangapple go评论72阅读模式
英文:

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

这是一个旧的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"
    }
  }
])

Mongo Playground

Here is an old Mongo Playground with $merge to update to a collection.

huangapple
  • 本文由 发表于 2023年1月25日 04:48:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75227143.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定