MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Nhóm và đếm bằng cách sử dụng khung tổng hợp

Có vẻ như bạn đã bắt đầu về điều này nhưng bạn lại bị lạc vào một số khái niệm khác. Có một số sự thật cơ bản khi làm việc với mảng trong tài liệu, nhưng hãy bắt đầu từ nơi bạn đã dừng lại:

db.sample.aggregate([
    { "$group": {
        "_id": "$status",
        "count": { "$sum": 1 }
    }}
])

Vì vậy, đó chỉ là sử dụng nhóm $group đường dẫn để thu thập tài liệu của bạn trên các giá trị khác nhau của trường "trạng thái" và sau đó cũng tạo ra một trường khác cho "đếm", tất nhiên "đếm" số lần xuất hiện của khóa nhóm bằng cách chuyển giá trị 1 tới $sum toán tử cho mỗi tài liệu được tìm thấy. Điều này đưa bạn đến một điểm giống như bạn mô tả:

{ "_id" : "done", "count" : 2 }
{ "_id" : "canceled", "count" : 1 }

Đó là giai đoạn đầu tiên của việc này và đủ dễ hiểu, nhưng bây giờ bạn cần biết cách lấy các giá trị ra khỏi một mảng. Sau đó, bạn có thể bị cám dỗ khi bạn hiểu "ký hiệu dấu chấm" khái niệm đúng để làm một cái gì đó như thế này:

db.sample.aggregate([
    { "$group": {
        "_id": "$status",
        "count": { "$sum": 1 },
        "total": { "$sum": "$devices.cost" }
    }}
])

Nhưng những gì bạn sẽ thấy là "tổng" trên thực tế sẽ là 0 cho mỗi kết quả đó:

{ "_id" : "done", "count" : 2, "total" : 0 }
{ "_id" : "canceled", "count" : 1, "total" : 0 }

Tại sao? Các hoạt động tổng hợp MongoDB như thế này không thực sự duyệt các phần tử mảng khi nhóm. Để làm được điều đó, khung tổng hợp có một khái niệm được gọi là $unwind . Tên là tương đối tự giải thích. Một mảng được nhúng trong MongoDB giống như có một liên kết "một-nhiều" giữa các nguồn dữ liệu được liên kết. Vậy điều gì $unwind không chính xác là loại kết quả "nối", trong đó "tài liệu" kết quả dựa trên nội dung của mảng và thông tin trùng lặp cho mỗi phụ huynh.

Vì vậy, để hoạt động trên các phần tử mảng, bạn cần sử dụng $unwind Đầu tiên. Về mặt logic, điều này sẽ dẫn bạn đến mã như thế này:

db.sample.aggregate([
    { "$unwind": "$devices" },
    { "$group": {
        "_id": "$status",
        "count": { "$sum": 1 },
        "total": { "$sum": "$devices.cost" }
    }}
])

Và sau đó là kết quả:

{ "_id" : "done", "count" : 4, "total" : 700 }
{ "_id" : "canceled", "count" : 2, "total" : 350 }

Nhưng điều đó không hoàn toàn đúng phải không? Hãy nhớ những gì bạn vừa học được từ $unwind và làm thế nào nó thực hiện một kết hợp không chuẩn hóa với thông tin gốc? Vì vậy, bây giờ nó được nhân bản cho mọi tài liệu vì cả hai đều có hai thành viên mảng. Vì vậy, trong khi trường "tổng số" là chính xác, "số lượng" nhiều gấp đôi so với trường hợp cần có.

Cần phải cẩn thận hơn một chút, vì vậy thay vì làm điều này trong một $group giai đoạn, nó được thực hiện trong hai:

db.sample.aggregate([
    { "$unwind": "$devices" },
    { "$group": {
        "_id": "$_id",
        "status": { "$first": "$status" },
        "total": { "$sum": "$devices.cost" }
    }},
    { "$group": {
        "_id": "$status",
        "count": { "$sum": 1 },
        "total": { "$sum": "$total" }
    }}
])

Bây giờ nhận được kết quả với các tổng chính xác trong đó:

{ "_id" : "canceled", "count" : 1, "total" : 350 }
{ "_id" : "done", "count" : 2, "total" : 700 }

Bây giờ các con số đã đúng, nhưng nó vẫn không phải là chính xác những gì bạn đang yêu cầu. Tôi nghĩ rằng bạn nên dừng lại ở đó vì loại kết quả bạn đang mong đợi thực sự không phù hợp với chỉ một kết quả từ tổng hợp đơn lẻ. Bạn đang tìm kiếm tổng số để nằm "bên trong" kết quả. Nó thực sự không thuộc về đó, nhưng trên dữ liệu nhỏ thì không sao:

db.sample.aggregate([
    { "$unwind": "$devices" },
    { "$group": {
        "_id": "$_id",
        "status": { "$first": "$status" },
        "total": { "$sum": "$devices.cost" }
    }},
    { "$group": {
        "_id": "$status",
        "count": { "$sum": 1 },
        "total": { "$sum": "$total" }
    }},
    { "$group": {
        "_id": null,
        "data": { "$push": { "count": "$count", "total": "$total" } },
        "totalCost": { "$sum": "$total" }
    }}
])

Và một mẫu kết quả cuối cùng:

{
    "_id" : null,
    "data" : [
            {
                    "count" : 1,
                    "total" : 350
            },
            {
                    "count" : 2,
                    "total" : 700
            }
    ],
    "totalCost" : 1050
}

Tuy nhiên, "Đừng làm điều đó" . MongoDB có giới hạn tài liệu về phản hồi là 16MB, đây là một giới hạn của thông số kỹ thuật BSON. Trên các kết quả nhỏ, bạn có thể thực hiện kiểu gói tiện lợi này, nhưng trong lược đồ lớn hơn, bạn muốn kết quả ở dạng sớm hơn và một truy vấn riêng biệt hoặc thực hiện lặp lại toàn bộ kết quả để lấy tổng số từ tất cả các tài liệu.

Có vẻ như bạn đang sử dụng phiên bản MongoDB nhỏ hơn 2.6 hoặc sao chép đầu ra từ trình bao RoboMongo không hỗ trợ các tính năng của phiên bản mới nhất. Từ MongoDB 2.6, mặc dù kết quả tổng hợp có thể là một "con trỏ" chứ không phải là một mảng BSON đơn lẻ. Vì vậy, phản hồi tổng thể có thể lớn hơn nhiều 16MB, nhưng chỉ khi bạn không nén thành một tài liệu duy nhất dưới dạng kết quả, được hiển thị trong ví dụ cuối cùng.

Điều này đặc biệt đúng trong trường hợp bạn đang "phân trang" kết quả, với 100 đến 1000 dòng kết quả nhưng bạn chỉ muốn "tổng số" trả về trong phản hồi API khi bạn chỉ trả lại một "trang" gồm 25 kết quả tại một thời gian.

Nhưng dù sao, điều đó sẽ cung cấp cho bạn một hướng dẫn hợp lý về cách nhận được loại kết quả bạn mong đợi từ biểu mẫu tài liệu chung của bạn. Hãy nhớ $unwind để xử lý mảng và nói chung $group nhiều lần để nhận tổng số ở các cấp độ nhóm khác nhau từ nhóm tài liệu và nhóm bộ sưu tập của bạn.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB tìm và xóa - cách nhanh nhất

  2. Mongoose:dân số sâu (điền vào một trường đông dân cư)

  3. cách truy vấn các đối tượng con trong mongodb

  4. Chọn cơ sở dữ liệu nào (Cassandra, MongoDB,?) Để lưu trữ và truy vấn dữ liệu sự kiện / nhật ký / số liệu?

  5. Mongodb - kết hợp regex của các khóa cho các tài liệu phụ