【问题标题】:Remove Records from MongoDB Collection based on the Individual User Pairs根据单个用户对从 MongoDB 集合中删除记录
【发布时间】:2020-07-25 14:08:27
【问题描述】:

我在 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()

请帮助我在这方面苦苦挣扎,但没有取得任何成功。非常感谢:)

【问题讨论】:

    标签: javascript arrays mongodb sorting


    【解决方案1】:

    根据规模,我会执行以下两项操作之一:

    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])
    }
    

    【讨论】:

    • 实际上,我主要关心的是使用 OR 条件进行分组。我喜欢这种方式,您在第一种情况下的管理方式。第二个对我来说是新的,我也会试试这个。非常感谢,汤姆。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-14
    • 2021-02-14
    • 2010-11-02
    • 1970-01-01
    • 2018-03-04
    • 2018-08-08
    • 2020-05-13
    相关资源
    最近更新 更多