>source

다음과 같은 문서가 많이 있습니다 :

{_id: ObjectId("5adc864eaaf408a2b6e325f7"), employee: ObjectId("5adc864eaa3c92b3c4c252c1"), end: { day: "2018-04-22 12:06:46.623" }, start: { day: "2018-04-22 11:06:46.623" }, date: "2018-04-22 11:06:46.623"}
{_id: ObjectId("5adc864eaaf408a2b6e325c8"),employee: ObjectId("5adc864eaa3c92b3c4c252c1"), end: { day: "2018-04-22 10:06:46.623" }, start: { day: "2018-04-22 8:06:46.623" }, date: "2018-04-22 11:06:46.623"}
{_id: ObjectId("5adc864eaaf408a2b6e325f6"),employee: ObjectId("5adc864eaa3c92b3c4c252c1"), end: { day: "2018-05-22 12:06:46.623" }, start: { day: "2018-04-22 11:06:46.623" }, date: "2018-05-22 11:06:46.623"}
{_id: ObjectId("5adc864eaaf408a2b6e325c4"),employee: ObjectId("5adc864eaa3c92b3c4c252c1"), end: { day: "2018-05-22 10:06:46.623" }, start: { day: "2018-05-22 8:06:46.623" }, date: "2018-05-22 11:06:46.623"}

하루 동안 각 직원의 활동을 나타냅니다.

각 활동의 시작 날짜 "start.day"와 종료 날짜 "end.day"사이의 각 활동 시간을 사용하여 하루에 근무한 시간을 계산하고 하나의 모든 활동을 합산해야합니다. 하루.

redact, sum, substrac과 같은 집계를 시도하지만 이것을 달성하기 위해 어떤 논리를 사용해야하는지 알 수 없습니다.


  • 답변 # 1

    그래서 가장 먼저 다루어야 할 것은 현재의 "날짜"가 모두 "문자열"이며, 이것은 실제로 도움이되지 않습니다. 기본적으로 집계 작업에 필요한 것이기 때문에 모든 것을 BSON Date로 변환하는 것이 더 좋습니다.

    두 번째 요점은 한 간격 내에서 "일일"의 총계를 얻는 것이 쉽지 않다는 것입니다. 실제로 MongoDB에서 이런 식을 수행하려면 몇 가지 식을 던져야합니다.

    db.collection.aggregate([
      { "$addFields": {
        "start": { "$toDate": "$start.day" },
        "end": { "$toDate": "$end.day" },
        "date": { "$toDate": "$date" },
        "dayworking": {
          "$map": {
            "input": {
              "$range": [
                0,
                { "$ceil": {
                  "$divide": [
                    { "$subtract": [
                      { "$toDate": "$end.day" },
                      { "$toDate": "$start.day" }
                    ]},
                    1000 * 60 * 60 * 24
                  ]
                }}
              ]
            },
            "in": {
              "$toDate": {
                "$add": [
                  { "$multiply": ["$$this", 1000 * 60 * 60 * 24 ] },
                  { "$subtract": [
                    { "$toLong": { "$toDate": "$start.day" } },
                    { "$mod": [ { "$toLong": { "$toDate": "$start.day" } }, 1000 * 60 * 60 * 24 ] }
                  ]}
                ]
              }
            }
          }
        }
      }},
      { "$unwind": "$dayworking" },
      { "$group": {
        "_id": {
          "employee": "$employee",
          "day": "$dayworking"
        },
        "hours": {
          "$sum": {
            "$floor": {
              "$divide": [
                { "$switch": {
                  "branches": [
                    { 
                      "case": {
                        "$and": [
                          { "$lt": [ "$dayworking", "$start" ] },
                          { "$gt": [
                            { "$add": [ "$dayworking", 1000 * 60 * 60 * 24 ] },
                            "$end"
                          ]}
                        ]
                      },
                      "then": { "$subtract": [ "$end", "$start" ] }
                    },
                    {
                      "case": {
                        "$lt": [
                          "$end",
                          { "$add": [ "$dayworking", 1000 * 60 * 60 * 24 ] }
                        ]
                      },
                      "then": {
                        "$subtract": [ "$end", "$dayworking" ]
                      }
                    },
                    {
                      "case": { "$lt": [ "$dayworking", "$start" ] },
                      "then": {
                        "$subtract": [
                          { "$add": [ "$dayworking", 1000 * 60 * 60 * 24 ] },
                          "$start"
                        ]
                      }
                    }
                  ],
                  "default": 1000 * 60 * 60 * 24
                }},
                1000 * 60 * 60
              ]
            }
          }
        }
      }},
      { "$sort": { "_id": 1 } }
    ])
    
    

    기본적으로 매일 시작 및 종료 간격 내에서 (간결하게 잘라 내기)로 반환합니다 :

    {
            "_id" : {
                    "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
                    "day" : ISODate("2018-04-22T00:00:00Z")
            },
            "hours" : 15
    }
    {
            "_id" : {
                    "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
                    "day" : ISODate("2018-04-23T00:00:00Z")
            },
            "hours" : 24
    }
    .... each day in between ...
    {
            "_id" : {
                    "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
                    "day" : ISODate("2018-05-21T00:00:00Z")
            },
            "hours" : 24
    }
    {
            "_id" : {
                    "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
                    "day" : ISODate("2018-05-22T00:00:00Z")
            },
            "hours" : 14
    }
    
    

    어느 날에는 24 시간이 할당되고 다른 시간에는 일부 시간이 할당됩니다. 귀하의 예에서와 같이 첫날에는 다음과 같이 생성되는 데이터가 있습니다.

    {
            "_id" : ObjectId("5adc864eaaf408a2b6e325f7"),
            "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
            "end" : ISODate("2018-04-22T12:06:46.623Z"),
            "start" : ISODate("2018-04-22T11:06:46.623Z"),
            "date" : ISODate("2018-04-22T11:06:46.623Z"),
            "dayending" : ISODate("2018-04-22T00:00:00Z"),
            "hours" : 1
    }
    {
            "_id" : ObjectId("5adc864eaaf408a2b6e325c8"),
            "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
            "end" : ISODate("2018-04-22T10:06:46.623Z"),
            "start" : ISODate("2018-04-22T08:06:46.623Z"),
            "date" : ISODate("2018-04-22T11:06:46.623Z"),
            "dayending" : ISODate("2018-04-22T00:00:00Z"),
            "hours" : 2
    }
    {
            "_id" : ObjectId("5adc864eaaf408a2b6e325f6"),
            "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
            "end" : ISODate("2018-05-22T12:06:46.623Z"),
            "start" : ISODate("2018-04-22T11:06:46.623Z"),
            "date" : ISODate("2018-05-22T11:06:46.623Z"),
            "dayending" : ISODate("2018-04-22T00:00:00Z"),
            "hours" : 12
    }
    
    

    두 개의 유일한 항목과 하나가 12 시간이고 나머지는 15 시간과 마지막 날입니다.

    {
            "_id" : ObjectId("5adc864eaaf408a2b6e325f6"),
            "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
            "end" : ISODate("2018-05-22T12:06:46.623Z"),
            "start" : ISODate("2018-04-22T11:06:46.623Z"),
            "date" : ISODate("2018-05-22T11:06:46.623Z"),
            "dayending" : ISODate("2018-05-22T00:00:00Z"),
            "hours" : 12
    }
    {
            "_id" : ObjectId("5adc864eaaf408a2b6e325c4"),
            "employee" : ObjectId("5adc864eaa3c92b3c4c252c1"),
            "end" : ISODate("2018-05-22T10:06:46.623Z"),
            "start" : ISODate("2018-05-22T08:06:46.623Z"),
            "date" : ISODate("2018-05-22T11:06:46.623Z"),
            "dayending" : ISODate("2018-05-22T00:00:00Z"),
            "hours" : 2
    }
    
    

    2 시간의 입장과 총 12 시간의 나머지 14 시간이 있습니다.

    설명

    날짜 전환 및 수학

    명백한 "날짜 변환"외부에서 수행해야 할 두 가지 주요 사항이 있다고 설명합니다. 그건 그렇고 $toDate 와 함께 할 수 있습니다  MongoDB 4.0 또는 $dateFromString 를 통해  MongoDB 3.6이있는 경우 후자의 경우"date math"

    에 다른 방법을 적용해야합니다.

    그룹의 이전 MongoDB 버전에서"date math"을 처리하는 자세한 예는 이전 버전이 있고 $dateFromString 인 경우 MongoDb에서 15 분 시간 간격으로 결과가 나타납니다.  또는 데이터를 직접 변환해야합니다.

    범위 내 날짜 투사

    이 작업을 수행하는 다음 주요 부분은 기본적으로 문서가 소스 문서 내부에 적용되는 날짜 배열을 구성해야한다는 것입니다. 이것이 $range 입니다표현은 시작 값 ( 0 )을 취함으로써  이 경우) 및 종료 값은 여기서"사이의 일 수"start 에 적용됩니다.  그리고 end  날짜 값.

    그 차이는 $subtract 에서 반환됩니다  밀리 초 단위로, $divide  전체 정수를 얻기 위해 하루에 일정한 밀리 초 이상 사용됩니다. $ceil 사용  여기에 반올림하지만, 이것은 쉽게 $mod 일 수 있습니다   $subtract 와 함께  이전 버전에서는 해당 연산자를 사용할 수 없습니다.

    그 시점에서 $range  실제로 정수 값의 배열을 생성했기 때문에 $map  데이터가 적용되는 "일"을 나타내는 실제 BSON Date 객체로 변환하기 위해 해당 배열에 적용됩니다. 다시 말하지만 배열 인덱스 값 (물론 +1)의추가를 원래반올림시작 날짜에 적용하는 것은"date math"입니다.

    시간 계산

    이제 이전 단계의 날짜 배열과 문서 값을 사용 가능한 BSON 날짜로 다시 포맷하는 다른 방법으로이 "배열"내용을 각 start 와 실제로 비교해야합니다.  그리고 end  해당 날짜에 몇 시간이 적용되었는지 결정합니다.

    첫 번째 기본 사례와 우리가 실제로배열을 생성한 이유는 $unwind 를 사용하는 것입니다. 간격 내에서 매일 발생하는 결과 문서를 효과적으로복사합니다. 그것은 당신이 $group 전에 발생 해야하는 작지만 중요한 단계입니다  실제로 계산합니다. 결론은 $group 입니다  실제로 출력을 위해"기본 키"의 일부로 해당 값을 사용하고 다른 날짜 정보와 비교합니다.

    물론실제 작품은 모두 $switch 에서 이루어지고 있습니다.  성명서, 다시 말해서 $cond"중첩"사용이전 버전에서. 여기서는 기본적으로가능한 사례를 분석하고 물론 "하루 종일"에 대한 기본 대체를 원합니다.

    사례는 기본적으로 :

    현재"그룹의 날"start 보다 작은 곳  그리고"다음 날"end 보다 큽니다.  날짜, 차이를 빼면됩니다.

    위가 아니라면 end  날짜가 그룹화를위한"다음 날"보다 작고 현재 end 에서"그룹화 일"을 뺍니다.   end 까지 그 시작 시간을 얻는 날짜  시간.

    위의 경우"그룹의 날"start 보다 작은 경우  (다른 end 없이  )에서 근무한 시간은"다음 날"에서 start 를 뺀 시간입니다.   start 와의 차이점  오늘의 마지막 날까지.

    사실이 아닌 경우 기본적으로 "전체 날짜"가 표시되며이 예에서는 24 시간으로 표시됩니다.

    신청할 다른 근무 시간이있는 경우, 오전 8시 시작시 "오늘 시작"+8 시간을 조정하면됩니다. 같은 것은 기본적으로 오후 5시 마감에 +17과 같은 것을 추가하여 "하루"에 적용됩니다. 그러나 구현할 논리의 기본 원칙은 여전히 ​​위에 표시된 것과 동일합니다.

    와이즈 비즈 와이즈 비즈 나는 MongoDB 3.0 또는 3.2에서 온 것으로 생각합니다. 어쨌든 현재로서는 3.4 이전 버전의 MongoDB를 실행해서는 안됩니다.

         

    이전 버전을 보유한 경우 그룹에 대한 자세한 내용이 있으며 시작 및 종료 범위를 넘어 여러 쿼리를 사용하여유사한프로세스를 표시하고 심지어

    NOTE: The main constraint here is  매우 비슷한 $range 외에도  예를 들어.

    mapReduce()

관련 자료

  • 이전 swift - CaseIterable, RawRepresentable을 준수하는 관련 값을 가진 열거 형
  • 다음 sql - nvachar의 시간과 날짜