使用公共数据合并内部数组

时间:2014-05-08 05:33:51

标签: mongodb aggregation-framework

db.test.aggregate([
    { "$unwind": "$Data" },
    { "$sort" : { "Data.cost" : -1 } },
    { "$group":{  
        "_id":"$Data.name",
        "Data":{ "$push": "$Data" }
    }}
])

我解雇了上面的查询。它给我的结果如下:

{
    "result":[{
        "_id" : "abc"
        "Data" : [
            {
                "uid" : "1...A",
                "name" : "abc",
                "city" : "Paris",
                "description" : {
                    "things" : [
                        {
                            "fruit" : {
                                "fruit_name" : "apple",
                                "fruit_rate" : "4 USD"
                            },
                            "flower" : {
                                "flower_name" : "rose",
                                "flower_rate" : "2 USD"
                            }
                        }
                    ]
                },
                "cost" : "6 USD"
            },
            {
                "uid" : "1...B",
                "name" : "abc",
                "city" : "Paris",
                "description" : {
                    "things" : [
                        {
                            "fruit" : {
                                "fruit_name" : "cherry",
                                "fruit_rate" : "3 USD"
                            },
                            "flower" : {
                                "flower_name" : "orchid",
                                "flower_rate" : "2 USD"
                            }
                        }
                    ]
                }, 
                "cost" : "5 USD"
            }
        ]
    }]
}

但我不想要这样的结果。如果" name" 相同,我想合并两个数据的" description" 数组。 如下所述:

{
    "result":[{
        "_id" : "abc"
        "Data" : [
            {
                "uid" : "1...A",
                "name" : "abc",
                "city" : "Paris",
                "description" : {
                    "things" : [
                        {
                            "fruit" : {
                                "fruit_name" : "apple",
                                "fruit_rate" : "4 USD"
                            },
                            "flower" : {
                                "flower_name" : "rose",
                                "flower_rate" : "2 USD"
                            }
                        }
                    ]
                },
                "description" : {
                    "things" : [
                        {
                            "fruit" : {
                                "fruit_name" : "cherry",
                                "fruit_rate" : "3 USD"
                            },
                            "flower" : {
                                "flower_name" : "orchid",
                                "flower_rate" : "2 USD"
                            }
                        }
                    ]
                }, 
                "cost" : "6 USD"   
            }
        ]
    }]
}

有可能得到这样的结果吗?我在查询中需要做哪些更改?

谢谢。

1 个答案:

答案 0 :(得分:1)

您构建所需结果的方式根本不可能。这样做的原因是你基本上打破了Hash Table或字典/关联数组背后的原则(无论哪个术语更适合你),因为你不能有超过一个键值名。

如果您想要同名的多个键,那么这些键必须包含在一个数组中,这与您拥有的结构类型以及结果中的结构非常相似。除了对数组元素进行排序,然后将它们组合回一个数组之外,该结果并没有真正做任何事情。

所以在这里给你一些空间,因为你只需要复制并粘贴来表示你想要的结果,并且你真的想要某种形式的合并的内部元素,你可以总是做这样的事情:

db.test.aggregate([
    { "$unwind": "$Data" },
    { "$unwind": "$Data.description.things" },
    { "$group": {
        "_id": "$Data.name",
        "city": { "$first": "$Data.city" },
        "things": { "$addToSet": "$Data.description.things" }
    }}
])

产生结果:

{
    "_id" : "abc",
    "city" : "Paris",
    "things" : [
        {
            "fruit" : {
                "fruit_name" : "cherry",
                "fruit_rate" : "3 USD"
             },
             "flower" : {
                 "flower_name" : "orchid",
                 "flower_rate" : "2 USD"
             }
        },
        {
             "fruit" : {
                 "fruit_name" : "apple",
                 "fruit_rate" : "4 USD"
             },
             "flower" : {
                 "flower_name" : "rose",
                 "flower_rate" : "2 USD"
             }
        }
    ]
}

因此,在对一个公共元素进行分组并添加一些额外字段时,内部“事物”现在被“推”到一个单个数组中。

如果你真的想要甚至更多“合并”的东西,甚至可能避免删除重复的“set”项目,那么你可以用这样的语句进一步重塑:

db.test.aggregate([
    { "$unwind": "$Data" },
    { "$unwind": "$Data.description.things" },
    { "$project": {
         "name": "$Data.name",
         "city": "$Data.city",
         "things": "$Data.description.things",
         "type": { "$literal": [ "flower", "fruit" ] }
    }},
    { "$unwind": "$type" },
    { "$group": {
        "_id": "$name",
        "city": { "$first": "$city" },
        "things": { "$push": { "$cond": [
            { "$eq": [ "$type", "flower" ] },
            { 
                "type": "$type", 
                "name": "$things.flower.flower_name", 
                "rate": "$things.flower.flower_rate"
            },
            { 
                "type": "$type", 
                "name": "$things.fruit.fruit_name", 
                "rate": "$things.fruit.fruit_rate"
            },
        ]}}
    }}
 ])

结果如下:

{
    "_id" : "abc",
    "city" : "Paris",
    "things" : [
        {
            "type" : "flower",
            "name" : "rose",
            "rate" : "2 USD"
        },
        {
            "type" : "fruit",
            "name" : "apple",
            "rate" : "4 USD"
        },
        {
            "type" : "flower",
            "name" : "orchid",
            "rate" : "2 USD"
        },
        {
            "type" : "fruit",
            "name" : "cherry",
            "rate" : "3 USD"
        }
    ]
}

这甚至可能表明原始数据首先如何更好地构建。当然,如果你想做一些像“找到'樱桃'或'鲜花'或'水果'的总价值”的东西,你肯定会需要重新塑造这样的形状或者不论那种类型。

因此,您构建结果的方式是不可能的,违反了上述规则。在我提出的表格中,有几种方法可以做到。

PS :我故意远离您的 $sort 表示,好像它在您的初始示例中“对您有用”,不要指望这可用于更广泛的示例,因为您的值是字符串而不是数字。简而言之,这意味着"10 USD"实际上小于 "4 USD",因为字符串在词汇上进行词汇比较。即:4 大于 1,这是比较完成的顺序。

因此,通过拆分字段并使用数字类型来更改它们,如:

        {
            "type" : "fruit",
            "name" : "cherry",
            "rate" : 3,
            "currency": "USD"
        }

如果需要,你甚至可以过滤“货币”。

P.P.S $literal运算符是可用于MongoDB 2.6及更高版本的构造。在以前的版本中,该运算符不可用,您可以改为编码:

         "type": { "$cond": [ 1, [ "flower", "fruit" ], 0 ] }

$cond(或甚至true值)返回的false值相同的事情是“字面上”声明的,所以你放在那里的内容实际上会被生成。在这种情况下,它是一种向投影添加“数组”的方法,这是为了匹配“类型”。

您可能会在网上找到使用 $const 的网址,但我并不特别相信,虽然它确实存在,但并不是为了这个目的因此没有正式记录