簡體   English   中英

基於單個用戶對從 MongoDB 集合中刪除記錄

[英]Remove Records from MongoDB Collection based on the Individual User Pairs

我在 MongoDB 集合中有一組文檔(消息),如下所示。 我只想為單個用戶對保留最新的 500 條記錄。 用戶被標識為sentBysentTo

/* 1 */
{
    "_id" : ObjectId("5f1c1b00c62e9b9aafbe1d6c"),
    "sentAt" : ISODate("2020-07-25T11:44:00.004Z"),
    "readAt" : ISODate("1970-01-01T00:00:00.000Z"),
    "msgBody" : "dummy text",
    "msgType" : "text",
    "sentBy" : ObjectId("54d6732319f899c704b21ef7"),
    "sentTo" : ObjectId("54d6732319f899c704b21ef5"),
}

/* 2 */
{
    "_id" : ObjectId("5f1c1b3cc62e9b9aafbe1d6d"),
    "sentAt" : ISODate("2020-07-25T11:45:00.003Z"),
    "readAt" : ISODate("1970-01-01T00:00:00.000Z"),
    "msgBody" : "dummy text",
    "msgType" : "text",
    "sentBy" : ObjectId("54d6732319f899c704b21ef9"),
    "sentTo" : ObjectId("54d6732319f899c704b21ef8"),
}

/* 3 */
{
    "_id" : ObjectId("5f1c1b78c62e9b9aafbe1d6e"),
    "sentAt" : ISODate("2020-07-25T11:46:00.003Z"),
    "readAt" : ISODate("1970-01-01T00:00:00.000Z"),
    "msgBody" : "dummy text",
    "msgType" : "text",
    "sentBy" : ObjectId("54d6732319f899c704b21ef6"),
    "sentTo" : ObjectId("54d6732319f899c704b21ef8"),
}

/* 4 */
{
    "_id" : ObjectId("5f1c1c2e1449dd9bbef28575"),
    "sentAt" : ISODate("2020-07-25T11:49:02.012Z"),
    "readAt" : ISODate("1970-01-01T00:00:00.000Z"),
    "msgBody" : "dummy text",
    "msgType" : "text",
    "sentBy" : ObjectId("54cfcf93e2b8994c25077924"),
    "sentTo" : ObjectId("54d6732319f899c704b21ef5"),
}

/* and soon... assume it to be 10k+ */

我想到的算法是-

  • 首先基於 OR 運算符進行分組
  • 及時對記錄進行降序排序
  • 將其限制為 500
  • 獲取應該保留的_id數組
  • 使用$nin條件將 ID 傳遞給新的 mongo 查詢.deleteMany()

請幫助我在這方面掙扎了很多,但沒有取得任何成功。 非常感謝:)

根據規模,我將執行以下兩項之一:

  1. 假設規模有點低,你實際上可以在合理的時間內對整個集合進行分組,我會做一些類似於你建議的事情:
db.collection.aggregate([
    {
        $sort: {
            sentAt: 1
        }
    },
    {
        $group: {
            _id: {
                $cond: [
                    {$gt: ["$sentBy", "$sentTo"]},
                    ["$sendBy", "$sentTo"],
                    ["$sentTo", "$sendBy"],
                ]
            },
            roots: {$push: "$$ROOT"}
        }
    },
    {
        $project: {
            roots: {$slice: ["$roots", -500]}
        }
    },
    {
        $unwind: "$roots"
    },
    {
        $replaceRoot: {
            newRoot: "$roots"
        }
    },
    {
        $out: "this_collection"
    }
])

排序階段必須首先出現,因為您無法對內部數組后組進行排序,組階段中的$cond模擬了不能在那里使用的$or運算符邏輯。 最后,而不是使用deleteMany$nin來檢索結果,您可以使用$out來重寫當前集合。

  1. 如果規模太大而無法支持這一點,那么您應該逐個用戶迭代並首先執行您的建議,這是一個簡單的示例:

let userIds = await db.collection.distinct("sentBy");

let done = [1];
for (let i = 0; i < userIds.length; i++) {
    
    let matches = await db.collection.aggregate([
        {
            $match: {
                $and: [
                    {
                        $or: [
                            {
                                "sentTo": userIds[i]
                            },
                            {
                                "sendBy": userIds[i]
                            }
                        ]
                    },
                    {  // this is not necessary it's just to avoid running on ZxY and YxZ 
                        $or: [
                            {
                                sendTo: {$nin: done}
                            },
                            {
                                sendBy: {$nin: done}
                            }
                        ]   
                    }
                ]
            }
        },
        {
            $sort: {
                sentAt: 1
            }
        },
        {
            $group: {
                _id: {
                    $cond: [
                        {$eq: ["$sentBy", userIds[i]]},
                        "$sendTo",
                        "$sentBy"
                    ]
                },
                roots: {$push: "$$ROOT"}
            }
        },
        {
            $project: {
                roots: {$slice: ["$roots", -500]}
            }
        },
        {
            $unwind: "$roots"
        },
        {
            $group: {
                _id: null,
                keepers: {$push: "$roots._id"}
            }
        }
    ]).toArray();
    
    if (matches.length) {
        await db.collection.deleteMany(
            {
                $and: [
                    {
                        $or: [
                            {
                                "sentTo": userIds[i]
                            },
                            {
                                "sendBy": userIds[i]
                            }
                        ]
                    },
                    {  // this is only necessary if you used it above.
                        $or: [
                            {
                                sendTo: {$nin: done}
                            },
                            {
                                sendBy: {$nin: done}
                            }
                        ]
                    },
                    {
                        _id: {$nin: matches[0].keepers}
                    }
                ]
            }
        )
    }
    
    done.push(userIds[i])
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM