Miễn là dữ liệu của bạn có cảm biến và thẻ đọc duy nhất trên mỗi tài liệu, cho đến nay những gì bạn đã trình bày sẽ hiển thị, thì bạn chỉ cần không cần $unwind
ở tất cả.
Trên thực tế, tất cả những gì bạn thực sự cần là một $group
:
db.endpoints.aggregate([
// In reality you would $match to limit the selection of documents
{ "$match": {
"DateTime": { "$gte": new Date("2018-05-01"), "$lt": new Date("2018-06-01") }
}},
{ "$group": {
"_id": "$EndpointId",
"FirstActivity" : { "$min" : "$DateTime" },
"LastActivity" : { "$max" : "$DateTime" },
"RequestCount": { "$sum": 1 },
"TagCount": {
"$sum": {
"$size": { "$setUnion": ["$Tags.Uid",[]] }
}
},
"SensorCount": {
"$sum": {
"$sum": {
"$map": {
"input": { "$setUnion": ["$Tags.Uid",[]] },
"as": "tag",
"in": {
"$size": {
"$reduce": {
"input": {
"$filter": {
"input": {
"$map": {
"input": "$Tags",
"in": {
"Uid": "$$this.Uid",
"Type": "$$this.Sensors.Type"
}
}
},
"cond": { "$eq": [ "$$this.Uid", "$$tag" ] }
}
},
"initialValue": [],
"in": { "$setUnion": [ "$$value", "$$this.Type" ] }
}
}
}
}
}
}
}
}}
])
Hoặc nếu bạn thực sự cần tích lũy các giá trị "duy nhất" đó của "Cảm biến" và "Thẻ" từ các tài liệu khác nhau, thì bạn vẫn cần $unwind
các câu lệnh để có được nhóm phù hợp, nhưng không ở đâu nhiều như bạn hiện có:
db.endpoints.aggregate([
// In reality you would $match to limit the selection of documents
{ "$match": {
"DateTime": { "$gte": new Date("2018-05-01"), "$lt": new Date("2018-06-01") }
}},
{ "$unwind": "$Tags" },
{ "$unwind": "$Tags.Sensors" },
{ "$group": {
"_id": {
"EndpointId": "$EndpointId",
"Uid": "$Tags.Uid",
"Type": "$Tags.Sensors.Type"
},
"FirstActivity": { "$min": "$DateTime" },
"LastActivity": { "$max": "$DateTime" },
"RequestCount": { "$addToSet": "$_id" }
}},
{ "$group": {
"_id": {
"EndpointId": "$_id.EndpointId",
"Uid": "$_id.Uid",
},
"FirstActivity": { "$min": "$FirstActivity" },
"LastActivity": { "$max": "$LastActivity" },
"count": { "$sum": 1 },
"RequestCount": { "$addToSet": "$RequestCount" }
}},
{ "$group": {
"_id": "$_id.EndpointId",
"FirstActivity": { "$min": "$FirstActivity" },
"LastActivity": { "$max": "$LastActivity" },
"TagCount": { "$sum": 1 },
"SensorCount": { "$sum": "$count" },
"RequestCount": { "$addToSet": "$RequestCount" }
}},
{ "$addFields": {
"RequestCount": {
"$size": {
"$reduce": {
"input": {
"$reduce": {
"input": "$RequestCount",
"initialValue": [],
"in": { "$setUnion": [ "$$value", "$$this" ] }
}
},
"initialValue": [],
"in": { "$setUnion": [ "$$value", "$$this" ] }
}
}
}
}}
],{ "allowDiskUse": true })
Và từ MongoDB 4.0, bạn có thể sử dụng $toString
trên ObjectId
trong _id
và chỉ cần hợp nhất các khóa duy nhất cho những khóa đó để giữ RequestCount
sử dụng $mergeObjects
. Điều này rõ ràng hơn và có thể mở rộng hơn một chút so với việc đẩy nội dung mảng lồng nhau và làm phẳng nó
db.endpoints.aggregate([
// In reality you would $match to limit the selection of documents
{ "$match": {
"DateTime": { "$gte": new Date("2018-05-01"), "$lt": new Date("2018-06-01") }
}},
{ "$unwind": "$Tags" },
{ "$unwind": "$Tags.Sensors" },
{ "$group": {
"_id": {
"EndpointId": "$EndpointId",
"Uid": "$Tags.Uid",
"Type": "$Tags.Sensors.Type"
},
"FirstActivity": { "$min": "$DateTime" },
"LastActivity": { "$max": "$DateTime" },
"RequestCount": {
"$mergeObjects": {
"$arrayToObject": [[{ "k": { "$toString": "$_id" }, "v": 1 }]]
}
}
}},
{ "$group": {
"_id": {
"EndpointId": "$_id.EndpointId",
"Uid": "$_id.Uid",
},
"FirstActivity": { "$min": "$FirstActivity" },
"LastActivity": { "$max": "$LastActivity" },
"count": { "$sum": 1 },
"RequestCount": { "$mergeObjects": "$RequestCount" }
}},
{ "$group": {
"_id": "$_id.EndpointId",
"FirstActivity": { "$min": "$FirstActivity" },
"LastActivity": { "$max": "$LastActivity" },
"TagCount": { "$sum": 1 },
"SensorCount": { "$sum": "$count" },
"RequestCount": { "$mergeObjects": "$RequestCount" }
}},
{ "$addFields": {
"RequestCount": {
"$size": {
"$objectToArray": "$RequestCount"
}
}
}}
],{ "allowDiskUse": true })
Một trong hai biểu mẫu trả về cùng một dữ liệu, mặc dù thứ tự các khóa trong kết quả có thể khác nhau:
{
"_id" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83",
"FirstActivity" : ISODate("2018-05-06T19:05:02.666Z"),
"LastActivity" : ISODate("2018-05-06T19:05:02.666Z"),
"RequestCount" : 2,
"TagCount" : 4,
"SensorCount" : 16
}
Kết quả thu được từ các tài liệu mẫu này mà ban đầu bạn đã cung cấp làm nguồn mẫu trong câu hỏi ban đầu về chủ đề này :
{
"_id" : ObjectId("5aef51dfaf42ea1b70d0c4db"),
"EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83",
"DateTime" : ISODate("2018-05-06T19:05:02.666Z"),
"Url" : "test",
"Tags" : [
{
"Uid" : "C1:3D:CA:D4:45:11",
"Type" : 1,
"DateTime" : ISODate("2018-05-06T19:05:02.666Z"),
"Sensors" : [
{
"Type" : 1,
"Value" : NumberDecimal("-95")
},
{
"Type" : 2,
"Value" : NumberDecimal("-59")
},
{
"Type" : 3,
"Value" : NumberDecimal("11.029802536740132")
},
{
"Type" : 4,
"Value" : NumberDecimal("27.25")
},
{
"Type" : 6,
"Value" : NumberDecimal("2924")
}
]
},
{
"Uid" : "C1:3D:CA:D4:45:11",
"Type" : 1,
"DateTime" : ISODate("2018-05-06T19:05:02.666Z"),
"Sensors" : [
{
"Type" : 1,
"Value" : NumberDecimal("-95")
},
{
"Type" : 2,
"Value" : NumberDecimal("-59")
},
{
"Type" : 3,
"Value" : NumberDecimal("11.413037961112279")
},
{
"Type" : 4,
"Value" : NumberDecimal("27.25")
},
{
"Type" : 6,
"Value" : NumberDecimal("2924")
}
]
},
{
"Uid" : "E5:FA:2A:35:AF:DD",
"Type" : 1,
"DateTime" : ISODate("2018-05-06T19:05:02.666Z"),
"Sensors" : [
{
"Type" : 1,
"Value" : NumberDecimal("-97")
},
{
"Type" : 2,
"Value" : NumberDecimal("-58")
},
{
"Type" : 3,
"Value" : NumberDecimal("10.171658037099185")
}
]
}
]
}
/* 2 */
{
"_id" : ObjectId("5aef51e0af42ea1b70d0c4dc"),
"EndpointId" : "89799bcc-e86f-4c8a-b340-8b5ed53caf83",
"Url" : "test",
"Tags" : [
{
"Uid" : "E2:02:00:18:DA:40",
"Type" : 1,
"DateTime" : ISODate("2018-05-06T19:05:04.574Z"),
"Sensors" : [
{
"Type" : 1,
"Value" : NumberDecimal("-98")
},
{
"Type" : 2,
"Value" : NumberDecimal("-65")
},
{
"Type" : 3,
"Value" : NumberDecimal("7.845424441900629")
},
{
"Type" : 4,
"Value" : NumberDecimal("0.0")
},
{
"Type" : 6,
"Value" : NumberDecimal("3012")
}
]
},
{
"Uid" : "12:3B:6A:1A:B7:F9",
"Type" : 1,
"DateTime" : ISODate("2018-05-06T19:05:04.574Z"),
"Sensors" : [
{
"Type" : 1,
"Value" : NumberDecimal("-95")
},
{
"Type" : 2,
"Value" : NumberDecimal("-59")
},
{
"Type" : 3,
"Value" : NumberDecimal("12.939770381907275")
}
]
}
]
}
Điểm mấu chốt là bạn có thể sử dụng biểu mẫu đã cho đầu tiên ở đây, biểu mẫu này sẽ tích lũy "trong mỗi tài liệu" và sau đó "tích lũy cho mỗi điểm cuối" trong một giai đoạn duy nhất và là tối ưu nhất hoặc bạn thực sự yêu cầu xác định những thứ như "Uid"
trên các thẻ hoặc "Type"
trên cảm biến nơi các giá trị đó xuất hiện nhiều lần trên bất kỳ tổ hợp tài liệu nào được nhóm theo điểm cuối.
Dữ liệu mẫu của bạn được cung cấp cho đến nay chỉ cho thấy rằng các giá trị này là "duy nhất trong mỗi tài liệu", do đó, biểu mẫu nhất định đầu tiên sẽ là tối ưu nhất nếu đây là trường hợp của tất cả dữ liệu còn lại.
Trong trường hợp không phải vậy, thì "giải nén" hai mảng lồng nhau để "tổng hợp chi tiết trên các tài liệu" là cách duy nhất để tiếp cận điều này. Bạn có thể giới hạn phạm vi ngày hoặc các tiêu chí khác vì hầu hết các "truy vấn" thường có một số giới hạn và không thực sự hoạt động trên dữ liệu thu thập "toàn bộ", nhưng thực tế chính là các mảng sẽ "không bị ràng buộc" tạo bản sao tài liệu về cơ bản cho mọi thành viên mảng.
Điểm tối ưu hóa có nghĩa là bạn chỉ cần làm điều này "hai lần" vì chỉ có hai mảng. Thực hiện liên tiếp $group
tới $unwind
tới $group
luôn là một dấu hiệu chắc chắn rằng bạn đang làm điều gì đó thực sự sai trái. Sau khi bạn "tách một thứ gì đó ra", bạn chỉ cần "đặt nó lại với nhau" một lần . Trong một loạt các bước được phân loại như được trình bày ở đây là một lần cách tiếp cận tối ưu hóa.
Ngoài phạm vi câu hỏi của bạn vẫn còn:
- Thêm các ràng buộc thực tế khác vào truy vấn để giảm bớt các tài liệu được xử lý, thậm chí có thể làm như vậy theo "lô" và kết hợp các kết quả
- Thêm
allowDiskUse
tùy chọn đường ống để cho phép sử dụng bộ nhớ tạm thời. (thực tế đã chứng minh trên các lệnh) - Cân nhắc rằng "các mảng lồng nhau" có lẽ không phải là phương pháp lưu trữ tốt nhất cho phân tích bạn muốn thực hiện. Nó luôn hiệu quả hơn khi bạn biết mình cần
$unwind
chỉ cần ghi trực tiếp dữ liệu ở dạng "chưa liên kết" đó vào một bộ sưu tập.