2012-04-16 26 views
5

(1) Tôi đã sau đây bổ sung vào bộ sưu tập:

{ "_id" : 1, "hitsPerOneSecond" : [ 2, 3, 5, 4, 1, 2, 3, 4, 1, 2 ], "startTime" : ISODate("2012-04-07T10:41:33.380Z"), "returnCodeHits" : { "300" : 5, "200" : 12 }, "xxxServer" : "xxx:8100", "statsSummarizedToSeconds" : 10, "pathStats_xxx_api_get_version" : [ 0.2280779683225852, 0.030849283020361273, 0.9947690473370484 ], "pathStats_xxx_api_get_response" : [ 1.2163705612407407, 1.0602539963494662, 1.4853219936411421 ], "type" : "xxxType", "startTimeStr" : "07-04-2012:10AM" } 

{ "_id" : 2, "hitsPerOneSecond" : [ 2, 3, 5, 4, 1, 2, 3, 4, 1, 2 ], "startTime" : ISODate("2012-04-07T10:41:43.380Z"), "returnCodeHits" : { "300" : 5, "200" : 12 }, "xxxServer" : "xxx:8100", "statsSummarizedToSeconds" : 10, "pathStats_xxx_api_get_version" : [ 0.2280779683225852, 0.030849283020361273, 0.9947690473370484 ], "pathStats_xxx_api_get_response" : [ 1.2163705612407407, 1.0602539963494662, 1.4853219936411421 ], "type" : "xxxType", "startTimeStr" : "07-04-2012:10AM" } 

(2) Khi thực hiện tập hợp sau:

db.newStats.aggregate({$unwind: "$hitsPerOneSecond"},{$group:{_id:"$startTimeStr", totalHits: {$sum: "$hitsPerOneSecond"}, totalHitsCount: {$sum: 1}, avgHit: {$avg: "$hitsPerOneSecond"}, minHit: {$min:"$hitsPerOneSecond"}, maxHit:{$max: "$hitsPerOneSecond"}}}); 

(3) Kết quả đi ra một cách chính xác:

{ 
"result" : [ 
    { 
     "_id" : "07-04-2012:10AM", 
     "totalHits" : 54, 
     "totalHitsCount" : 20, 
     "avgHit" : 2.7, 
     "minHit" : 1, 
     "maxHit" : 5 
    } 
], 
"ok" : 1 

}

(4) Tuy nhiên, tôi cần phải thực hiện thư giãn trên 'pathStats_xxx_api_get_response' (từ bộ sưu tập) trong cùng một tập hợp ở trên để tôi có thể có totalResponses, totalResponsesCount, avgResponse, minResponse và maxResponse đầu ra trong cùng một kết quả ở trên. Do đó, kết quả của tôi nên tìm một cái gì đó như thế này:

{ 
"result" : [ 
    { 
     "_id" : "07-04-2012:10AM", 
     "totalHits" : 54, 
     "totalHitsCount" : 20, 
     "avgHit" : 2.7, 
     "minHit" : 1, 
     "maxHit" : 5, 
        "totalResponses" : ?? 
        "totalResponsesCount": ?? 
     "avgResponse" : 2.7, 
     "minResponse" : 1, 
     "maxResponse" : 5 
    } 
], 
"ok" : 1 

}

Không chắc chắn chính xác làm thế nào để thêm $ thư giãn trong tập hợp giống như tôi gần như có!

Trả lời

2

Có thể giải pháp đơn giản nhất là thực hiện điều này với hai thao tác tổng hợp riêng biệt và kết hợp các kết quả trong ứng dụng của bạn.

Ngoài ra, bạn có thể làm điều này với một bản đồ Giảm hoạt động:

Bản đồ sau và giảm chức năng cần cung cấp các kết quả mà bạn đang tìm kiếm:

var map = function() { 
    var totalHits = this.hitsPerOneSecond.map(function(a,b){return a+b;}); 
    var totalHitsCount = this.hitsPerOneSecond.length; 
    var avgHit = totalHits/totalHitsCount; 
    var minHit = Math.min.apply(Math, this.hitsPerOneSecond); 
    var maxHit = Math.max.apply(Math, this.hitsPerOneSecond); 
    var totalResponses = pathStats_xxx_api_get_response.map(function(a,b){return a+b;}); 
    var totalResponsesCount = this.pathStats_xxx_api_get_response.length; 
    var avgResponse = totalResponses/totalResponsesCount; 
    var minResponse = Math.min.apply(Math, this.pathStats_xxx_api_get_response); 
    var maxResponse = Math.max.apply(Math, this.pathStats_xxx_api_get_response); 
    emit(this.startTimeStr, { 
    "totalHits": totalHits, 
    "totalHitsCount": totalHitsCount, 
    "avgHit": avgHit, 
    "minHit": minHit, 
    "maxHit": maxHit, 
    "totalResponses": totalResponses, 
    "totalResponsesCount": totalResponsesCount, 
    "avgResponse": avgResponse, 
    "maxResponse": maxResponse, 
    "minResponse": minResponse 
    }) 
} 

var reduce = function(key, values) { 
    var output = { 
    "totalHits": 0, 
    "totalHitsCount": 0, 
    "avgHit": 0, 
    "minHit": null, 
    "maxHit": null, 
    "totalResponses": 0, 
    "totalResponsesCount": 0, 
    "avgResponse": 0, 
    "maxResponse": null, 
    "minResponse": null 
    }; 
    values.forEach(function(v) { 
    output.totalHits += v.totalHits; 
    output.totalHitsCount += v.totalHitsCount; 
    output.avgHit = output.totalHits/output.totalHitsCount; 
    if (output.minHit == null) { 
     output.minHit = v.minHit; 
    } else { 
     if (v.minHit < output.minHit) { 
     output.minHit = v.minHit 
     } 
    } 
    if (output.maxHit == null) { 
     output.maxHit = v.maxHit; 
    } else { 
     if (v.maxHit > output.maxHit) { 
     output.maxHit = v.maxHit 
     } 
    } 

    output.totalResponses += v.totalResponses; 
    output.totalResponsesCount += v.totalResponsesCount; 
    output.avgResponse = output.totalResponses/output.totalResponsesCount; 
    if (output.minResponse == null) { 
     output.minResponse = v.minResponse; 
    } else { 
     if (v.minResponse < output.minResponse) { 
     output.minResponse = v.minResponse 
     } 
    } 
    if (output.maxResponse == null) { 
     output.maxResponse = v.maxResponse; 
    } else { 
     if (v.maxResponse > output.maxResponse) { 
     output.maxResponse = v.maxResponse 
     } 
    } 
    }); 
    return output; 
} 

> db.newStats.mapReduce(map, reduce, {out:{inline:1}}) 
{ 
    "results" : [ 
     { 
      "_id" : "07-04-2012:10AM", 
      "value" : { 
       "totalHits" : 54, 
       "totalHitsCount" : 20, 
       "avgHit" : 2.7, 
       "minHit" : 1, 
       "maxHit" : 5, 
       "totalResponses" : 7.523893102462698, 
       "totalResponsesCount" : 6, 
       "avgResponse" : 1.253982183743783, 
       "maxResponse" : 1.4853219936411421, 
       "minResponse" : 1.0602539963494662 
      } 
     } 
    ], 
    "timeMillis" : 0, 
    "counts" : { 
     "input" : 2, 
     "emit" : 2, 
     "reduce" : 1, 
     "output" : 1 
    }, 
    "ok" : 1, 
} 
> 

Nếu bạn không quen với MapReduce, các tài liệu có thể được tìm thấy ở đây: http://www.mongodb.org/display/DOCS/MapReduce

Bên cạnh đó, có một số đồ tốt Giảm ví dụ trong MongoDB Cookbook: http://cookbook.mongodb.org/

Phần "Extras" của sách nấu ăn "Tìm giá trị tối đa và tối thiểu với tài liệu được phiên bản" http://cookbook.mongodb.org/patterns/finding_max_and_min/ chứa hướng dẫn từng bước tốt về thao tác Giảm bản đồ, giải thích cách thực hiện các chức năng.

Hy vọng điều này sẽ giúp bạn đạt được kết quả mong muốn. Nếu bạn có thể tìm ra cách để thực hiện điều này bằng một thao tác tổng hợp, vui lòng chia sẻ giải pháp của bạn để Cộng đồng có thể đạt được lợi ích của trải nghiệm của bạn. Cảm ơn.

Dưới đây là một số lưu ý trên Bản đồ thu nhỏ, để phản hồi nhận xét của bạn:

MapReduce thực thi JavaScript trên máy chủ. Kết quả là, bạn có thể thấy rằng hiệu suất bị ảnh hưởng cho các hoạt động khác. Bản đồ Giảm là tốt cho các hoạt động một lần trong một thời gian có thể được thực hiện tại một thời điểm khi máy chủ không ở lưu lượng cao nhất. Bạn có thể thấy rằng việc sử dụng Map Reduce cho số liệu thống kê trực tiếp từ một bộ sưu tập lớn không phải là tối ưu.

Khung tổng hợp, mặt khác, dựa vào mã gốc và không thực thi JavaScript phía máy chủ, làm cho nó nhanh hơn Map Reduce.

Nếu có thể, tùy chọn tốt nhất là thêm trường vào từng tài liệu có thể truy vấn.Điều này cho biết thêm một chút chi phí bổ sung cho mỗi chèn hoặc cập nhật, nhưng kết quả sẽ được trả lại nhanh hơn nhiều nếu một hoạt động Map Reduce có thể tránh được. Thật không may, điều này là khó khăn với các giá trị tối đa và tối thiểu và trung bình.

Nếu thao tác Giảm bản đồ là tùy chọn duy nhất, có một vài điều có thể được thực hiện để giảm thiểu tác động của nó trên máy chủ. Thứ nhất, có thể chạy Map Reduce trên thứ cấp với SlaveOk. Tuy nhiên, vì dữ liệu không thể được ghi vào một phụ, đầu ra phải được trả về nội dòng và do đó được giới hạn ở mức 16MB. Một số người dùng sẽ lấy một phần thứ hai ra khỏi bộ bản sao, khởi động lại nó như là một quá trình mongod độc lập, chạy thao tác giảm bản đồ trên nó, sao chép bộ sưu tập đầu ra ở bất cứ nơi nào nó cần, và nối lại phần phụ vào bộ repica.

Một điều cuối cùng để xem xét là gia tăng Map Giảm: http://www.mongodb.org/display/DOCS/MapReduce#MapReduce-IncrementalMapreduce Bạn có thể vượt qua một truy vấn để bản đồ giảm lệnh đó sẽ chỉ phù hợp với các văn bản đã được sửa đổi kể từ khi bản đồ cuối cùng giảm, và chạy bản đồ giảm hoạt động với giảm tùy chọn đầu ra.

Hy vọng rằng ở trên sẽ cung cấp cho bạn một số thực phẩm cho suy nghĩ liên quan đến cách tốt nhất để tính toán thống kê của bạn. Bao gồm các thông tin mong muốn trong các tài liệu là thích hợp hơn, nhưng nếu điều đó là không thể, sử dụng Khung tổng hợp sẽ hiệu quả hơn Map Reduce.

Đây là một ghi chú trên khung Aggregation và pymongo, để đáp ứng với những nhận xét thứ hai:

Khung tập hợp có thể được sử dụng trong pymongo với phương pháp lệnh của đối tượng cơ sở dữ liệu.
Tài liệu hướng dẫn về phương pháp lệnh có thể được tìm thấy tại đây: http://api.mongodb.org/python/current/api/pymongo/database.html#pymongo.database.Database.command

Để thực hiện thao tác tổng hợp, chuyển tài liệu sang phương thức lệnh bằng hai phím; "tổng hợp" và "đường ống". Giá trị của "tổng hợp" là tên của bộ sưu tập mà các hoạt động sẽ được thực hiện trên, và giá trị của "đường ống" sẽ là một mảng của các hoạt động tổng hợp được thực hiện. Đường ống được giải thích trong "Aggregation Khung" tài liệu: http://www.mongodb.org/display/DOCS/Aggregation+Framework#AggregationFramework-Pipelines

Dưới đây là một ví dụ về cách bạn có thể thực hiện các hoạt động trong $ Thư giãn pymongo:

In [1]: import pymongo 

In [2]: conn = pymongo.Connection() 

In [3]: db = conn.test 

In [4]: result = db.command({"aggregate":"newStats", "pipeline": 
          [{"$unwind": "$hitsPerOneSecond"}, 
          {"$group": {"_id":"$startTimeStr", 
              "totalHits": {"$sum": 
              "$hitsPerOneSecond"}, 
           "totalHitsCount": {"$sum": 1}, 
           "avgHit": {"$avg": "$hitsPerOneSecond"}, 
           "minHit": {"$min":"$hitsPerOneSecond"}, 
           "maxHit":{"$max": "$hitsPerOneSecond"}}}]}) 

In [5]: result 
Out[5]: 
{u'ok': 1.0, 
u'result': [{u'_id': u'07-04-2012:10AM', 
    u'avgHit': 2.7, 
    u'maxHit': 5.0, 
    u'minHit': 1.0, 
    u'totalHits': 54.0, 
    u'totalHitsCount': 20}]} 
+0

Đây là một ví dụ tuyệt vời. Rất cám ơn vì điều đó. Ngoài ra, các liên kết đến tài liệu được đánh giá cao.Về tổng hợp - mục tiêu là tổng hợp số liệu thống kê bay thông qua Bảng điều khiển và pymongo nhưng tôi nghĩ cả bản đồ/giảm và tập hợp với nhóm $ vẫn có thể tương đối chậm? Tôi đang tìm kiếm để tổng hợp 60.080 (1 tuần giá trị) của 10 số liệu thống kê dữ liệu thứ hai từ một kịch bản python đã collates nó vào số liệu thống kê 10 giây từ các tập tin đăng nhập apache. – sam0673

+0

Rất vui được trợ giúp! Tôi đã cập nhật câu trả lời của mình ở trên với một số ghi chú trên Map Reduce. – Marc

+0

Một câu hỏi quan trọng mặc dù .. làm thế nào bạn sẽ thực hiện db.newStats.aggregate ({$ thư giãn: "$ hitsPerOneSecond"} .. vv thông qua pymongo như tôi không thể tìm thấy bất kỳ tài liệu hướng dẫn cho điều này? – sam0673

13

Làm thế nào để $unwind nhiều hơn một mảng? Bạn đã thử $unwinding nhiều lần chưa? :)

db.newStats.aggregate([ 
    {$unwind: "$hitsPerOneSecond"}, 
    {$unwind: "$pathStats_xxx_api_get_response"}, 

    {$group:{ 
     _id:"$startTimeStr", 
     totalHits: {$sum: "$hitsPerOneSecond"}, 
     totalHitsCount: {$sum: 1}, 
     avgHit: {$avg: "$hitsPerOneSecond"}, 
     minHit: {$min:"$hitsPerOneSecond"}, 
     maxHit:{$max: "$hitsPerOneSecond"}, 

     totalResponses: {$sum: "$pathStats_xxx_api_get_response"}, 
     . . . 
    }} 
]); 

Hãy nhớ rằng khuôn khổ tổng thể phải mất một mảng như một đầu vào (lưu ý rằng tôi đã thêm [, ]). Trong mảng bạn có thể thêm vào các đường ống như nhiều chức năng tổng hợp như bạn muốn (trích dẫn cần thiết) và đầu ra của bất kỳ bước sẽ là đầu vào của kế tiếp!

LƯU Ý:

Đừng quên rằng nếu bạn cố gắng $unwind trên một chìa khóa không tồn tại hoặc trên một mảng trống rỗng bạn kết thúc với không có tài liệu nào cả! Vì vậy, với nhân với nhiều (có thể nhiều) $unwind, cơ hội đối phó với ma tăng: Nếu bất kỳ mảng liên quan nào trống, toàn bộ tài liệu bị mất và bạn không nhận được gì cho bất kỳ $group của bạn tập hợp ...

Các vấn đề liên quan