繁体   English   中英

MongoDB 2.2 Aggregation Framework按字段名称分组

[英]MongoDB 2.2 Aggregation Framework group by field name

可以按字段名称分组吗? 还是我需要其他结构以便按价值分组?

我知道我们可以在值上使用分组依据,并且可以展开数组,但是可以在这里的三个房屋中获得约翰拥有的苹果,梨和橙子的总数, 而无需明确指定“苹果”,“豌豆”和“橙子”为查询的一部分? (所以不是这样);

// total all the fruit John has at each house
db.houses.aggregate([
    {
        $group: {
            _id: null,

            "apples":  { $sum: "$people.John.items.apples" },
            "pears":   { $sum: "$people.John.items.pears" }, 
            "oranges": { $sum: "$people.John.items.oranges" }, 
        }
    },
])

换句话说,我可以将“项”下的第一个字段名称分组,以获取苹果:104,梨:202和橙子:306以及香蕉,瓜类以及其他可能存在的总和吗? 还是我需要将数据重组为键/值对(如类别)的数组?

db.createCollection("houses");
db.houses.remove();
db.houses.insert(
[
    {
        House: "birmingham",
        categories : [
            {
                k : "location",
                v : { d : "central" }
            }
        ],
        people: {
            John: {
                items: {
                    apples: 2,
                    pears: 1,
                    oranges: 3,
                }
            },
            Dave: {
                items: {
                    apples: 30,
                    pears: 20,
                    oranges: 10,
                },
            },
        },
    },
    {
        House: "London", categories: [{ k: "location", v: { d: "central" } }, { k: "type", v: { d: "rented" } }],
        people: {
            John: { items: { apples: 2, pears: 1, oranges: 3, } },
            Dave: { items: { apples: 30, pears: 20, oranges: 10, }, },
        },
    },
    {
        House: "Cambridge", categories: [{ k: "type", v: { d: "rented" } }],
        people: {
            John: { items: { apples: 100, pears: 200, oranges: 300, } },
            Dave: { items: { apples: 0.3, pears: 0.2, oranges: 0.1, }, },
        },
    },
]
);

其次,更重要的是,我可以然后按“ house.categories.k”分组吗? 换句话说,是否可以找出“约翰”在“出租”与“拥有”或“朋友”房屋中有多少“苹果”(因此按“ categories.k.type”分组)?

最后-如果可能,这是否明智? 最初,我认为使用对象的实际字段名称创建嵌套对象的字典非常有用,因为这似乎是对文档数据库的合理使用,而且似乎使MR查询相对于数组更容易编写,但是现在我我开始怀疑这是否不是一个好主意,而具有可变字段名称会使编写聚合查询非常棘手/效率低下。

好的,所以我认为我已经部分解决了。 至少对于最初问题中的数据形状而言。

// How many of each type of fruit does John have at each location
db.houses.aggregate([
    {
        $unwind: "$categories"
    },
    {
        $match: { "categories.k": "location" }
    },
    {
        $group: {
            _id: "$categories.v.d",
            "numberOf": { $sum: 1 },
            "Total Apples": { $sum: "$people.John.items.apples" },
            "Total  Pears": { $sum: "$people.John.items.pears" },
        }
    },
])

产生;

{
        "result" : [
                {
                        "_id" : "central",
                        "numberOf" : 2,
                        "Total Apples" : 4,
                        "Total  Pears" : 2
                }
        ],
        "ok" : 1
}

请注意,只有“中央”,但是如果我的数据库中还有其他“位置”,则每个位置都会得到一定范围的总计。 如果我已命名属性而不是“类别”数组,则不需要$ unwind步骤,但这是我发现结构与自身不符的地方。 在“类别”下可能有几个关键字。 示例数据显示了“类型”和“位置”,但是这些分类中可能有大约10种都具有不同的值。 因此,如果我使用命名字段;

"categories": {
  location: "london",
  type: "owned",
}

...然后我遇到的问题是索引编制。 我不能简单地对“位置”进行索引,因为这些是用户定义的类别,如果10,000个用户选择10,000种不同的房屋分类方法,我将需要10,000个索引,每个字段一个。 但是通过将其设置为数组,我只需要在数组字段本身上设置一个即可。 不利的一面是$ unwind步骤。 我在使用MapReduce之前就遇到了这个问题。 您要做的最后一件事是JavaScript中的ForEach循环,如果可以帮助您,可以循环一个数组。 您真正想要的是按名称过滤字段,因为它更快。

现在,这一切都很好,我已经知道我要寻找什么水果,但是如果我不想要,那就很难了。 我无法(据我所知)在这里$ unwind或其他ForEach“ people.John.items”。 如果可以的话,我会很高兴。 因此,由于水果的名称又是用户定义的,因此我也需要将它们转换为数组。

{
    "people" : {
        "John" : {
            "items" : [
                { k:"apples", v:100 },
                { k:"pears", v:200 },
                { k:"oranges", v:300 },
            ]
        },
    }
}

这样一来,我就可以再次按位置汇总水果(我不知道要寻找的水果)。

db.houses.aggregate([
    {
        $unwind: "$categories"
    },
    {
        $match: { "categories.k": "location" }
    },
    {
        $unwind: "$people.John.items" 
    },
    {
        $group: { // compound key - thanks to Jenna
            _id: { fruit:"$people.John.items.k", location:"$categories.v.v" },
            "numberOf": { $sum: 1 },
            "Total Fruit": { $sum: "$people.John.items.v" },
        }
    },
])

所以现在我要进行两个$ unwind。 如果您认为这看起来效率低下,那将是正确的。 如果我只有10,000个房屋记录,每个记录有10个类别,并且有10种水果,则此查询需要半分钟才能运行。 好的,所以我可以看到在$ unwind之前移动$ match可以显着改善性能,但是输出是错误的。 我不需要每个类别的条目,我只想过滤掉“位置”类别。

我本来会发表此评论,但在响应文本框中设置格式更加容易。

{ _id: 1,
  house: "New York",
  people: {
      John: {
          items: {apples: 1, oranges:2}
      }
      Dave: {
          items: {apples: 2, oranges: 1}
      }
  }
}

{ _id: 2,
      house: "London",
      people: {
          John: {
              items: {apples: 3, oranges:2}
          }
          Dave: {
              items: {apples: 1, oranges:3}
          }
      }
}

为了确保我理解您的问题,这是您要完成的工作吗?

{location: "New York", johnFruit:3}
{location: "London", johnFruit: 5}

由于类别未嵌套在房屋下面,因此无法按“ house.categories.k”进行分组,但是可以对$ group的_id使用复合键来获得以下结果:

{ $group: _id: {house: "$House", category: "$categories.k"} 

尽管“ k”不包含您可能试图分组的信息。 至于“ categories.k.type”,type是k的值,所以您不能使用此语法。 您必须按“ categories.vd”分组。

您当前的模式可能有可能使用$ unwind,$ project,可能是$ match,最后是$ group来完成此聚合,但是命令不是很漂亮。 如果可能的话,我强烈建议您对数据进行重组,以使这种聚合更加简单。 如果您需要有关架构的帮助,请告诉我们。

我不确定这是否可行,但是如果您通过使用distinct()确定不同位置的数量并为每个位置运行单独的聚合命令来开始聚合过程,该怎么办? exclude()可能并不高效,但是每个后续聚合都可以使用$ match,因此可以使用类别索引。 您可以使用相同的逻辑来计算“ categories.type”的结果。

{
    "_id" : 1,
    "house" : "New York",
    "people" : {
        "John" : [{"k" : "apples","v" : 1},{"k" : "oranges","v" : 2}],
        "Dave" : [{"k" : "apples","v" : 2},{"k" : "oranges","v" : 1}]
    },
    "categories" : [{"location" : "central"},{"type" : "rented"}]
}
{
    "_id" : 2,
    "house" : "London",
    "people" : {
        "John" : [{"k" : "apples","v" : 3},{"k" : "oranges","v" : 2}],
        "Dave" : [{"k" : "apples","v" : 3},{"k" : "oranges","v" : 1}]
    },
    "categories" : [{"location" : "suburb"},{"type" : "rented"}]
}
{
    "_id" : 3,
    "house" : "London",
    "people" : {
        "John" : [{"k" : "apples","v" : 0},{"k" : "oranges","v" : 1}],
        "Dave" : [{"k" : "apples","v" : 2},{"k" : "oranges","v" : 4}]
    },
    "categories" : [{"location" : "central"},{"type" : "rented"}]
}

运行distinct(),并通过对“ categories.location”的每个唯一值运行aggregate()命令来遍历结果:

db.agg.distinct("categories.location")
[ "central", "suburb" ]

db.agg.aggregate(
    {$match: {categories: {location:"central"}}}, //the index entry is on the entire 
    {$unwind: "$people.John"},                    //document {location:"central"}, so 
    {$group:{                                     //use this syntax to use the index
         _id:"$people.John.k", 
         "numberOf": { $sum: 1 },
         "Total Fruit": { $sum: "$people.John.v"}
        }
     }
 )


{
    "result" : [
        {
            "_id" : "oranges",
            "numberOf" : 2,
            "Total Fruit" : 3
        },
        {
            "_id" : "apples",
            "numberOf" : 2,
            "Total Fruit" : 1
        }
    ],
    "ok" : 1
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM