如何在深度嵌套的 MongoDB 文档中保存删除内容

发布于 2025-01-13 03:10:33 字数 2997 浏览 0 评论 0原文

我是 MongoDB 新手,我正在使用 MongoDB shell 来执行操作。 我正在努力从所有项目中删除名为 Process 的数组,但似乎我没有正确掌握删除概念。 我们使用的文档是深度嵌套的 - 我们不知道有多少项,也不知道嵌套的级别有多深。
到目前为止我尝试的是使用递归来迭代项目:



    function removeAllProcessFields(docItems)
    {
        if(Array.isArray(docItems))
        {
            docItems.forEach(function(item)
                {
                   print("idItem: "+item._id);
                   if(item.Process == null)
                   {
                      print("Process null");
                   }
                   else
                   {
                      $unset: { Process: ""}
                   }
                   removeAllProcessFields(item.Items);
            })
        }
    }
    
    var docs = db.getCollection('MyCollection').find({})
    docs.forEach(function(doc)
    {
        print("idDoc: "+doc._id);
        removeAllProcessFields(doc.Items);
    })

但是我在正确使用 unset 来保存操作方面遇到困难。
示例文档是:



    {
        "_id": "622226d319517e83e8ed6151",
        "Name": "test1",
        "Description": "",
        "Items": [{
            "_id": "622226d319517e83e8ed614e",
            "Name": "test-item",
            "Description": "",
            "Process": [{
                "Name": "Step1"
            }, {
                "Name": "Step2"
            }],
            "Items": [{
                    "_id": "622226d319517e83e8ed614f",
                    "Name": "test-subItem1",
                    "Description": "",
                    "Process": [{
                        "Name": "StepSub1"
                    }, {
                        "Name": "StepSub2"
                    }, {
                        "Name": "StepSub3"
                    }],
                    "Items": []
                },
                {
                    "_id": "622226d319517e83e8ed6150",
                    "Name": "test-subItem2",
                    "Description": "",
                    "Process": [{
                        "Name": "StepSub4"
                    }, {
                        "Name": "StepSub5"
                    }, {
                        "Name": "StepSub6"
                    }],
                    "Items": []
                }
    
            ]
        }]
    }

我希望实现的目标是:



    {
        "_id": "622226d319517e83e8ed6151",
        "Name": "test1",
        "Description": "",
        "Items": [{
            "_id": "622226d319517e83e8ed614e",
            "Name": "test-item",
            "Description": "",
            "Items": [{
                    "_id": "622226d319517e83e8ed614f",
                    "Name": "test-subItem1",
                    "Description": "",
                    "Items": []
                },
                {
                    "_id": "622226d319517e83e8ed6150",
                    "Name": "test-subItem2",
                    "Description": "",
                    "Items": []
                }
    
            ]
        }]
    }

I am new to MongoDB and I am using MongoDB shell to perform the operations.
I am working to remove the array named Process from all the Items, but it seems that I do not grasp the remove concept correctly.
The documents we use are deeply nested - we do not know how many items there are, or how deep the level of nesting.

What I tried so far is to use recursion to iterate through the items:



    function removeAllProcessFields(docItems)
    {
        if(Array.isArray(docItems))
        {
            docItems.forEach(function(item)
                {
                   print("idItem: "+item._id);
                   if(item.Process == null)
                   {
                      print("Process null");
                   }
                   else
                   {
                      $unset: { Process: ""}
                   }
                   removeAllProcessFields(item.Items);
            })
        }
    }
    
    var docs = db.getCollection('MyCollection').find({})
    docs.forEach(function(doc)
    {
        print("idDoc: "+doc._id);
        removeAllProcessFields(doc.Items);
    })

But I have difficulties on using unset properly to save the operation.

An example document would be:



    {
        "_id": "622226d319517e83e8ed6151",
        "Name": "test1",
        "Description": "",
        "Items": [{
            "_id": "622226d319517e83e8ed614e",
            "Name": "test-item",
            "Description": "",
            "Process": [{
                "Name": "Step1"
            }, {
                "Name": "Step2"
            }],
            "Items": [{
                    "_id": "622226d319517e83e8ed614f",
                    "Name": "test-subItem1",
                    "Description": "",
                    "Process": [{
                        "Name": "StepSub1"
                    }, {
                        "Name": "StepSub2"
                    }, {
                        "Name": "StepSub3"
                    }],
                    "Items": []
                },
                {
                    "_id": "622226d319517e83e8ed6150",
                    "Name": "test-subItem2",
                    "Description": "",
                    "Process": [{
                        "Name": "StepSub4"
                    }, {
                        "Name": "StepSub5"
                    }, {
                        "Name": "StepSub6"
                    }],
                    "Items": []
                }
    
            ]
        }]
    }

What I hope to achieve would be:



    {
        "_id": "622226d319517e83e8ed6151",
        "Name": "test1",
        "Description": "",
        "Items": [{
            "_id": "622226d319517e83e8ed614e",
            "Name": "test-item",
            "Description": "",
            "Items": [{
                    "_id": "622226d319517e83e8ed614f",
                    "Name": "test-subItem1",
                    "Description": "",
                    "Items": []
                },
                {
                    "_id": "622226d319517e83e8ed6150",
                    "Name": "test-subItem2",
                    "Description": "",
                    "Items": []
                }
    
            ]
        }]
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雨落□心尘 2025-01-20 03:10:33

像这样的东西可能使用 $[] 位置运算符:

db.collection.update({},
{
 $unset: {
  "Items.$[].Items.$[].Process": 1,
  "Items.$[].Process": 1
}
})

您只需要在递归中构造它...

playground< /a>

JavaScript 递归函数示例:

 mongos> db.rec.find()
{ "_id" : ObjectId("622a6c46ae295edb276df8e2"), "Items" : [ { "a" : 1 }, { "Items" : [ { "Items" : [ { "Items" : [ ], "Process" : [ 1, 2, 3 ] } ], "Process" : [ 4, 5, 6 ] } ], "Process" : [ ] } ] }

 mongos> db.rec.find().forEach(function(obj){ var id=obj._id,ar=[],z=""; function x(obj){ if(typeof obj.Items != "undefined" ){ obj.Items.forEach(function(k){ if( typeof k.Process !="undefined" ){ z=z+".Items.$[]";ar.push(z.substring(1)+".Process") }; if(typeof k.Items != "undefined"){x(k)}else{}  }) }else{}  };x(obj);ar.forEach(function(del){print( "db.collection.update({_id:ObjectId('"+id+"')},{$unset:{'"+del+"':1}})" );}) })

 db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Process':1}})
 db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Items.$[].Process':1}})
 db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Items.$[].Items.$[].Process':1}})
 mongos> 

解释:

  1. 使用 forEach 循环遍历集合中的所有文档
  2. 定义递归函数 x,它将循环遍历任意数量的嵌套 Items 并确定是否存在 Process 字段并推送到数组ar
  3. 最后循环数组 ar 并构造更新 $unset 查询,在示例中仅出于安全原因打印,但您可以改进为每个文档生成单个查询并执行 unset 查询...

Something like this maybe using the $[] positional operator:

db.collection.update({},
{
 $unset: {
  "Items.$[].Items.$[].Process": 1,
  "Items.$[].Process": 1
}
})

You just need to construct it in the recursion ...

playground

JavaScript recursive function example:

 mongos> db.rec.find()
{ "_id" : ObjectId("622a6c46ae295edb276df8e2"), "Items" : [ { "a" : 1 }, { "Items" : [ { "Items" : [ { "Items" : [ ], "Process" : [ 1, 2, 3 ] } ], "Process" : [ 4, 5, 6 ] } ], "Process" : [ ] } ] }

 mongos> db.rec.find().forEach(function(obj){ var id=obj._id,ar=[],z=""; function x(obj){ if(typeof obj.Items != "undefined" ){ obj.Items.forEach(function(k){ if( typeof k.Process !="undefined" ){ z=z+".Items.$[]";ar.push(z.substring(1)+".Process") }; if(typeof k.Items != "undefined"){x(k)}else{}  }) }else{}  };x(obj);ar.forEach(function(del){print( "db.collection.update({_id:ObjectId('"+id+"')},{$unset:{'"+del+"':1}})" );}) })

 db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Process':1}})
 db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Items.$[].Process':1}})
 db.collection.update({_id:ObjectId('622a6c46ae295edb276df8e2')},{$unset:{'Items.$[].Items.$[].Items.$[].Process':1}})
 mongos> 

Explained:

  1. Loop over all documents in collection with forEach
  2. Define recursive function x that will loop over any number of nested Items and identify if there is Process field and push to array ar
  3. Finally loop over array ar and construct the update $unset query , in the example only printed for safety , but you can improve generating single query per document and executing unset query ...
写下不归期 2025-01-20 03:10:33

假设您在 v>=4.4 上,您可以使用 $merge 的“合并到自身”功能,并定义一个递归函数来扫描集合并通过外科手术删除任何级别的一个或一组字段的层次结构。当处理也是任意分层的 json-schema 数据时,也会出现同样的需求。

下面的解决方案具有额外的逻辑来“标记”进行过任何修改的文档,以便可以从传递给 $merge 的更新集中删除其他。还可以进一步细化,减少一些变量;它是从一个更通用的解决方案中编辑而来的,该解决方案必须检查键值。

db.foo.aggregate([
    {$replaceRoot: {newRoot: {$function: {
        body: function(obj, target) {
            var didSomething = false;

            var process = function(holder, spot, value) {
                // test FIRST since [] instanceof Object is true!                           
                if(Array.isArray(value)) {
                    for(var jj = value.length - 1; jj >= 0; jj--) {
                        process(value, jj, value[jj]);
                    }
                } else if(value instanceof Object) {
                    walkObj(value);
                }
            };

            var walkObj = function(obj) {
                Object.keys(obj).forEach(function(k) {
                    if(target.indexOf(k) > -1) {
                        delete obj[k];
                        didSomething = true;
                    } else {
                        process(obj, k, obj[k]);
                    }
                });
            }

            // ENTRY POINT:      
            if(!Array.isArray(target)) {
                target = [ target ]; // if not array, make it an array
            }
            walkObj(obj);

            if(!didSomething) {
                obj['__didNothing'] = true;
            }

            return obj;
        },

        // Invoke!
        // You can delete multiple fields with an array, e.g.:
        //   ..., ['Process','Description']
        args: [ "$ROOT", 'Process' ],

        lang: "js"
        }}
    }}

    // Only let thru docs WITHOUT the marker:
    ,{$match: {'__didNothing':{$exists:false}} }

    ,{$merge: {
        into: "foo",
        on: [ "_id" ],
        whenMatched: "merge",
        whenNotMatched: "fail"
    }}

]);

Assuming you are on v>=4.4 you can use the "merge onto self" feature of $merge plus defining a recursive function to sweep through the collection and surgically remove one or a list of fields at any level of the hierarchy. The same sort of needs arise when processing json-schema data which is also arbitrarily hierarchical.

The solution below has extra logic to "mark" documents that had any modifications so the others can be removed from the update set passed to $merge. It also can be further refined to reduce some variables; it was edited down from a more general solution that had to examine keys and values.

db.foo.aggregate([
    {$replaceRoot: {newRoot: {$function: {
        body: function(obj, target) {
            var didSomething = false;

            var process = function(holder, spot, value) {
                // test FIRST since [] instanceof Object is true!                           
                if(Array.isArray(value)) {
                    for(var jj = value.length - 1; jj >= 0; jj--) {
                        process(value, jj, value[jj]);
                    }
                } else if(value instanceof Object) {
                    walkObj(value);
                }
            };

            var walkObj = function(obj) {
                Object.keys(obj).forEach(function(k) {
                    if(target.indexOf(k) > -1) {
                        delete obj[k];
                        didSomething = true;
                    } else {
                        process(obj, k, obj[k]);
                    }
                });
            }

            // ENTRY POINT:      
            if(!Array.isArray(target)) {
                target = [ target ]; // if not array, make it an array
            }
            walkObj(obj);

            if(!didSomething) {
                obj['__didNothing'] = true;
            }

            return obj;
        },

        // Invoke!
        // You can delete multiple fields with an array, e.g.:
        //   ..., ['Process','Description']
        args: [ "$ROOT", 'Process' ],

        lang: "js"
        }}
    }}

    // Only let thru docs WITHOUT the marker:
    ,{$match: {'__didNothing':{$exists:false}} }

    ,{$merge: {
        into: "foo",
        on: [ "_id" ],
        whenMatched: "merge",
        whenNotMatched: "fail"
    }}

]);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文