【问题标题】:Get all objects for a week number获取周数的所有对象
【发布时间】:2015-10-19 20:20:06
【问题描述】:

我需要获取 MongoDB 中所有记录的周范围列表。当我单击一周范围时,它将仅显示该周范围的记录。点击周范围会发送周的 ID(比如说42,即 2015 年的第 42 周),它应该会得到这些结果。

问题:如何查询给定周数和年份的一组记录?这应该可行,对吧?

架构:

var orderSchema = mongoose.Schema({
        date: Date, //ISO date
        request: {
            headers : {
            ...

首先:获取所有对象的所有周 ID:

    var query = Order.aggregate(
        [
            {
                $project:
                {
                    week:
                    {
                        $week: '$date'
                    }
                }
            },
            {
                $group:
                {
                    _id: null,
                    distinctDate:
                    {
                        $addToSet:
                        {
                            week: '$week'
                        }
                    }
                }
            }
        ]
    );

结果:

distinctDate: Array[35]
    0: Object
       week: 40
    1: Object
       week: 37
       ...

使用MomentJS 转换为周范围并显示:

    data.forEach(function(v, k) {
        $scope.weekRanges.push(getWeekRange(v.week));
    });

    function getWeekRange(weekNum) {

        var monday = moment().day("Monday").isoWeek(weekNum).format('MM-DD-YYYY');
        var sunday = moment().day("Sunday").isoWeek(weekNum).format('MM-DD-YYYY');
        ...

输出:

Week 
10-12-2015 to 10-18-2015 //week ID 42
10-05-2015 to 10-11-2015 //week ID 41
09-28-2015 to 10-04-2015 ...
...

第二次:点击周范围,获取 Objects Per Week ID:

var year = 2015;
var weekID = weekParamID; //42

    if (!Order) {
        Order = mongoose.model('Order', orderSchema());
    }

    var query = Order.aggregate(
        {
            $project:
            {
                cust_ID : '$request.headers.custID',
                cost : '$response.body.pricing.cost',
                year :
                {
                    $year:  '$date'
                },
                month :
                {
                    $month: '$date'
                },
                week:
                {
                    $week: '$date'
                },
                day:
                {
                    $dayOfMonth: '$date'
                }
            }
        },
        {
            $match:
            {
                year : year, //2015
                week : weekID //42
            }
        }
    );

如果我点击周范围10-12-2015 to 10-18-2015(第 42 周),我会得到日期超出范围(2015 年 10 月 19 日)的结果:

10-19-2015      Order info  
10-18-2015      Order info
10-19-2015      Order info

使用 MongoDB 命令行:

db.mycollection.aggregate({ $project: { week: { $week: '$date' }, day: { $dayOfMonth: '$date' } }  }, { $match: { week: 42 } }

结果:

{ "_id" : "1bd482f6759b", "week" : 42, "day" : 19 } //shouldn't exceed week range
{ "_id" : "b3d38759", "week" : 42, "day" : 19 }

编辑:更新

因此,MongoDB ISO 周(从周日开始)和 Moment JS ISO(从周一开始)存在差异。

This SO post 建议从查询中减去日期,以便 Mongo 日期从星期一开始:

{ 
$project: 
  {
    week: { $week: [ "$datetime" ] },
    dayOfWeek:{$dayOfWeek:["$datetime"]}}
  },
{
$project:
 {
  week:{$cond:[{$eq:["$dayOfWeek",1]},{$subtract:["$week",1]},'$week']}
 }
}

我用我的查询实现了这个,但现在它没有返回我需要的两个字段:

            cust_ID : '$request.headers.custID',
            cost : '$response.body.pricing.cost'

查询

 db.mycollection.aggregate(
        {
            $project:
            {
                cust_ID : '$request.headers.custID',
                cost : '$response.body.pricing.cost',
                week:
                {
                    $week: ['$date']
                },
                dayOfWeek:
                {
                    $dayOfWeek: ['$date']
                }
            }
        },
        {
            $project:
            {
                week: {
                    $cond: [
                        {
                            $eq: [
                                "$dayOfWeek", 1
                            ]
                        },
                        {
                            $subtract: [
                                "$week", 1
                            ]
                        }, '$week'
                    ]
                }
            }
        },
        {
            $match:
            {
                week : 42
            }
        }
    );

结果:

{ "_id" : "387e2", "week" : 42 }
{ "_id" : "ef269f6341", "week" : 42 }
{ "_id" : "17482f6759b", "week" : 42 }
{ "_id" : "7123d38759", "week" : 42 }
{ "_id" : "ff89b1fb", "week" : 42 }

它没有返回我在 $project 中指定的字段集

【问题讨论】:

  • 您需要在第二个项目中再次包含它们。例如。成本:'$成本'
  • @sheilak,谢谢。因此,mongodb $cond 修复似乎不起作用。我正在返回 $dayOfMonth 以显示下订单 obj 的月份中的哪一天,但它仍然显示超出 2015 年 10 月 12 日范围的天数(即 2015 年 10 月 19 日甚至 2015 年 10 月 20 日)- 2015 年 10 月 18 日
  • 更新了我的答案。您链接到的答案转换为星期一开始的一周,而不是 ISO 标准周。 ISO 标准周与 MongoDB 的不同之处在于它如何定义年初的第 1 周。对不起,我没有解决方案,但希望有几个指针可以更接近

标签: javascript mongodb mongoose


【解决方案1】:

MongoDB $week 操作员认为星期从星期日开始,see docs

周从星期日开始,第 1 周从第一个星期日开始 年...此行为与 strftime 的“%U”运算符相同 标准库函数。

Moment.JS 的 isoWeekday() 使用 ISO 周,它认为周从星期一开始。它的不同之处还在于它将第 1 周视为第一周,其中包含星期四。

这种差异可以解释您所看到的行为。

例如如果我将此文档保存在周一的 MongoDB 中:

db.test.save({ "date" : new ISODate("2015-10-19T10:10:10Z")  })

然后运行上面的聚合查询,我得到第 42 周。

但是如果我运行以下命令:

console.log(moment().day("Monday").isoWeek(42))

我得到以下日期,这不是我最初保存在 MongoDB 中的日期,即使它是 MongoDB 报告的一周中的星期一。

Mon Oct 12 2015

我猜如何解决它取决于您需要的星期定义。

如果您对 MongoDB $week 定义感到满意,可能很容易找到/编写一个替代实现来将周数转换为相应的日期。这是一个为 Moment.js 添加 strftime 支持的库:

https://github.com/benjaminoakes/moment-strftime

如果你想使用 ISO 格式,那就更复杂了。根据您上面的编辑,您需要考虑周开始差异。但是您还需要考虑年初差异的周数。这种差异意味着 strftime 周数可以有第 0 周,而 ISO 总是从第 1 周开始。对于 2015 年,看起来您需要在 strftime 周上增加 1 周才能获得 ISO 周,并考虑周开始日,但一般来说这并不可靠。

【讨论】:

  • 我已经尝试在 Mongo 方面进行更改。请参阅上面的编辑
【解决方案2】:

MongoDB 版本 3.4 开始,您可以使用 $isoWeek 聚合运算符。

以 ISO 8601 格式返回周数,范围从 1 到 53。周数从 1 开始,其中包含一年的第一个星期四的周(周一到周日)。

您可以在MongoDB docs 中找到更多相关信息。

【讨论】:

    猜你喜欢
    • 2011-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多