복붙노트

[MONGODB] 기록에 날짜 누락 기입

MONGODB

기록에 날짜 누락 기입

나는 ProductViews의 컬렉션이 있습니다.

ProductView의

{
    productId: '5b8c0f3204a10228b00a1745,
    createdAt: '2018-09-07T17:18:40.759Z',
}

그리고 특정 제품의 일일 조회수를 가져 오는에 대한 쿼리를 가지고있다.

질문

ProductView.aggregate([
    { $match: { productId } },
    { $project: { day: { $substr: ["$createdAt", 0, 10] } } },
    {
        $group: {
            _id: "$day",
            count: { $sum: 1 },
            time: { $avg: "$createdAt" },
        }
    },
    { $sort: { _id: 1 } },
    {
        $project: {
            date: '$_id',
            views: '$count',
        },
    },
]).exec((err, result) => ...)

현재 결과

[
    { date: '2018-09-01', views: 1 },
    { date: '2018-09-02', views: 3 },
    { date: '2018-09-04', views: 2 },
    { date: '2018-09-05', views: 5 },
    // ...
]

발행물

문제는이 집계는 반환하지 않습니다이다 {날짜 : '2018년 9월 3일', 조회 : 0} 0 전망 일. 데이터의 표시 잘못된 이러한 결과 :

결과는 같아야합니다

[
    { date: '2018-09-01', views: 1 },
    { date: '2018-09-02', views: 3 },
    { date: '2018-09-03', views: 0 },
    { date: '2018-09-04', views: 2 },
    { date: '2018-09-05', views: 5 },
    // ...
]

P.S은 :이 범위에 따라 출력 결과에 대한 시작 및 종료 날짜에 통과 할 완벽한 것

해결법

  1. ==============================

    1.당신은 기본 값을 반환하는 몇 가지 추가 단계가 필요합니다. 우선 당신은 하나 개의 문서에있는 모든 결과를 수집하는 null로 _id 세트 $ 그룹을 사용해야합니다. 그럼 당신은 입력으로 일의 배열 $지도를 사용할 수 있습니다. 그 $ 내부 당신이 그 날짜가 현재 결과 집합에 존재하는지 찾기 위해 $ indexOfArray을 사용할 수 있습니다 매핑합니다. 예라면 (인덱스! = -1) 당신은 그렇지 않으면 당신은 그런 다음에 중첩 된 통계를 촉진하기 위해 문서 및 $ replaceRoot의 목록을 다시 얻을 $ 언 와인드을 사용할 수 있습니다 0으로 설정 전망의 기본 하위 문서를 반환해야, 그 값을 반환 할 수 있습니다 최고 수준입니다.

    당신은 기본 값을 반환하는 몇 가지 추가 단계가 필요합니다. 우선 당신은 하나 개의 문서에있는 모든 결과를 수집하는 null로 _id 세트 $ 그룹을 사용해야합니다. 그럼 당신은 입력으로 일의 배열 $지도를 사용할 수 있습니다. 그 $ 내부 당신이 그 날짜가 현재 결과 집합에 존재하는지 찾기 위해 $ indexOfArray을 사용할 수 있습니다 매핑합니다. 예라면 (인덱스! = -1) 당신은 그렇지 않으면 당신은 그런 다음에 중첩 된 통계를 촉진하기 위해 문서 및 $ replaceRoot의 목록을 다시 얻을 $ 언 와인드을 사용할 수 있습니다 0으로 설정 전망의 기본 하위 문서를 반환해야, 그 값을 반환 할 수 있습니다 최고 수준입니다.

    ProductView.aggregate([
        { $match: { productId: '5b8c0f3204a10228b00a1745' } },
        { $project: { day: { $substr: ["$createdAt", 0, 10] } } },
        {
            $group: {
                _id: "$day",
                count: { $sum: 1 },
                time: { $avg: "$createdAt" },
            }
        },
        { $sort: { _id: 1 } },
        {
            $project: {
                date: '$_id',
                views: '$count',
            },
        },
        {
            $group: {
                _id: null,
                stats: { $push: "$$ROOT" }
            }
        },
        {
            $project: {
                stats: {
                    $map: {
                        input: [ "2018-09-01", "2018-09-02", "2018-09-03", "2018-09-04", "2018-09-05" ],
                        as: "date",
                        in: {
                            $let: {
                                vars: { dateIndex: { "$indexOfArray": [ "$stats._id", "$$date" ] } },
                                in: { 
                                    $cond: {
                                        if: { $ne: [ "$$dateIndex", -1 ] },
                                        then: { $arrayElemAt: [ "$stats", "$$dateIndex" ] },
                                        else: { _id: "$$date", date: "$$date", views: 0 }
                                    } 
                                }
                            }
                        }
                    }
                }
            }
        },
        {
            $unwind: "$stats"
        },
        {
            $replaceRoot: {
                newRoot: "$stats"
            }
        }
    ]).exec((err, result) => ...)
    

    당신은 간단한 루프를 사용하여 응용 프로그램 논리의 날짜의 정적 목록을 생성 할 수 있습니다. 나는 잘 ($ 범위를 사용)하지만이 집계 파이프 라인을 복잡 하듯이 그 MongoDB가 가능하다고 믿습니다. 내가 그와 당신이 경우에있는 거 잘 알거나하여 MongoDB의 날짜의 배열을 생성하려고 시도하자.

  2. ==============================

    2.귀하의 질문은 2014 년 게시 유사합니다.

    귀하의 질문은 2014 년 게시 유사합니다.

    해당 게시물에서 제공되는 모든 답변은 유효하며 당신이 당신의 응용 프로그램 코드에없는 일을 생성 할 수있는 경우가 훨씬 더 간단 할 것이다.

    당신이 MongoDB의 솔루션을 요구하고 많은 2014로 변경되었습니다 때문에 나는 당신이 3.6 버전으로 사용할 수있는 새로운 통합 파이프 라인을 만들었습니다.

    ProductView.aggregate([
       -- convert the string date into date type for date calcualtions. can avoid this step if you can store the date as date type in collection
        {"$addFields":{"createdAt":{"$dateFromString":{"dateString":"$createdAt"}}}},
          -- strip the time part so we can add whole milliseconds from epoch to calculate next day
        {"$project":{
            "day":{"$dateFromParts":{"year":{"$year":"$createdAt"},"month":{"$month":"$createdAt"},"day":{"$dayOfMonth":"$createdAt"}}}
        }},
          -- generate two sets of data, one that has count by day, other that has unique days, min day and max day
        {"$facet":{
            "daycounts":[{"$group":{"_id":"$day","count":{"$sum":1}}}],
            "maxmindays":[
              {"$group":{
                 "_id":null,
                 "days":{"$addToSet":"$day"},
                 "minday":{"$min":{"$divide":[{"$subtract":["$day",new Date("1-1-1970")]},1000]}},
                 "maxday":{"$max":{"$divide":[{"$subtract":["$day",new Date("1-1-1970")]},1000]}}
               }}
            ]
        }},
        {"$project":{
            "data":{
              "$let":{
                "vars":{"maxminday":{"$arrayElemAt":["$maxmindays",0]}},
                "in":{
                  -- $range - iterate from min date to max date one day at a time
                  "$map":{
                    "input":{"$range":["$$maxminday.minday",{"$add": ["$$maxminday.maxday", 60*60*24]},60*60*24]},
                    "as":"r",
                    "in": {
                  -- convert back to milliseconds to get the day
                      "$let":{
                        "vars":{"current":{"$add": [new Date(0), {"$multiply":["$$r", 1000 ]}]}},
                        "in":{
                  -- check if the day is in the collection, if yes lookup view inside the daycount facet to get the matching count, else set the view to zero
                          "$cond":[
                            {"$in":["$$current","$$maxminday.days"]},
                            {
                              "date":{"$substr":["$$current",0,10]},
                              "views":{"$let":{"vars":{"daycount":{"$arrayElemAt":["$daycounts",{"$indexOfArray":["$daycounts._id","$$current"]}]}},"in":"$$daycount.count"}}
                            },
                            {"date":{"$substr":["$$current",0,10]},"views":0}
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
        }},
        -- flatten the array of data
        {"$unwind":"$data"},
        -- promote the data to top
        {"$replaceRoot":{newRoot:"$data"}}
    ])
    
  3. ==============================

    3.일부 자바 스크립트와 통합 트릭.

    일부 자바 스크립트와 통합 트릭.

    먼저 제공된 날짜 범위 사이의 날짜를 찾을 필요가있다.

    function getDates(startDate, stopDate) {
      var dateArray = []
      var currentDate = moment(startDate)
      var stopDate = moment(stopDate)
      while (currentDate <= stopDate) {
        dateArray.push(moment(currentDate).format('YYYY-MM-DD'))
        currentDate = moment(currentDate).add(1, 'days')
      }
      return dateArray
    }
    
    const dummyArray = getDates('2018-09-01', '2018-09-05')
    dummyArray = [ "2018-09-01", "2018-09-02", "2018-09-03", "2018-09-04", "2018-09-05" ]
    

    이제와 데이터베이스에서 사용할 수없는 날짜를 찾을 수 있습니다 집계 아래.

    db.collection.aggregate([
      { "$match": { productId } },
      { "$group": {
        "_id": { "$substr": ["$createdAt", 0, 10] },
        "count": { "$sum": 1 },
        "time": { "$avg": "$createdAt" },
      }},
      { "$sort": { "_id": 1 } },
      { "$project": { "date": "$_id", "views": "$count" }},
      { "$group": { "_id": null, "data": { "$push": "$$ROOT" }}},
      { "$project": {
        "data": {
          "$map": {
            "input": dummyArray,
            "in": {
              "k": "$$this",
              "v": { "$cond": [{ "$in": ["$$this", "$data.date" ] }, 1, 0 ] }
            }
          }
        }
      }},
      { "$unwind": "$data" },
      { "$group": { "_id": "$data.k", "count": { "$sum": "$data.v" }}}
    ])
    

    및 출력 할 것이다

    [
        { date: '2018-09-01', views: 1 },
        { date: '2018-09-02', views: 3 },
        { date: '2018-09-03', views: 0 },
        { date: '2018-09-04', views: 2 },
        { date: '2018-09-05', views: 5 }
    ]
    
  4. ==============================

    4.나는 그것이 하나 또는 두 개의 그리고 프로세스 문서의 수가 적을 경우 누락 된 날짜 클라이언트 측을 추가 건의 할 것입니다.

    나는 그것이 하나 또는 두 개의 그리고 프로세스 문서의 수가 적을 경우 누락 된 날짜 클라이언트 측을 추가 건의 할 것입니다.

    그 존재는 다음과 같은 파이프 라인은 MongoDB를 4.0 이상에서 작동하지만 약간의 노력으로, 우리가 3.6에서 작동 할 수 있습니다 말했다.

    [
        {
            $group: {
                _id: null,
                dates: {
                    $push: {
                        $let: {
                            vars: {
                                date: {
                                    $dateToParts: {
                                        date: {
                                            $toDate: "$createdAt"
                                        }
                                    }
                                }
                            },
                            in: {
                                $toDouble: {
                                    $dateFromParts: {
                                        year: "$$date.year",
                                        month: "$$date.month",
                                        day: "$$date.day"
                                    }
                                }
                            }
                        }
                    }
                }
            }
        },
        {
            $addFields: {
                startDate: {
                    $divide: [
                        {
                            $min: "$dates"
                        },
                        1000
                    ]
                },
                endDate: {
                    $divide: [
                        {
                            "$max": "$dates"
                        },
                        1000
                    ]
                }
            }
        },
        {
            $addFields: {
                dates: {
                    $map: {
                        input: {
                            $concatArrays: [
                                "$dates",
                                {
                                    $setDifference: [
                                        {
                                            $map: {
                                                input: {
                                                    $range: [
                                                        {
                                                            $toDouble: "$startDate"
                                                        },
                                                        {
                                                            $toDouble: "$endDate"
                                                        },
                                                        24*60*60
                                                    ]
                                                },
                                                in: {
                                                    $multiply: [
                                                        "$$this",
                                                        1000
                                                    ]
                                                }
                                            }
                                        },
                                        "$dates"
                                    ]
                                }
                            ]
                        },
                        in: {
                            $toDate: "$$this"
                        }
                    }
                }
            }
        },
        {
            "$unwind": "$dates"
        },
        {
            "$group": {
                _id: "$dates",
                views: {
                    $sum: 1
                }
            }
        },
        {
            "$sort": {
                _id: -1
            }
        }
    ]
    
  5. from https://stackoverflow.com/questions/52235027/fill-missing-dates-in-records by cc-by-sa and MIT license