MongoDB插入文档“或".增量字段(如果存在于数组中) [英] MongoDB insert document "or" increment field if exists in array

查看:152
本文介绍了MongoDB插入文档“或".增量字段(如果存在于数组中)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试做的事情很简单,我在文档中有一个数组

"tags": [ 
    {
        "t" : "architecture",
        "n" : 12
    }, 
    {
        "t" : "contemporary",
        "n" : 2
    }, 
    {
        "t" : "creative",
        "n" : 1
    }, 
    {
        "t" : "concrete",
        "n" : 3
    }
]

我想将一组项目推入

这样的数组

["architecture","blabladontexist"]

如果项目存在,我想增加对象的n值(在本例中为architecture),

,如果没有,则将其添加为新项(值n=0){ "t": "blabladontexist", "n":0}

我尝试了$addToSet$set$inc$upsert: true这么多组合,但无法做到.

我们如何在MongoDB中做到这一点?

解决方案

在MongoDB 4.2和更高版本中,更新方法现在可以获取文档或 $addFields 及其别名 $set

  • $project 及其别名 $unset
  • $replaceRoot 及其别名 $replaceWith .
  • 结合上述内容,您使用聚合管道进行的更新操作将通过过滤输入列表的过滤后的tags数组和映射后的数组与映射中的某些数据进行连接来覆盖tags字段:

    首先,用于过滤标签数组的聚合表达式使用 $filter 如下:

    const myTags = ["architecture", "blabladontexist"];
    
    { 
        "$filter": { 
            "input": "$tags",
            "cond": { 
                "$not": [
                    { "$in": ["$$this.t", myTags] } 
                ] 
            }
        } 
    }
    

    产生过滤后的文档数组

    [  
        { "t" : "contemporary", "n" : 2 }, 
        { "t" : "creative", "n" : 1 }, 
        { "t" : "concrete", "n" : 3 }
    ]
    

    现在,第二部分将是派生到上面的另一个数组.此数组需要在myTags输入上使用 $map 数组为

    { 
        "$map": { 
            "input": myTags,
            "in": {
                "$cond": {
                    "if": { "$in": ["$$this", "$tags.t"] },
                    "then": { 
                        "t": "$$this", 
                        "n": { 
                            "$sum": [
                                { 
                                    "$arrayElemAt": [
                                        "$tags.n", 
                                        { "$indexOfArray": [ "$tags.t", "$$this" ] } 
                                    ] 
                                },
                                1
                            ]
                        } 
                    },
                    "else": { "t": "$$this", "n": 0 }
                }
            }
        } 
    }
    

    上面的 $map 本质上是循环输入数组,并与每个元素进行比较,检查每个元素是否在tags数组中,如果t属性存在,则子文档的n字段的值将成为其当前的n值 用

    表示

    { 
        "$arrayElemAt": [
            "$tags.n", 
            { "$indexOfArray": [ "$tags.t", "$$this" ] } 
        ] 
    }
    

    否则添加默认文档,其n值为0.

    总体而言,您的更新操作如下

    您的最终更新操作将变为:

    const myTags = ["architecture", "blabladontexist"];
    
    db.getCollection('coll').update(
        { "_id": "1234" },
        [
            { "$addToSet": {
                "tags": {
                    "$concatArrays": [
                        { "$filter": { 
                            "input": "$tags",
                            "cond": { "$not": [ { "$in": ["$$this.t", myTags] } ] }
                        } },
                        { "$map": { 
                            "input": myTags,
                            "in": {
                                "$cond": [
                                    { "$in": ["$$this", "$tags.t"] },
                                    { "t": "$$this", "n": { 
                                        "$sum": [
                                            { "$arrayElemAt": [
                                                "$tags.n", 
                                                { "$indexOfArray": [ "$tags.t", "$$this" ] } 
                                            ] },
                                            1
                                        ]
                                    } },
                                    { "t": "$$this", "n": 0 }
                                ]
                            }
                        } }
                    ]
                }
            } }
        ],
        { "upsert": true }
    );
    

    What I try to do is fairly simple, I have an array inside a document ;

    "tags": [ 
        {
            "t" : "architecture",
            "n" : 12
        }, 
        {
            "t" : "contemporary",
            "n" : 2
        }, 
        {
            "t" : "creative",
            "n" : 1
        }, 
        {
            "t" : "concrete",
            "n" : 3
        }
    ]
    

    I want to push an array of items to array like

    ["architecture","blabladontexist"]
    

    If item exists, I want to increment object's n value (in this case its architecture),

    and if don't, add it as a new Item (with value of n=0) { "t": "blabladontexist", "n":0}

    I have tried $addToSet, $set, $inc, $upsert: true with so many combinations and couldn't do it.

    How can we do this in MongoDB?

    解决方案

    With MongoDB 4.2 and newer, the update method can now take a document or an aggregate pipeline where the following stages can be used:

    1. $addFields and its alias $set
    2. $project and its alias $unset
    3. $replaceRoot and its alias $replaceWith.

    Armed with the above, your update operation with the aggregate pipeline will be to override the tags field by concatenating a filtered tags array and a mapped array of the input list with some data lookup in the map:

    To start with, the aggregate expression that filters the tags array uses the $filter and it follows:

    const myTags = ["architecture", "blabladontexist"];
    
    { 
        "$filter": { 
            "input": "$tags",
            "cond": { 
                "$not": [
                    { "$in": ["$$this.t", myTags] } 
                ] 
            }
        } 
    }
    

    which produces the filtered array of documents

    [  
        { "t" : "contemporary", "n" : 2 }, 
        { "t" : "creative", "n" : 1 }, 
        { "t" : "concrete", "n" : 3 }
    ]
    

    Now the second part will be to derive the other array that will be concatenated to the above. This array requires a $map over the myTags input array as

    { 
        "$map": { 
            "input": myTags,
            "in": {
                "$cond": {
                    "if": { "$in": ["$$this", "$tags.t"] },
                    "then": { 
                        "t": "$$this", 
                        "n": { 
                            "$sum": [
                                { 
                                    "$arrayElemAt": [
                                        "$tags.n", 
                                        { "$indexOfArray": [ "$tags.t", "$$this" ] } 
                                    ] 
                                },
                                1
                            ]
                        } 
                    },
                    "else": { "t": "$$this", "n": 0 }
                }
            }
        } 
    }
    

    The above $map essentially loops over the input array and checks with each element whether it's in the tags array comparing the t property, if it exists then the value of the n field of the subdocument becomes its current n value expressed with

    { 
        "$arrayElemAt": [
            "$tags.n", 
            { "$indexOfArray": [ "$tags.t", "$$this" ] } 
        ] 
    }
    

    else add the default document with an n value of 0.

    Overall, your update operation will be as follows

    Your final update operation becomes:

    const myTags = ["architecture", "blabladontexist"];
    
    db.getCollection('coll').update(
        { "_id": "1234" },
        [
            { "$addToSet": {
                "tags": {
                    "$concatArrays": [
                        { "$filter": { 
                            "input": "$tags",
                            "cond": { "$not": [ { "$in": ["$$this.t", myTags] } ] }
                        } },
                        { "$map": { 
                            "input": myTags,
                            "in": {
                                "$cond": [
                                    { "$in": ["$$this", "$tags.t"] },
                                    { "t": "$$this", "n": { 
                                        "$sum": [
                                            { "$arrayElemAt": [
                                                "$tags.n", 
                                                { "$indexOfArray": [ "$tags.t", "$$this" ] } 
                                            ] },
                                            1
                                        ]
                                    } },
                                    { "t": "$$this", "n": 0 }
                                ]
                            }
                        } }
                    ]
                }
            } }
        ],
        { "upsert": true }
    );
    

    这篇关于MongoDB插入文档“或".增量字段(如果存在于数组中)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆