Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/310.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 从GridFS中清除孤立文件_Python_Mongodb_Mongodb Query_Gridfs - Fatal编程技术网

Python 从GridFS中清除孤立文件

Python 从GridFS中清除孤立文件,python,mongodb,mongodb-query,gridfs,Python,Mongodb,Mongodb Query,Gridfs,我有一个引用GridFS文件的集合,通常每个记录1-2个文件。这些集合相当大——父集合中约有705k条记录,790k个GridFS文件。随着时间的推移,出现了许多孤立的GridFS文件—父记录被删除,但引用的文件没有被删除。我现在正试图清除GridFS集合中的孤立文件 类似建议的方法的问题在于,将700k记录组合到一个大的ID列表中会产生内存约为4mb的Python列表,将其传递到fs.files集合上Mongo中的$nin查询中需要花费很长时间。执行相反的操作(获取fs.files中所有ID的

我有一个引用GridFS文件的集合,通常每个记录1-2个文件。这些集合相当大——父集合中约有705k条记录,790k个GridFS文件。随着时间的推移,出现了许多孤立的GridFS文件—父记录被删除,但引用的文件没有被删除。我现在正试图清除GridFS集合中的孤立文件

类似建议的方法的问题在于,将700k记录组合到一个大的ID列表中会产生内存约为4mb的Python列表,将其传递到fs.files集合上Mongo中的$nin查询中需要花费很长时间。执行相反的操作(获取fs.files中所有ID的列表并查询父集合以查看它们是否存在)也需要花费很长时间


有人提出过这个问题,并提出了一个更快的解决方案吗?

< P>首先,让我们花些时间来考虑<强>实际上< <强> >是什么。首先,让我们阅读参考的手册页面:

GridFS是一种用于存储和检索超过16MB BSON文档的文件的规范

因此,有了它,这很可能就是您的用例。但这里要吸取的教训是,存储文件的“转到”方法不是自动的

在您(和其他人)的案例中发生的事情是因为“驱动程序级别”规范(MongoDB本身也有no魔力),您的“文件”被“拆分”到两个集合中。一个集合用于内容的主要引用,另一个集合用于数据的“块”

您(和其他人)的问题是,既然“主”引用已被删除,您就已经设法留下了“块”。那么有了大量的孤儿,如何摆脱孤儿呢

您当前的阅读内容是“循环并比较”,并且由于MongoDB不进行连接,因此没有其他答案。但是有一些事情可以帮上忙

因此,与其运行一个庞大的
$nin
,不如尝试做一些不同的事情来打破这种局面。考虑逆序的工作,例如:

db.fs.chunks.aggregate([
{“$group”:{“\u id”:“$files\u id”},
{“$limit”:5000}
])
因此,您要做的是从所有条目中获取5000个条目的不同的“files\u id”值(即对
fs.files
的引用)。然后,您当然会返回循环,检查
fs.files
是否有匹配的
\u id
。如果找不到内容,则从“块”中删除与“文件id”匹配的文档

但这仅仅是5000,所以保留该集中找到的最后一个id,因为现在您将再次运行相同的聚合语句,但不同:

db.fs.chunks.aggregate([
{“$match”:{“files_id”:{“$gte”:last_id}},
{“$group”:{“\u id”:“$files\u id”},
{“$limit”:5000}
])
因此这是有效的,因为
ObjectId
值正在增加或“不断增加”。因此,所有新的条目总是大于上一个条目。然后,您可以再次循环这些值,并在未找到的位置执行相同的删除操作

这会“永远”吗。嗯,是的。您可以为此雇用员工,但请阅读文档。但总的来说,这是使用两个系列所付出的代价

回到起点。规范是这样设计的,因为它特别想绕过16MB限制。但是如果这不是你的限制,那么首先要问你为什么要使用

MongoDB在给定BSON文档的任何元素中存储“二进制”数据都没有问题。因此,您不需要仅使用
来存储文件。如果你这样做了,那么你的所有更新都将是完全“原子的”,因为它们一次只作用于一个集合中的一个文档

因为有意地将文档分割到不同的集合中,所以若您使用它,那个么您将承受痛苦。因此,如果您需要它,请使用它,但是如果您需要而不是,则只需将
BinData
存储为正常字段,这些问题就会消失


但至少你有一个比把所有东西都载入内存更好的方法。

我想在这个讨论中补充我的一点。根据差异的大小,您可能会发现首先查找文件的标识是合理的,您必须首先保留,然后删除不应保留的块。当您管理大量临时文件时,可能会发生这种情况

在我的例子中,我们每天都有相当数量的临时文件保存到GridFS中。我们目前有大约180k个临时文件和一些非临时文件。当到期指数达到时,我们最终得到大约40万孤儿

在试图查找这些文件时,要知道的有用的一点是ObjectID是基于时间戳的。因此,您可以缩小日期之间的搜索范围,但将范围限定在
\u id
文件\u id

要开始查找文件,我从以下日期开始循环:

var nowDate = new Date();
nowDate.setDate(nowDate.getDate()-1);

var startDate = new Date(nowDate);
startDate.setMonth(startDate.getMonth()-1) // -1 month from now

var endDate = new Date(startDate);
endDate.setDate(startDate.getDate()+1); // -1 month +1 day from now

while(endDate.getTime() <= nowDate.getTime()) {
    // interior further in this answer
}
并收集到文件的可变ID,该ID确实存在于集合
。文件

var found = db.getCollection("collection.files").find({
    _id: {
        $gte: idGTE,
        $lt: idLT
    }
}).map(function(o) { return o._id; });
目前,我在
found
变量中有大约50个ID。现在,为了删除
.chunks
集合中孤立项上的大量内容,我正在循环搜索要删除的100个ID,因为我没有找到任何内容:

var removed = 0;
while (true) {

    // note that you have to search in a IDs range, to not delete all your files ;)
    var idToRemove = db.getCollection("collection.chunks").find({
        files_id: {
            $gte: idGTE, // important!
            $lt: idLT,   // important!
            $nin: found, // `NOT IN` var found
        },
        n: 0 // unique ids. Choosen this against aggregate for speed
    }).limit(100).map(function(o) { return o.files_id; });

    if (idToRemove.length > 0) {

        var result = db.getCollection("collection.chunks").remove({
            files_id: {
                $gte: idGTE, // could be commented
                $lt: idLT,   // could be commented
                $in: idToRemove // `IN` var idToRemove
            }
        });

        removed += result.nRemoved;

    } else {
        break;
    }
}
之后增加日期以接近当前日期:

startDate.setDate(startDate.getDate()+1);
endDate.setDate(endDate.getDate()+1);
有一件事我现在无法解决,那就是移除操作需要相当长的时间。根据
文件\u id
查找和删除块需要每~200个块(100个唯一id)3-5秒
startDate.setDate(startDate.getDate()+1);
endDate.setDate(endDate.getDate()+1);
var startDate = new Date();
startDate.setDate(startDate.getDate()-3) // from -3 days

var endDate = new Date();
endDate.setDate(endDate.getDate()-1); // until yesterday

var idGTE = new ObjectID(startDate.getTime()/1000);
var idLT = new ObjectID(endDate.getTime()/1000);

var found = db.getCollection("collection.files").find({
    _id: {
        $gte: idGTE,
        $lt: idLT
    }
}).map(function(o) { return o._id; });

db.getCollection("collection.chunks").deleteMany({
    files_id: {
        $gte: idGTE,
        $lt: idLT, 
        $nin: found,
    }
}, {
    writeConcern: {
        w: 0 // "fire and forget", allows you to close console.
    }
});
/* 
 * This function will count orphaned chunks grouping them by file_id.
 * This is faster but uses more memory.
 */
function countOrphanedFilesWithDistinct(){
    var start = new Date().getTime();
    var orphanedFiles = [];
    db.documents.chunks.distinct("files_id").forEach(function(id){
        var count = db.documents.files.count({ "_id" : id });
        if(count===0){
            orphanedFiles.push(id);
        }
    });
    var stop = new Date().getTime();
    var time = stop-start;
    print("Found [ "+orphanedFiles.length+" ] orphaned files in: [ "+time+"ms ]");
}

/*
 * This function will delete any orphaned document cunks.
 * This is faster but uses more memory.
 */
function deleteOrphanedFilesWithDistinctOneBulkOp(){
    print("Building bulk delete operation");
    var bulkChunksOp = db.documents.chunks.initializeUnorderedBulkOp();
    db.documents.chunks.distinct("files_id").forEach(function(id){
        var count = db.documents.files.count({ "_id" : id });
        if(count===0){
            bulkChunksOp.find({ "files_id" : id }).remove();
        }
    });
    print("Executing bulk delete...");
    var result = bulkChunksOp.execute();
    print("Num Removed: [ "+result.nRemoved+" ]");        
}