英文:
Sampling data from two collections with multiple array matching MongoDB
问题
我将为您翻译以下内容:
我正在尝试通过聊天ID和用户ID查找所有未读消息。未读消息是指chats_users.max_read_date
小于message.create_date
且message.from_id
不等于用户的消息。
目前的工作方式是,我有一个聊天ID的数组,然后通过chat_id
和user_id
搜索所有chats_users
文档,然后循环搜索消息。
我想将所有内容重写为一个查询,但是遇到了一些问题。我的问题是什么,如何找到所有chats_users
通过聊天ID和用户ID,然后找到所有由非用户编写的消息,并且创建日期message.create_date
大于chats_users.max_read_date
。
如何设置条件,以便对于每个聊天,找到其chats_users,然后仅选择那些不是由发出请求的用户和所有其他聊天者编写的消息,并且消息的创建日期message.create_date
大于chats_users.max_read_date
。
消息集合
type ChatsUsers struct {
ID string `json:"id" bson:"id"`
ChatID string `json:"chat_id" bson:"chat_id"`
UserID string `json:"user_id" bson:"user_id"`
MaxReadDate int64 `json:"max_read_date" bson:"max_read_date"`
}
ChatsUsers集合
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"`
}
将查询重写为单个聚合,查询的目的是找到所有未读且非用户编写的消息,uid变量是查找此类消息的用户
func (r *Mongo) UnreadMessageCount(ctx context.Context, chats []*Chat, uid string) (map[string]int64, error) {
match := bson.A{}
for _, chat := range chats {
match = append(match, chat.ID)
}
chatsUsersList := make([]*domain.ChatsUsers, 0)
for _, ch := range chats {
chu, err := r.FindChatUser(ctx, ch.ID, uid)
if err != nil {
l.Error().Err(err).Msg("failed to find chat user")
return nil, err
}
chatsUsersList = append(chatsUsersList, chu)
}
list := make([]*domain.Message, 0)
for _, ch := range chats {
for _, chu := range chatsUsersList {
if chu.ChatID == ch.ID {
filter := bson.D{
{Key: "chat_id", Value: ch.ID},
{Key: "$and",
Value: bson.A{
bson.D{
{
Key: "create_date", Value: bson.D{bson.D{
{Key: "$gt", Value: chu.MaxReadDate}}}},
{Key: "from_id", Value: bson.M{"$ne": uid}},
},
},
},
}
cursor, err := r.colMessage.Find(ctx, filter)
....
var res []*domain.Message
....
}
}
}
messages := make(map[string]int64)
for _, msg := range list {
messages[msg.ChatID]++
}
return messages, nil
}
我的尝试(抱歉,但这是一个非常复杂的查询,但我仍在尝试编写正确的查询)
db.message.aggregate([
{
$match: {
$expr: {
$and: [
{
chat_id: {
$in: ["ad0a3405-1a16-48f9-93e6-51b17a7283e2"]
}
},
{
from_id: {
$ne: "63f5002735bb916dab3f2b1d"
}
},
{
$gt: ["$create_date", { $max: "$chats_users.max_read_date" }]
}
]
}
}
},
{
$lookup: {
from: "chatsusers",
localField: "chat_id",
foreignField: "chat_id",
as: "chats_users"
}
},
{
$unwind: "$chats_users"
},
{
$group: {
_id: "$chat_id",
messages: {
$push: {
id: "$id",
chat_id: "$chat_id",
from_id: "$from_id",
create_date: "$create_date",
type: "$type",
media: "$media",
body: "$body",
update_at: "$update_at",
modifications: "$modifications",
viewed: "$viewed"
}
},
max_read_date: { $max: "$chats_users.max_read_date" }
}
}
]);
预计响应中只会得到测试数据中的四个对象
{
"_id": {
"$oid": "63f502d3c1be2b78aaa6152e"
},
"body": "полковник конь тобі копитом в грудь! 3",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2",
"create_date": 1677001427977,
"from_id": "63f5002735bb916dab3f2b1d5g",
"id": "G30uZ1HvlYQCZVwRRTBi",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": false
},
{
"_id": {
"$oid": "63f502d2c1be2b78aaa6152d"
},
"body": "полковник конь тобі копитом в грудь! 2",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2",
"create_date": 1677001428101,
"from_id": "63f5002735bb916dab3f2b1d5g",
"id": "RzrfvZuLWyhrKOHEx5gK",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": false
},
{
"_id": {"$oid": "63f5002735bb916dab3f2bb2"},
"body": "after you",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2-y7y3r2",
"create_date": 1677001427398,
"from_id": "63f5002735bb916dab3f2b23",
"id": "e5189d64-0c43-41dd-befa-f554d09475cf",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": true
},
{
"_id": {"$oid": "63f5002735bb916dab3f2bb1"},
"body": "I'm not religious",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2-y7y3r2",
"create_date": 1677001427327,
"from_id": "63f5002735bb916dab3f2b23",
"id": "a1fbc6b0-5f8d-4fa2-aef7-807314dfba0d",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": true
}
我的第二次尝试
db.chats_users.aggregate([
{
$match: {
user_id: "63f5002735bb916dab3f2b1d",
chat_id: { $in: ["ad0a3405-1a16-48f9-93e6-51b17a7283e2"] },
}
},
{
$lookup: {
from: "message",
let: { chat_id: "$chat_id", max_read_date: "$max_read_date" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$chat_id", "$$chat_id"] },
{ $ne: ["$from_id", "63f5002735bb916dab3f2b1d"] },
{ $gt: ["$create_date", "$$max_read_date"] },
]
}
}
}
],
as: "messages"
}
},
{
$project: {
chat_id: 1,
message_count: { $size: "$messages" }
}
}
])
英文:
I'm trying to find all unread messages, by chat ID, and user ID. Unread message is if chats_users.max_read_date
is less than message.create_date
and message.from_id
is not equal to user.
How it works now. I have an array of chat IDs, and I search all chats_users documents by chat_id
and user_id
and then just loop search for messages.
I'd like to rewrite it all into one query, but I'm running into a few problems. What is my problem, how do I find all chats_users
by chat ID and user ID to then find all messages that are written by a non-user, and the creation date message.create_date
is greater than chats_users.max_read_date
How to make a condition so that for each chat, find its chats_users and then select only those messages which are written not by the user who makes the request and all other chatters, and the creation date of the message message.create_date
is greater than chats_users.max_read_date
Message collection
type ChatsUsers struct {
ID string `json:"id" bson:"id"`
ChatID string `json:"chat_id" bson:"chat_id"`
// user ID, which corresponds to the user ID in the system.
UserID string `json:"user_id" bson:"user_id"`
// MaxReadDate time of the last message read
MaxReadDate int64 `json:"max_read_date" bson:"max_read_date"`
}
ChatsUsers collection
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"`
}
Rewrite the query to get a single aggregation, the purpose of the query is to find all messages, by all chats that are unread and unscripted by a non-user, the uid variable is the user looking for such messages
fun (r *Mongo) UnreadMessageCount(ctx context.Context, chats []*Chat, uid string) (map[string]int64, error) {
match := bson.A{}
for _, chat := range chats {
match = append(match, chat.ID)
}
chatsUsersList := make([]*domain.ChatsUsers, 0)
for _, ch := range chats {
chu, err := r.FindChatUser(ctx, ch.ID, uid)
if err != nil {
l.Error().Err(err).Msg("failed to find chat user")
return nil, err
}
chatsUsersList = append(chatsUsersList, chu)
}
list := make([]*domain.Message, 0)
for _, ch := range chats {
for _, chu := range chatsUsersList {
if chu.ChatID == ch.ID {
filter := bson.D{
// search for messages by active chat IDs
{ Key: "chat_id", Value: ch.ID},
// add filtering: that the message has not yet been read,
// and that the messages we select are not written by the current user.
{Key: "$and",
Value: bson.A{
bson.D{
{
Key: "create_date", Value: bson.D{ bson.D{
// $gt Matches values that are greater than the specified value.
{ Key: "$gt", Value: chu.MaxReadDate}}
},
// $ne Matches all values that are not equal to the specified value.
// https://www.mongodb.com/docs/manual/reference/operator/query-comparison/
{Key: "from_id", Value: bson.M{"$ne": uid}}
},
},
},
}
cursor, err := r.colMessage.Find(ctx, filter)
....
var res []*domain.Message
....
}
}
}
// it doesn't make sense to use an array of messages, we need to create a map,
// which will have the chat ID and the number of unread messages in it.
messages := make(map[string]int64)
for _, msg := range list {
messages[msg.ChatID]++
}
return messages, nil
}
My try (sorry, but this is a very crooked query, but I'm still trying to write the right query)
db.message.aggregate([
{
$match: {
$expr: {
$and: [
{
chat_id: {
$in: ["ad0a3405-1a16-48f9-93e6-51b17a7283e2"]
}
},
{
from_id: {
$ne: "63f5002735bb916dab3f2b1d"
}
},
{
$gt: ["$create_date", { $max: "$chats_users.max_read_date" }]
}
]
}
}
},
{
$lookup: {
from: "chatsusers",
localField: "chat_id",
foreignField: "chat_id",
as: "chats_users"
}
},
{
$unwind: "$chats_users"
},
{
$group: {
_id: "$chat_id",
messages: {
$push: {
id: "$id",
chat_id: "$chat_id",
from_id: "$from_id",
create_date: "$create_date",
type: "$type",
media: "$media",
body: "$body",
update_at: "$update_at",
modifications: "$modifications",
viewed: "$viewed"
}
},
max_read_date: { $max: "$chats_users.max_read_date" }
}
}
]);
In the response I expect to get only four objects from the test data
{
"_id": {
"$oid": "63f502d3c1be2b78aaa6152e"
},
"body": "полковник конь тобі копитом в грудь! 3",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2",
"create_date": 1677001427977,
"from_id": "63f5002735bb916dab3f2b1d5g",
"id": "G30uZ1HvlYQCZVwRRTBi",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": false
},
{
"_id": {
"$oid": "63f502d2c1be2b78aaa6152d"
},
"body": "полковник конь тобі копитом в грудь! 2",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2",
"create_date": 1677001428101,
"from_id": "63f5002735bb916dab3f2b1d5g",
"id": "RzrfvZuLWyhrKOHEx5gK",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": false
},
{
"_id": {"$oid": "63f5002735bb916dab3f2bb2"},
"body": "after you",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2-y7y3r2",
"create_date": 1677001427398,
"from_id": "63f5002735bb916dab3f2b23",
"id": "e5189d64-0c43-41dd-befa-f554d09475cf",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": true
},
{
"_id": {"$oid": "63f5002735bb916dab3f2bb1"},
"body": "I'm not religious",
"chat_id": "ad0a3405-1a16-48f9-93e6-51b17a7283e2-y7y3r2",
"create_date": 1677001427327,
"from_id": "63f5002735bb916dab3f2b23",
"id": "a1fbc6b0-5f8d-4fa2-aef7-807314dfba0d",
"media": "",
"modifications": null,
"type": "text",
"update_at": 0,
"viewed": true
}
And my second try
db.chats_users.aggregate([
{
$match: {
user_id: "63f5002735bb916dab3f2b1d",
chat_id: { $in: ["ad0a3405-1a16-48f9-93e6-51b17a7283e2"] },
}
},
{
$lookup: {
from: "message",
let: { chat_id: "$chat_id", max_read_date: "$max_read_date" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$chat_id", "$$chat_id"] },
{ $ne: ["$from_id", "63f5002735bb916dab3f2b1d"] },
{ $gt: ["$create_date", "$$max_read_date"] },
]
}
}
}
],
as: "messages"
}
},
{
$project: {
chat_id: 1,
message_count: { $size: "$messages" }
}
}
])
答案1
得分: 1
db.chatsusers.aggregate([
{
$match: {
chat_id: {
$in: ["ad0a3405-1a16-48f9-93e6-51b17a7283e2", "22e1e6f1-459a-4ed1-bc8f-c9947492972d"],
},
}
},
{
$lookup: {
from: "message",
localField: "chat_id",
foreignField: "chat_id",
let: { chat_id: "$chat_id", max_read_date: "$max_read_date" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$chat_id", "$$chat_id"] },
{ $ne: ["$from_id", "63f5002735bb916dab3f2b1e"] },
{ $gt: ["$create_date", "$$max_read_date"] },
]
}
},
}
],
as: "messages"
}
},
{
$project: {
chat_id: 1,
message_count: { $size: "$messages" },
}
},
{
$group: {
_id: "$chat_id",
message_count: { $sum: "$message_count" },
},
},
{
$sort: { "message_count": -1 },
},
{
$project: {
_id: 0,
chat_id: "$_id",
message_count: 1,
},
},
])
英文:
It turned out to be simpler, I made a query using the chatsusers
collection - which simplified the query.
db.chatsusers.aggregate([
{
$match: {
chat_id: {
$in: ["ad0a3405-1a16-48f9-93e6-51b17a7283e2", "22e1e6f1-459a-4ed1-bc8f-c9947492972d"],
},
}
},
{
$lookup: {
from: "message",
localField: "chat_id",
foreignField: "chat_id",
let: { chat_id: "$chat_id", max_read_date: "$max_read_date" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: ["$chat_id", "$$chat_id"] },
{ $ne: ["$from_id", "63f5002735bb916dab3f2b1e"] },
{ $gt: ["$create_date", "$$max_read_date"] },
]
}
},
}
],
as: "messages"
}
},
{
$project: {
chat_id: 1,
message_count: { $size: "$messages" },
}
},
{
$group: {
_id: "$chat_id",
message_count: { $sum: "$message_count" },
},
},
{
$sort: { "message_count": -1 },
},
{
$project: {
_id: 0,
chat_id: "$_id",
message_count: 1,
},
},
])
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论