2015-08-31 15 views
5

Tôi có một bộ sưu tập mà là nhật ký của hoạt động trên các đối tượng như thế này:MongoDB đếm tất cả các phần tử mảng trong tất cả các đối tượng phù hợp theo các tiêu chí

{ 
    "_id" : ObjectId("55e3fd1d7cb5ac9a458b4567"), 
    "object_id" : "1", 
    "activity" : [ 
     { 
      "action" : "test_action", 
      "time" : ISODate("2015-08-31T00:00:00.000Z") 
     }, 
     { 
      "action" : "test_action", 
      "time" : ISODate("2015-08-31T00:00:22.000Z") 
     } 
    ] 
} 

{ 
    "_id" : ObjectId("55e3fd127cb5ac77478b4567"), 
    "object_id" : "2", 
    "activity" : [ 
     { 
      "action" : "test_action", 
      "time" : ISODate("2015-08-31T00:00:00.000Z") 
     } 
    ] 
} 

{ 
    "_id" : ObjectId("55e3fd0f7cb5ac9f458b4567"), 
    "object_id" : "1", 
    "activity" : [ 
     { 
      "action" : "test_action", 
      "time" : ISODate("2015-08-30T00:00:00.000Z") 
     } 
    ] 
} 

Nếu tôi followoing truy vấn:

db.objects.find({ 
    "createddate": {$gte : ISODate("2015-08-30T00:00:00.000Z")}, 
    "activity.action" : "test_action"} 
    }).count() 

nó trả về số lượng tài liệu có chứa "test_action" (3 trong tập này), nhưng tôi cần phải đếm tổng số test_actions (4 trên tập này). Làm thế nào để làm điều đó?

Trả lời

8

Nhất "performant" cách để làm điều này là để bỏ qua $unwind altogther và chỉ cần $group để đếm. Về cơ bản "bộ lọc" mảng được $size các kết quả để $sum:

db.objects.aggregate([ 
    { "$match": { 
     "createddate": { 
      "$gte": ISODate("2015-08-30T00:00:00.000Z") 
     }, 
     "activity.action": "test_action" 
    }}, 
    { "$group": { 
     "_id": null, 
     "count": { 
      "$sum": { 
       "$size": { 
        "$setDifference": [ 
         { "$map": { 
          "input": "$activity", 
          "as": "el", 
          "in": { 
           "$cond": [ 
            { "$eq": [ "$$el.action", "test_action" ] }, 
            "$$el", 
            false 
           ] 
          }    
         }}, 
         [false] 
        ] 
       } 
      } 
     } 
    }} 
]) 

phiên bản tương lai của MongoDB sẽ có $filter, mà làm này đơn giản hơn nhiều:

db.objects.aggregate([ 
    { "$match": { 
     "createddate": { 
      "$gte": ISODate("2015-08-30T00:00:00.000Z") 
     }, 
     "activity.action": "test_action" 
    }}, 
    { "$group": { 
     "_id": null, 
     "count": { 
      "$sum": { 
       "$size": { 
        "$filter": { 
         "input": "$activity", 
         "as": "el", 
         "cond": { 
          "$eq": [ "$$el.action", "test_action" ] 
         } 
        } 
       } 
      } 
     } 
    }} 
]) 

Sử dụng $unwind gây các chứng từ để de -normalize và tạo hiệu quả một bản sao cho mỗi mục nhập mảng. Nếu có thể, bạn nên tránh điều này do chi phí cực kỳ thường xuyên. Lọc và đếm các mục mảng trên mỗi tài liệu nhanh hơn nhiều bằng cách so sánh. Như một đường ống đơn giản $match$group so với nhiều giai đoạn.

+1

Cảm ơn bạn rất nhiều. Tránh "$ thư giãn" là phải trên bộ dữ liệu lớn. Truy vấn hoạt động như một nét duyên dáng. Kiến thức của tôi khá cơ bản bây giờ và tôi thực sự không biết CÁCH nó hoạt động thế nào :) Nhưng việc tìm ra điều này sẽ là bài tập về nhà của tôi cho ngày hôm nay) – aokozlov

5

Bạn có thể làm như vậy bằng cách sử dụng kết hợp:

db.objects.aggregate([ 
    {$match: {"createddate": {$gte : ISODate("2015-08-30T00:00:00.000Z")}, {"activity.action" : "test_action"}}}, 
    {$unwind: "$activity"}, 
    {$match: {"activity.action" : "test_action"}}}, 
    {$group: {_id: null, count: {$sum: 1}}} 
]) 

này sẽ tạo ra một kết quả như:

{ 
    count: 4 
} 
+0

Cảm ơn bạn, nó hoạt động, nhưng có vẻ như nó không sử dụng các chỉ mục và hoạt động cực chậm trên bộ dữ liệu tài liệu 600k. Tôi có các chỉ mục cho '_id',' createddate' và 'activity.action'. Tôi nên tạo các chỉ mục nào khác? – aokozlov

+0

Tập hợp không sử dụng chỉ mục cho giai đoạn kết hợp $ (nếu được chỉ định ở đầu), nhưng như Blakes Seven đã nói, giai đoạn thư giãn gây ra rất nhiều chi phí. – ZeMoon

+0

Tôi đã thêm một bản chỉnh sửa, điều này sẽ làm cho nó chạy nhanh hơn một chút – ZeMoon

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