Mongodb 删除所有空字段

Mongodb 删除所有空字段,mongodb,null,Mongodb,Null,如何从给定集合的所有文档中删除null的所有字段 我有一系列文件,如: { 'property1': 'value1', 'property2': 'value2', ... } 但是每个文档可能有一个null条目,而不是一个值条目 我想通过删除所有null条目来删除。在我的例子中,null条目的存在不包含任何信息,因为我事先知道JSON文档的格式。//在mongo shell中运行 var coll=db.getCollection(“collectionName”

如何从给定集合的所有文档中删除
null
的所有字段


我有一系列文件,如:

{
    'property1': 'value1',
    'property2': 'value2',
    ...
}
但是每个文档可能有一个
null
条目,而不是一个值条目

我想通过删除所有
null
条目来删除。在我的例子中,
null
条目的存在不包含任何信息,因为我事先知道JSON文档的格式。

//在mongo shell中运行
var coll=db.getCollection(“collectionName”);
var cursor=coll.find();
while(cursor.hasNext()){
var doc=cursor.next();
var键={};
var hasNull=false;
用于(单据中的var x){
如果(x!=“_id”&&doc[x]==null){
键[x]=1;
hasNull=true;
}
}
if(hasNull){
coll.update({u id:doc.{u id},{$unset:keys});
}
}

像上面提到的这个问题():

不幸的是,MongoDB不支持任何查询具有特定值的所有字段的方法

因此,您可以迭代文档(如向导的示例)或以非mongodb的方式进行

如果这是一个JSON文件,请删除sed中
null
的所有行:

sed '/null/d' ./mydata.json

您可以使用mongoupdateMany功能,但必须指定要更新的参数,例如
year
参数:

db.collection.updateMany({year: null}, { $unset : { year : 1 }})

这是一个重要的问题,因为mongodb无法索引空值(即不要查询空值,否则您将等待很长时间),所以最好完全避免空值,并使用
setOnInsert
设置默认值

下面是一个删除空值的递归解决方案:

/**
 * RETRIEVES A LIST OF ALL THE KEYS IN A DOCUMENT, WHERE THE VALUE IS 'NULL' OR 'UNDEFINED'
 *
 * @param doc
 * @param keyName
 * @param nullKeys
 */
function getNullKeysRecursively(doc, keyName, nullKeys)
{
    for (var item_property in doc)
    {
        // SKIP BASE-CLASS STUFF
        if (!doc.hasOwnProperty(item_property))
            continue;
        // SKIP ID FIELD
        if (item_property === "_id")
            continue;

        // FULL KEY NAME (FOR SUB-DOCUMENTS)
        var fullKeyName;
        if (keyName)
            fullKeyName = keyName + "." + item_property;
        else
            fullKeyName = item_property;

        // DEBUGGING
        // print("fullKeyName: " + fullKeyName);

        // NULL FIELDS - MODIFY THIS BLOCK TO ADD CONSTRAINTS
        if (doc[item_property] === null || doc[item_property] === undefined)
            nullKeys[fullKeyName] = 1;

        // RECURSE OBJECTS / ARRAYS
        else if (doc[item_property] instanceof Object || doc[item_property] instanceof Array)
            getNullKeysRecursively(doc[item_property], fullKeyName, nullKeys);
    }
}

/**
 * REMOVES ALL PROPERTIES WITH A VALUE OF 'NULL' OR 'UNDEFINED'.
 * TUNE THE 'LIMIT' VARIABLE TO YOUR MEMORY AVAILABILITY.
 * ONLY CLEANS DOCUMENTS THAT REQUIRE CLEANING, FOR EFFICIENCY.
 * USES bulkWrite FOR EFFICIENCY.
 *
 * @param collectionName
 */
function removeNulls(collectionName)
{
    var coll = db.getCollection(collectionName);
    var lastId = ObjectId("000000000000000000000000");
    var LIMIT = 10000;
    while (true)
    {
        // GET THE NEXT PAGE OF DOCUMENTS
        var page = coll.find({ _id: { $gt: lastId } }).limit(LIMIT);
        if (! page.hasNext())
            break;

        // BUILD BULK OPERATION
        var arrBulkOps = [];
        page.forEach(function(item_doc)
        {
            lastId = item_doc._id;

            var nullKeys = {};
            getNullKeysRecursively(item_doc, null, nullKeys);

            // ONLY UPDATE MODIFIED DOCUMENTS
            if (Object.keys(nullKeys).length > 0)
            // UNSET INDIVIDUAL FIELDS, RATHER THAN REWRITE THE ENTIRE DOC
                arrBulkOps.push(
                    { updateOne: {
                            "filter": { _id: item_doc._id },
                            "update": { $unset: nullKeys }
                        } }
                );
        });

        coll.bulkWrite(arrBulkOps, { ordered: false } );
    }
}

// GO GO GO
removeNulls('my_collection');
在以下日期之前提交文件:

{
    "_id": ObjectId("5a53ed8f6f7c4d95579cb87c"),
    "first_name": null,
    "last_name": "smith",
    "features": {
        "first": {
            "a": 1,
            "b": 2,
            "c": null
        },
        "second": null,
        "third" : {},
        "fourth" : []
    },
    "other": [ 
        null, 
        123, 
        {
            "a": 1,
            "b": "hey",
            "c": null
        }
    ]
}
在下列日期后提交文件:

{
    "_id" : ObjectId("5a53ed8f6f7c4d95579cb87c"),
    "last_name" : "smith",
    "features" : {
        "first" : {
            "a" : 1,
            "b" : 2
        }
    },
    "other" : [ 
        null, 
        123, 
        {
            "a" : 1,
            "b" : "hey"
        }
    ]
}
如您所见,它删除了
null
未定义的
、空对象和空数组。如果需要增加/减少攻击性,则需要修改块“空字段-修改此块以添加约束”


欢迎编辑,特别是@stennie

开始
Mongo 4.2
,可以接受聚合管道,最终允许基于其值删除字段:

// { _id: ObjectId("5d0e8...d2"), property1: "value1", property2: "value2" }
// { _id: ObjectId("5d0e8...d3"), property1: "value1", property2: null, property3: "value3" }
db.collection.update(
  {},
  [{ $replaceWith: {
    $arrayToObject: {
      $filter: {
        input: { $objectToArray: "$$ROOT" },
        as: "item",
        cond: { $ne: ["$$item.v", null] }
      }
    }
  }}],
  { multi: true }
)
// { _id: ObjectId("5d0e8...d2"), property1: "value1", property2: "value2" }
// { _id: ObjectId("5d0e8...d3"), property1: "value1", property3: "value3" }
详情如下:

  • 第一部分是匹配查询,过滤要更新的文档(在本例中是所有文档)

  • 第二部分是更新聚合管道(注意方括号表示使用聚合管道):

    • 使用,我们首先将文档转换为键/值数组,例如
      [{k:“property1”,v:“value1”},{k:“property2”,v:null},…]
    • 使用,我们通过删除
      v
      null
      的项来过滤这个键值数组
    • 然后,我们使用将经过过滤的键值数组转换回一个对象
    • 最后,我们用修改后的文档替换整个文档
  • 不要忘记
    {multi:true}
    ,否则只会更新第一个匹配的文档


此答案很有用,但不会取消子文档空成员的设置