読者です 読者をやめる 読者になる 読者になる

あいつの日誌β

あいつの日誌です。

mongodb の aggregate で日付をうまく sort したい

Mongo Node.js

こんにちは

氷室京介氷室京介を卒業するというニュースのおかげで日本中にザラついた心どうすればいいんだ問題を抱える人々が急増している今日この頃ですがみなさんは大丈夫ですか?

僕は常にザラついているので大丈夫です。

本題

日毎にある商品がどれぐらい売れているのかを一覧で表示させる画面があります。 以下はイメージです

日付, 商品ID, 販売個数
2014-06-19, product2, 3
2014-06-19, product1, 2
2014-06-19, product3, 5
2014-06-18, product3, 6
2014-06-18, product2, 3

で、以下のように並べ替えたいという依頼がありましたとさ

日付, 商品ID, 販売個数
2014-06-19, product3, 5
2014-06-19, product2, 3
2014-06-19, product1, 2
2014-06-18, product3, 6
2014-06-18, product2, 3

つまり日付、販売個数をそれぞれ降順、日付を優先という事です。 で、この作業が手こずったので忘却しないうちにメモ

やってみろ

というわけで改修前の状態は以下です。

db.collection('orders').aggregate
  $project:
    product_id: 1
    ordered_at: 1
    year:
      $year: "$ordered_at"
    month:
      $month: "$ordered_at"
    day:
      $dayOfMonth: "$ordered_at" 
,   
  $group:
    _id:
      year: "$year"
      month: "$month"
      day: "$day"
      product_id: "$product_id"
    ordered_at:
      $last: "$ordered_at"
    count:
      $sum: 1
, (e, results)->
  for r in results
    console.log e if e
    console.log moment(r.ordered_at).format('YYYY-MM-DD') + ', ' + r._id.product_id + ', ' +   r.count

ordered_at, count の順番で降順に sort すればいいんじゃないのだろうか

    count:
      $sum: 1
+   $sort:
+     ordered_at: -1
+     count: -1
, (e, results)->

結果

2014-06-19, product2, 3
2014-06-19, product3, 5
2014-06-19, product1, 2
2014-06-18, product3, 6
2014-06-18, product2, 3
2014-06-18, product1, 1
2014-06-17, product1, 1

おかしいな count の並び順が期待どおりではない。 そうか ordered_at を ymd 形式にしてから並べないとだめだった。

日付を整形

というわけで $year, $month, $day を連結させればいいんじゃないかと思ったのでさらにこうしました。

$project に以下を追加

daykey:
  $concat: [
    {$year: "$ordered_at"},
    {$month: "$ordered_at"},
    {$dayOfMonth: "$ordered_at"}
  ]

そうしたらこうなった

$concat only supports strings, not NumberInt32

ええ!?なんだって!?(オーディエンスにもっと声をだせと煽る時の氷室京介風に)

解決策を探す

で文字列にする方法を探しだし、こうやって解決できた

daykey:
  $concat: [
    $substr: [{$year: "$ordered_at"}, 0, 4]
    '-' 
    $substr: [{$month: "$ordered_at"}, 0, 2]
    '-' 
    $substr: [{$dayOfMonth: "$ordered_at"}, 0, 2]
  ]

おしまい

サンプルコード

https://gist.github.com/okamuuu/ccf62dc83e6a87e94645