mongodb avg聚合数组元素

时间:2015-05-06 12:54:06

标签: php mongodb

我有以下收集结构

{
   "_id": {
     "d_timestamp": NumberLong(1429949699),
     "d_isostamp": ISODate("2015-04-25T08:14:59.0Z")
   },
   "XBT-USD-cpx-okc": [
   {
       "buySpread": -1.80081
   }

我运行以下聚合

$spreadName ='XBT-USD-stp-nex';
$pipe = array(
    array(
        '$match' => array(
            '_id.d_isostamp' => array(
                '$gt' => $start, '$lt' => $end
            )
        )
    ),
    array(
        '$project' => array(
            'sellSpread' =>'$'.$spreadName.'.sellSpread',
        )
    ),
    array(
        '$group' => array(
            '_id' => array(
                'isodate' => array(
                    '$minute' => '$_id.d_isostamp'
                )
            ),
            'rsell_spread' => array(
                '$avg' => '$sellSpread'
            ),
        )
    ),
);

$out = $collection->aggregate($pipe ,$options);

我得到rsell_spread的值为0,而如果我在$max中运行$avg而不是$group,我会得到一个准确的结果rsell_spread的值,具有以下结构

{
  "_id": {
     "isodate": ISODate("2015-04-25T08:00:58.0Z")
  },
  "rsell_spread": [
     -4.49996▼
  ]
}

所以我有两个问题:

1 /为什么$avg功能不起作用?

2 /例如,当我使用$max(只是一个常规数字)时,如何才能使结果不在数组中?

1 个答案:

答案 0 :(得分:1)

  1. $avg组累加器运算符确实有效,只是在你的情况下,它被应用于数组中的元素,从而使得"不正确"结果

  2. 当您使用$max组累加器运算符时,它将返回将表达式应用于一组文档中的每个文档所产生的最高值,因此在您的示例中它返回了最大数组。

  3. 为了证明这一点,请考虑在mongoshell中将一些示例文档添加到测试集合中:

    db.test.insert([
    {
        "_id" : {
            "d_timestamp" : NumberLong(1429949699),
            "d_isostamp" : ISODate("2015-04-25T08:14:59.000Z")
        },
        "XBT-USD-stp-nex" : [ 
            {
                "sellSpread" : -1.80081
            }
        ]
    },
    {
        "_id" : {
            "d_timestamp" : NumberLong(1429949710),
            "d_isostamp" : ISODate("2015-04-25T08:15:10.000Z")
        },
        "XBT-USD-stp-nex" : [ 
            {
                "sellSpread" : -1.80079
            }
        ]
    },
    {
        "_id" : {
            "d_timestamp" : NumberLong(1429949720),
            "d_isostamp" : ISODate("2015-04-25T08:15:20.000Z")
        },
        "XBT-USD-stp-nex" : [ 
            {
                "sellSpread" : -1.80083
            }
        ]
    },
    {
        "_id" : {
            "d_timestamp" : NumberLong(1429949730),
            "d_isostamp" : ISODate("2015-04-25T08:15:30.000Z")
        },
        "XBT-USD-stp-nex" : [ 
            {
                "sellSpread" : -1.80087
            }
        ]
    }
    ])
    

    现在,在mongoshell中复制上面的相同操作:

    var spreadName = "XBT-USD-stp-nex",
        start = new Date(2015, 3, 25),
        end = new Date(2015, 3, 26);
    db.test.aggregate([
        {
            "$match": {
                "_id.d_isostamp": { "$gte": start, "$lte": end }
            }
        },
        {
            "$project": {
                "sellSpread": "$"+spreadName+".sellSpread"
            }
        }/*,<--- deliberately omitted the $unwind stage from the pipeline to replicate the current pipeline
        {
            "$unwind": "$sellSpread"
        }*/,
        {
            "$group": {
                "_id": {
                    "isodate": { "$minute": "$_id.d_isostamp"}
                },
                "rsell_spread": {
                    "$avg": "$sellSpread"
                }
            }
        }
    ])
    

    <强>输出

    /* 0 */
    {
        "result" : [ 
            {
                "_id" : {
                    "isodate" : 15
                },
                "rsell_spread" : 0
            }, 
            {
                "_id" : {
                    "isodate" : 14
                },
                "rsell_spread" : 0
            }
        ],
        "ok" : 1
    }
    

    解决方案是在$unwind步骤之后包含$project运算符管道阶段,这将从输入文档解构XBT-USD-stp-nex数组字段并输出每个元素的文档。每个输出文档都使用元素值替换数组。这将使$avg组累加器运算符成为可能。

    包括这将给出聚合结果:

    /* 0 */
    {
        "result" : [ 
            {
                "_id" : {
                    "isodate" : 15
                },
                "rsell_spread" : -1.80083
            }, 
            {
                "_id" : {
                    "isodate" : 14
                },
                "rsell_spread" : -1.80081
            }
        ],
        "ok" : 1
    }
    

    所以你在PHP中的最终工作聚合应该是:

    $spreadName ='XBT-USD-stp-nex';
    $pipe = array(
        array(
            '$match' => array(
                '_id.d_isostamp' => array(
                    '$gt' => $start, '$lt' => $end
                )
            )
        ),    
        array(
            '$project' => array(
                'sellSpread' =>'$'.$spreadName.'.sellSpread',
            )
        ),
        array('$unwind' => '$sellSpread'),
        array(
            '$group' => array(
                '_id' => array(
                    'isodate' => array(
                        '$minute' => '$_id.d_isostamp'
                    )
                ),
                'rsell_spread' => array(
                    '$avg' => '$sellSpread'
                ),
            )
        ),
    );
    
    $out = $collection->aggregate($pipe ,$options);
    
相关问题