복붙노트

[MONGODB] MongoDB의 : 집계 프레임 워크 : 그룹화 ID 당 마지막 날짜가 문서를 가져 오기

MONGODB

MongoDB의 : 집계 프레임 워크 : 그룹화 ID 당 마지막 날짜가 문서를 가져 오기

나는 다른 모든 필드와 각 국의 마지막 문서를 얻으려면 :

{
        "_id" : ObjectId("535f5d074f075c37fff4cc74"),
        "station" : "OR",
        "t" : 86,
        "dt" : ISODate("2014-04-29T08:02:57.165Z")
}
{
        "_id" : ObjectId("535f5d114f075c37fff4cc75"),
        "station" : "OR",
        "t" : 82,
        "dt" : ISODate("2014-04-29T08:02:57.165Z")
}
{
        "_id" : ObjectId("535f5d364f075c37fff4cc76"),
        "station" : "WA",
        "t" : 79,
        "dt" : ISODate("2014-04-29T08:02:57.165Z")
}

나는 역마다 최신 DT에 대한 t 및 스테이션이 필요합니다. 집계 프레임 워크 :

db.temperature.aggregate([{$sort:{"dt":1}},{$group:{"_id":"$station", result:{$last:"$dt"}, t:{$last:"$t"}}}])

보고

{
        "result" : [
                {
                        "_id" : "WA",
                        "result" : ISODate("2014-04-29T08:02:57.165Z"),
                        "t" : 79
                },
                {
                        "_id" : "OR",
                        "result" : ISODate("2014-04-29T08:02:57.165Z"),
                        "t" : 82
                }
        ],
        "ok" : 1
}

이 그렇게 할 수있는 가장 효율적인 방법이 있나요?

감사

해결법

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

    1.직접 귀하의 질문에 대답하기 위해, 네, 그것은 가장 효율적인 방법입니다. 그러나 나는 우리가 왜 이렇게되는지를 명확히해야한다고 생각합니까.

    직접 귀하의 질문에 대답하기 위해, 네, 그것은 가장 효율적인 방법입니다. 그러나 나는 우리가 왜 이렇게되는지를 명확히해야한다고 생각합니까.

    AS는 한 가지 사람들이 $ 조별 리그와 당신이 없는지에 있음을 해보고 싶어요, 그래서 그들은는 "타임 스탬프"값이보고있는에 전달하기 전에 결과를 "정렬"되어보고있는 대안으로 제시되었다 "타임 스탬프"순서이므로 이에 형태 :

    db.temperature.aggregate([
        { "$sort": { "station": 1, "dt": -1 } },
        { "$group": {
            "_id": "$station", 
            "result": { "$first":"$dt"}, "t": {"$first":"$t"} 
        }}
    ])
    

    언급 한 바와 같이 그리고 당신은 물론 인덱스 순서대로 정렬 효율적으로 만들기 위해 반영 할 것입니다 :

    그러나, 이것은 실제 지점입니다. 다른 사람들이 간과하고있는 것 같다 (그렇지 않으면 그렇게 자신을위한 것은)이 데이터의 모든 가능성이 추가로 각 측정 값이 기록되는 점에서, 시간 순서에 이미 삽입되고 있다는 점이다.

    이의 아름다움이 (가) _id 필드가 이미 (a는 기본 ObjectId가와)입니다 그래서 그 자체를 수행으로 "타임 스탬프"순서는 실제로 시간 값을 포함하고이 가능한 성명을 :

    db.temperature.aggregate([
        { "$group": {
            "_id": "$station", 
            "result": { "$last":"$dt"}, "t": {"$last":"$t"} 
        }}
    ])
    

    그리고 그것은 빠릅니다. 왜? 그럼 당신은 인덱스 (호출에 추가 코드) 당신은 또한 "로드"를 필요로하지 않는 문서에 추가하여 인덱스를 선택할 필요가 없습니다.

    우리는 이미 문서가 순서대로 $ 마지막 경계를 완벽하게 유효 있도록 (_id가) 알고있다. 어쨌든 모든 것을 스캔, 두 날짜 사이에 동등하게 유효로 _id 값에 당신은 또한 할 수 있었다 "범위"쿼리된다.

    여기에서 말할 수있는 유일한 것은 "현실 세계"의 사용에, 그냥 "첫 번째"와 "마지막"점점 반대로 축적 이런 종류의 일을 날짜 범위 사이에 $ 일치로 더 실용적 일 수 있다는 것입니다 _id 값이 "범위"또는 실제 사용에서 비슷한 정의합니다.

    그렇다면 이것의 증거는 무엇입니까? 난 그냥 몇 가지 샘플 데이터를 생성하여 그렇게 잘 그것은 매우 쉽게 재현하는 것입니다 :

    var stations = [ 
        "AL", "AK", "AZ", "AR", "CA", "CO", "CT", "DE", "FL",
        "GA", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA",
        "ME", "MD", "MA", "MI", "MN", "MS", "MO", "MT", "NE",
        "NV", "NH", "NJ", "NM", "NY", "NC", "ND", "OH", "OK",
        "OR", "PA", "RI", "SC", "SD", "TN", "TX", "UT", "VT",
        "VA", "WA", "WV", "WI", "WY"
    ];
    
    
    for ( i=0; i<200000; i++ ) {
    
        var station = stations[Math.floor(Math.random()*stations.length)];
        var t = Math.floor(Math.random() * ( 96 - 50 + 1 )) +50;
        dt = new Date();
    
        db.temperatures.insert({
            station: station,
            t: t,
            dt: dt
        });
    
    }
    

    명령문의 각 양식을 실행하는 내 하드웨어에 (별, 그러나 확실하게 적절하지 spinny 디스크와 8 기가 바이트 노트북) 분명히 인덱스와 정렬 (정렬 문 같은 인덱스에 동일한 키)를 사용하여 버전과 주목할만한 일시 정지를 보여줍니다. 그것은 단지 작은 일시 정지하지만, 차이는 통지 충분히 중요하다.

    심지어 $ 일종의 인해 인덱스의 존재에게 최적화되어 있지만 당신은 그 차이를 볼 수있는 설명 출력을보고 (버전 2.6까지, 또는 실제로 문서화 된 것은 아니지만 2.4.9에있다), 시간 촬영 이 나타납니다 다음 인덱스 항목을로드 인덱스 선택으로 할 수 있습니다. 는 "적용"인덱스 쿼리에 대한 모든 필드를 포함하면 차이가 없습니다.

    또한, 기록, 순수 일자 색인만을 날짜 값 정렬에 동일한 결과를 제공한다. 아마도 약간 빠르게,하지만 여전히 느린 정렬하지 않고 자연 인덱스 형태보다.

    그러니 당신이 행복 할 수있는 첫 번째와 마지막 _id 값에 대한 "범위"로, 다음은 삽입 순서에 자연 인덱스를 사용하여이 작업을 수행하는 가장 효율적인 방법은 실제로 사실이다. 귀하의 실제 주행이 당신 여부에 대한 실용적인 여부에 대한 다를 수 있으며 단순히 인덱스를 구현하기 위해 더 편리되는 및 날짜에 정렬 끝낼 수 있습니다.

    하지만 연속 쿼리에서 해당 정보를 사실 저장소에 당신이 할 수있는 귀하의 결과와 함께 값을 얻을 사용하기 위해, 귀하의 질의에 아마도 한 팅겨 "마지막"_id보다 큰 _id 범위 또는를 사용하여 행복했다 경우 :

    db.temperature.aggregate([
        // Get documents "greater than" the "highest" _id value found last time
        { "$match": {
            "_id": { "$gt":  ObjectId("536076603e70a99790b7845d") }
        }},
    
        // Do the grouping with addition of the returned field
        { "$group": {
            "_id": "$station", 
            "result": { "$last":"$dt"},
            "t": {"$last":"$t"},
            "lastDoc": { "$last": "$_id" } 
        }}
    ])
    

    당신이 실제로 있다면 그리고 그와 같은 결과가 다음 결과에서 ObjectId가의 최대 값을 결정하고 다음 쿼리에서 사용할 수 "에 다음과 같은".

    어쨌든, 그 재미 재생을 가지고 있지만 다시 예, 쿼리가 가장 빠른 방법이라고이 경우이다.

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

    2.인덱스는 당신이 정말로 필요로하는 모두이다 :

    인덱스는 당신이 정말로 필요로하는 모두이다 :

    db.temperature.ensureIndex({ 'station': 1, 'dt': 1 })
    for s in db.temperature.distinct('station'):
        db.temperature.find({ station: s }).sort({ dt : -1 }).limit(1)
    

    물론 구문은 실제로 당신의 언어에 대한 유효 무엇이든 사용.

    편집 : 당신이 올바른지이 초래 스테이션 당 왕복, 그리고 그것의 큰 몇 스테이션, 그리고 당신은 여전히 ​​역에 복합 인덱스를 원하는가 1000에 대한 좋은 + DT하지만, 그리고 걸릴하지 않도록 같은 루프 내림차순 정렬의 장점 :

    db.temperature.aggregate([
        { $sort: { station: 1, dt: -1 } },
        { $group: { _id: "$station", result: {$first:"$dt"}, t: {$first:"$t"} } }
    ])
    
  3. ==============================

    3.지금까지 집계 쿼리로 당신이 게시 한 것처럼, 나는 당신이 DT에 인덱스를 가지고 특정 만들 것입니다 :

    지금까지 집계 쿼리로 당신이 게시 한 것처럼, 나는 당신이 DT에 인덱스를 가지고 특정 만들 것입니다 :

    db.temperature.ensureIndex({'dt': 1 })
    

    이 집계 파이프 라인의 시작 부분에 $ 정렬이 가능한 한 효율적이라고 특정 할 것이다.

    이 루프의 쿼리 대,이 데이터를 얻을 수있는 가장 효율적인 방법입니다 여부에 관해서는, 가능성이 당신이 얼마나 많은 데이터 포인트의 기능이 될 것입니다. 아마도 "방송국의 수천"및 데이터 포인트의 수천 수백 초, 나는 집계 방식이 더 빠를 것이라고 생각 것입니다.

    당신은 더 많은 데이터를 추가하지만, 문제는 집계 쿼리가 모든 문서를 계속해서 터치하는 것입니다. 당신이 수백만 이상의 문서까지 확장 이것은 점점 더 비싼 얻을 것이다. 이 경우에 대한 하나의 접근 방식은 일종의 문서의 총 개수가 고려되고 제한 할 권리 $ 후 $ 제한을 추가하는 것입니다. 즉 비트 해키 및 부정확입니다하지만 필요에 액세스 할 수 있다는 문서의 총 수를 제한하는 것이 도움이 될 것이다.

  4. from https://stackoverflow.com/questions/23360551/mongodb-aggregation-framework-get-last-dated-document-per-grouping-id by cc-by-sa and MIT license