如何从MongoDB集合中删除空字符串?

6
我有一个“mongodb集合”,我想从中删除键中的“空字符串”。
从这里开始:
{
    "_id" : ObjectId("56323d975134a77adac312c5"), 
    "year" : "15", 
    "year_comment" : "", 
}
{
    "_id" : ObjectId("56323d975134a77adac312c5"), 
    "year" : "", 
    "year_comment" : "asd", 
}

我想要获得这个结果:
{
    "_id" : ObjectId("56323d975134a77adac312c5"), 
    "year" : "15", 
}
{
    "_id" : ObjectId("56323d975134a77adac312c5"), 
    "year_comment" : "asd", 
}

我该怎么解决它?
3个回答

3

首先需要获取集合中所有键的清单,以这些键为查询依据,使用Bulk API操作进行有序批量更新。更新语句使用$unset运算符来删除字段。

通过Map-Reduce机制可以获得所需键列表并组装查询。以下mapreduce操作将使用所有键作为_id值在单独的集合中填充:

mr = db.runCommand({
    "mapreduce": "my_collection",
    "map" : function() {
        for (var key in this) { emit(key, null); }
    },
    "reduce" : function(key, stuff) { return null; }, 
    "out": "my_collection" + "_keys"
})

要获取所有动态密钥的列表,请在结果集上运行distinct:
db[mr.result].distinct("_id")
// prints ["_id", "year", "year_comment", ...]

根据上面的列表,您可以通过创建一个对象并在循环中设置其属性来组装查询。通常,您的查询将具有以下结构:

var keysList = ["_id", "year", "year_comment"];
var query = keysList.reduce(function(obj, k) {
      var q = {};
      q[k] = "";
      obj["$or"].push(q);
      return obj;
    }, { "$or": [] });
printjson(query); // prints {"$or":[{"_id":""},{"year":""},{"year_comment":""}]} 

您可以使用MongoDB 2.6及以上版本提供的Bulk API来优化上述查询的性能,从而使更新更加高效。总体而言,您应该能够得到以下可用的结果:

var bulk = db.collection.initializeOrderedBulkOp(),
    counter = 0,
    query = {"$or":[{"_id":""},{"year":""},{"year_comment":""}]},
    keysList = ["_id", "year", "year_comment"];


db.collection.find(query).forEach(function(doc){
    var emptyKeys = keysList.filter(function(k) { // use filter to return an array of keys which have empty strings
            return doc[k]==="";
        }),
        update = emptyKeys.reduce(function(obj, k) { // set the update object 
            obj[k] = "";
            return obj;
        }, { });

    bulk.find({ "_id": doc._id }).updateOne({
        "$unset": update // use the $unset operator to remove the fields
    });

    counter++;
    if (counter % 1000 == 0) {
        // Execute per 1000 operations and re-initialize every 1000 update statements
        bulk.execute();
        bulk = db.collection.initializeOrderedBulkOp();
    }
})

1
谢谢回答。 - Ferenc Straub
@FerencStraub 没问题,很高兴能帮忙 :) - chridam

3
请尝试在Mongo shell中执行以下代码片段,该代码可以去除空值或null值的字段。
var result=new Array();
db.getCollection('test').find({}).forEach(function(data)
{
  for(var i in data)
  {
      if(data[i]==null || data[i]=='')
      {
         delete data[i]
      }
  }
  result.push(data)

})

print(tojson(result))

它只删除第一行中的空项,但其他项目仍然保留在原位。我仍在努力找出问题所在。Shell 写入了这个:[object Object],[object Object],[object Object] ... - Ferenc Straub
使用这个函数:print(tojson(result)); 问题解决了。 - Ferenc Straub
@FerencStraub 上述代码片段可以很好地去除结果中的空值字段。 - Rubin Porwal
1
仅供参考,(data[i] == null || data[i] == '') 相当于 (!data[i]),但如果你的数据中有 false 值,那么请小心使用这两种方法;如果有的话,你可以将所有内容都包裹在 if (!(data[i] === false)) {...} 中。这个答案对我有用,但是对于大数据集,这个命令可能会在执行过程中突然停止而没有任何警告。 - tim-phillips
1
我在上面的评论中提到的这个命令在执行过程中停止的问题是由于Robomongo中的一个错误引起的。在mongo shell中运行时,它永远不会停止。 - tim-phillips

3

如果您需要更新单个空白参数或者您更喜欢逐个参数进行更新,您可以使用mongo的updateMany功能:

db.comments.updateMany({year: ""}, { $unset : { year : 1 }})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接