MongoDB按平均组合数或嵌套子数组排序

时间:2015-05-21 07:37:28

标签: php arrays mongodb

在MongoDB中有一些问题可以找到最好的方法,可以说它是一个关系数据集,所以我可能会被提到。仍然是一个挑战,看它是否可能。

我目前需要由物流经理订购。他们所在部门的货车每日平均里程数,以及每周平均值的单独列表。

数据库中的First先生设置如下

{
   "_id" : ObjectId("555cf04fa3ed8cc2347b23d7"),
   "name" : "My Manager 1",
   "vans" : [ 
       {
           "name" : "van1",
           "miles" : NumberLong(56)
       },
       {
           "name" : "van2",
           "miles" : NumberLong(34)
       }
   ]
}

但我不知道如何通过嵌套数组值排序而不知道父数组键(这些将是标准的0-x)

所以我的下一个选择是废弃这个想法,只是在第一个集合中有名字,第二个集合中的面包车是经理的Id。

所以从上面的例子中移除面包车并添加这个集合(面包车)

{
   "_id" : ObjectId("555cf04fa3ed8cc2347b23d9"),
   "name" : "van1",
   "miles" : NumberLong(56),
   "manager_id" : "555cf04fa3ed8cc2347b23d7"
}

但是因为我需要通过经理显示结果,如何在查询中(如果可能)订购此集合中的平均里程,其中id = x,然后按其ID显示经理。

感谢您的帮助

2 个答案:

答案 0 :(得分:2)

如果Manager的{​​{1}}数量有限,那么您的第一种方法会更好,因为您不必对数据库进行两次单独的调用/查询来收集您的信息

接下来是如何计算每Van平均值的问题,其中Aggregation Framework将对您有所帮助。这是一个可以获得所需数据的查询:

Manager

第一个db.manager.aggregate([ {$unwind: "$vans"}, {$group: {_id: { _id: "$_id", name: "$name" }, avg_milage: {$avg: "$vans.miles"} } }, {$sort: {"avg_milage": -1}}, {$project: {_id: "$_id._id", name: "$_id.name", avg_milage: "$avg_milage" } } ]) 步骤只需展开$unwind数组,并为数组的每个元素创建单独的文档。

然后vans阶段获取具有相同$group对的所有文档,并在(_id, name)字段中,计算这些文档中avg_milage字段的平均值。

miles阶段显而易见,它只是按降序对文档进行排序,使用新的$sort字段作为排序键。

最后,最后avg_milage步骤只是通过做出适当的预测来清理文档,仅用于美容:)

您的第二个期望结果需要类似的事情:

$project

这将生成db.manager.aggregate([ {$unwind: "$vans"}, {$group: {_id: { _id: "$_id", name: "$name" }, total_milage: {$sum: "$vans.miles"} } }, {$sort: {"total_milage": -1}}, {$project: {_id: "$_id._id", name: "$_id.name", weekly_milage: { $multiply: [ "$total_milage", 7 ] } } } ]) 的列表及其每周的milage,按降序排序。因此,您可以Managers结果,并获得具有最高级别的$limit

以非常相似的方式,你可以获取你的货车的信息:

Manager

答案 1 :(得分:1)

首先,您需要一天的平均里程,特定时间段内的平均里程数,或经理人生命周期内的平均里程数吗?我会考虑添加一个时间戳字段。是的,_id有时间戳,但这只反映了文档创建的时间,不一定是初始日志的时间。

第一个数据模型的注意事项:

  • 每份文件是代表一天还是一位经理?
  • 您希望阵列中有多少“面包车”?这个清单会随着时间而增长吗?从现在开始,您需要在一年或两年内考虑16MB的最大文档大小吗?

第二个数据模型的注意事项:

  • 您可以将经理的姓名存储为“manager_id”字段吗?这可以用作辅助元查找的唯一ID吗?这样做会限制辅助管理器元数据查找的必要性,只是为了得到他们的名字。

作为@n9code has pointed out,聚合框架就是两种情况下的答案。

对于第一个数据模型,假设每个文档代表一天,并且您想要检索给定日期或天数范围内的平均值:

db.collection.aggregate([
    { $match: {
        name: 'My Manager 1',
        timestamp: { $gte: ISODate(...), $lt: ISODate(...) }
    } },
    { $unwind: '$vans' },
    { $group: {
        _id: {
            _id: '$_id',
            name: '$name',
            timestamp: '$timestamp'
        },
        avg_mileage: {
            $avg: '$miles'
        }
    } },
    { $sort: {
        avg_mileage: -1
    } },
    { $project: {
        _id: '$_id._id',
        name: '$_id.name',
        timestamp: '$_id.timestamp',
        avg_mileage: 1
    } }
]);

如果对于第一个数据模型,每个文档代表一个管理器并且“vans”数组每天都在增长,那么这个特定的数据模型并不理想,原因有两个:

  • “vans”数组可能会超出最大文档大小...最终,虽然这将是大量数据
  • 限制某个日期范围更加困难和内存密集,因为此时的时间戳将嵌套在“货车”项目中,而不是在文档的根目录中

为了完整起见,这是查询:

/*
Assuming data model is:
{
    _id: ...,
    name: ...,
    vans: [
        { name: ..., miles: ..., timestamp: ... }
    ]
}
*/

db.collection.aggregate([
    { $match: {
        name: 'My Manager 1'
    } },
    { $unwind: '$vans' },
    { $match: {
        'vans.timestamp': { $gte: ISODate(...), $lt: ISODate(...) }
    } },
    { $group: {
        _id: {
            _id: '$_id',
            name: '$name'
        },
        avg_mileage: {
            $avg: '$miles'
        }
    } },
    { $sort: {
        avg_mileage: -1
    } },
    { $project: {
        _id: '$_id._id',
        name: '$_id.name',
        avg_mileage: 1
    } }
]);

对于第二个数据模型,聚合更直接。我假设包含一个时间戳:

db.collection.aggregate([
    { $match: {
        manager_id: ObjectId('555cf04fa3ed8cc2347b23d7')
        timestamp: { $gte: ISODate(...), $lt: ISODate(...) }
    } },
    { $group: {
        _id: '$manager_id'
        },
        avg_mileage: {
            $avg: '$miles'
        }
        names: {
            $addToSet: '$name'
        }
    } },
    { $sort: {
        avg_mileage: -1
    } },
    { $project: {
        manager_id: '$_id',
        avg_mileage: 1
        names: 1
    } }
]);

我在平均计算过程中添加了一系列名称(车辆?)。

相关文件: