【问题标题】:Mongodb aggregation remove null values from object with nested propertiesMongodb聚合从具有嵌套属性的对象中删除空值
【发布时间】:2022-02-02 14:18:55
【问题描述】:

有没有办法从一个对象中删除所有的 null 或空字符串值?我们有一个聚合,它创建一个具有空字段的对象,如果值为 null,则为空对象。 我们希望做的是删除所有 null 属性和空对象并重新创建对象,以保持数据尽可能小。

例如在下面的对象中,只需要考虑'test'和'more-nested-data',其余的可以去掉


{
    "test": "some",
    "test2": {
    },
    "test3": {
        "some-key": {
        },
        "some-other-key": {
            "more-nested-data": true,
            "more-nested-emtpy": null
        }
    }
}

应该变成:

{
    "test": "some",
    "test3": {
        "some-other-key": {
            "more-nested-data": true
        }
    }
}

我尝试了很多,但我认为通过使用 objectToArray 可以完成一些事情,但我还没有找到解决方案。所需的聚合应该需要递归地(或按定义的级别)删除空属性和空对象。

【问题讨论】:

  • 等待一些答案。

标签: spring mongodb mongodb-query aggregation-framework


【解决方案1】:

听起来unwind 运算符会有所帮助。在https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/ 处查看unwind 运算符

【讨论】:

    【解决方案2】:

    使用 4.4(2021 年 8 月)中可用的 $function 运算符以递归方式执行此操作,正如您注意到的那样。鉴于此输入是问题中提供的稍微扩展的版本:

    var dd = {
        "test": "some",
        "test2": { },
        "test3": {
        "some-key": { },
            "some-other-key": {
            "more-nested-data": true,
                "more-nested-emtpy": null,
                "emptyArr": [],
                "notEmptyArr": [
                    "XXX",
                    null,
                    {"corn":"dog"},
                    {"bad":null},
                    {"other": {zip:null, empty:[], zap:"notNull"}}
                ]
            }
        }
    }
    db.foo.insert(dd);
    

    那么这个管道:

    db.foo.aggregate([
        {$replaceRoot: {newRoot: {$function: {
            body: function(obj) {
                var process = function(holder, spot, value) {
                    var remove_it = false;
                    // test FIRST since [] instanceof Object is true!                                   
                    if(Array.isArray(value)) {
                        // walk BACKWARDS due to potential splice() later                               
                        // that will change the length...                                               
                        for(var jj = value.length - 1; jj >= 0; jj--) {
                            process(value, jj, value[jj]);
                        }
                        if(0 == value.length) {
                            remove_it = true;
                        }
    
                    } else if(value instanceof Object) {
                        walkObj(value);
                        if(0 == Object.keys(value).length) {
                            remove_it = true;
                        }
    
                    } else {
                        if(null == value) {
                            remove_it = true;
                        }
                    }
    
                    if(remove_it) {
                        if(Array.isArray(holder)) {
                            holder.splice(spot,1); // snip out the val                                  
                        } else if(holder instanceof Object) {
                            delete holder[spot];
                        }
                    }
                };
    
                var walkObj = function(obj) {
                    Object.keys(obj).forEach(function(k) {
                        process(obj, k, obj[k]);
                    });
                }
    
                walkObj(obj); // entry point!                                                           
                return obj;
        },
            args: [ "$$CURRENT" ],
            lang: "js"
          }}
        }}
    ]);
    

    产生这个结果:

    {
        "_id" : 0,
        "test" : "some",
        "test3" : {
            "some-other-key" : {
                "more-nested-data" : true,
                "notEmptyArr" : [
                    "XXX",
                    {
                        "corn" : "dog"
                    },
                    {
                        "other" : {
                            "zap" : "notNull"
                        }
                    }
                ]
            }
        }
    }
    

    调试此类复杂函数的一种便捷方法是将它们声明为管道外部的变量并通过它们运行数据以模拟从数据库中输出的文档(对象),例如:

    ff = function(obj) {                                  
                var process = function(holder, spot, value) {
                    var remove_it = false;
                    // test FIRST since [] instanceof Object is true!                                   
                    if(Array.isArray(value)) {
    ...
    printjson(ff(dd));  // use the same doc as above
    

    你可以将print和其他调试辅助工具放入代码中,完成后,你可以删除它们并调用管道处理真实数据,如下所示:

    db.foo.aggregate([
        {$replaceRoot: {newRoot: {$function: {
            body: ff,  // substitute here!
            args: [ "$$CURRENT" ],
            lang: "js"
          }}
        }}
    ]);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-23
      • 2022-08-05
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 2021-08-10
      • 2020-11-11
      • 2021-10-29
      相关资源
      最近更新 更多