Nếu bạn cần tính toán thứ gì đó như thế này trong thời gian chạy, với nội dung "đã lọc" từ mảng xác định thứ tự sắp xếp, thì tốt nhất bạn nên làm điều gì đó với .aggregate ()
để định hình lại và xác định một giá trị sắp xếp như thế này:
db.collection.aggregate([
// Pre-filter the array elements
{ "$project": {
"tags": 1,
"score": {
"$setDifference": [
{ "$map": {
"input": "$tags",
"as": "tag",
"in": {
"$cond": [
{ "$eq": [ "$$el.id", "t1" ] },
"$$el.score",
false
]
}
}},
[false]
]
}
}},
// Unwind to denormalize
{ "$unwind": "$score" },
// Group back the "max" score
{ "$group": {
"_id": "$_id",
"tags": { "$first": "$tags" },
"score": { "$max": "$score" }
}},
// Sort descending by score
{ "$sort": { "score": -1 } }
])
Trong đó phần đầu tiên của đường ống được sử dụng để "lọc trước" nội dung mảng (cũng như giữ trường gốc) chỉ với những giá trị của "điểm" trong đó id bằng "t1". Điều này được thực hiện bằng cách xử lý $ map
áp dụng một điều kiện cho từng phần tử qua $ cond
để xác định xem trả lại "điểm" cho phần tử đó hay false
.
$ setDifference
hoạt động so sánh với một mảng phần tử duy nhất [false]
loại bỏ hiệu quả mọi false
các giá trị được trả về từ $ map
. Là một "tập hợp", điều này cũng loại bỏ các mục nhập trùng lặp, nhưng với mục đích sắp xếp ở đây, đây là một điều tốt.
Với mảng được thu nhỏ và định dạng lại thành các giá trị bạn xử lý $ unwind
sẵn sàng cho giai đoạn tiếp theo để xử lý các giá trị dưới dạng các phần tử riêng lẻ. $ group
về cơ bản giai đoạn áp dụng $ max
trên "điểm số" để trả về giá trị cao nhất có trong các kết quả đã lọc.
Sau đó, vấn đề chỉ là áp dụng $ sort
vào giá trị xác định để đặt hàng các tài liệu. Naturaly nếu bạn muốn điều này theo cách khác thì hãy sử dụng $ min
và sắp xếp theo thứ tự tăng dần.
Tất nhiên, hãy thêm một $ đối sánh
bắt đầu nếu tất cả những gì bạn thực sự muốn là tài liệu thực sự chứa giá trị "t1" cho id
trong các thẻ. Nhưng phần đó ít liên quan nhất đến việc sắp xếp các kết quả đã lọc mà bạn muốn đạt được.
Cách thay thế để tính toán là thực hiện tất cả khi bạn ghi các mục nhập vào mảng trong tài liệu. Hơi lộn xộn, nhưng nó diễn ra như thế này:
db.collection.update(
{ "_id": docId },
{
"$push": { "tags": { "id": "t1", "score": 60 } },
"$max": { "maxt1score": 60 },
"$min": { "mint1score": 60 }
}
)
Đây là $ max
toán tử cập nhật chỉ đặt giá trị cho trường được chỉ định nếu giá trị mới lớn hơn giá trị hiện có hoặc không có thuộc tính nào tồn tại. Trường hợp ngược lại đúng với $ min
, chỉ khi nhỏ hơn nó sẽ được thay thế bằng giá trị mới.
Điều này tất nhiên sẽ có tác dụng thêm các thuộc tính bổ sung khác nhau vào tài liệu, nhưng kết quả cuối cùng là việc sắp xếp được đơn giản hóa rất nhiều:
db.collection.find().sort({ "maxt1score": -1 })
Và nó sẽ chạy nhanh hơn rất nhiều so với tính toán với một đường ống tổng hợp.
Vì vậy, hãy xem xét các nguyên tắc thiết kế. Dữ liệu có cấu trúc trong các mảng mà bạn muốn kết quả được lọc và ghép nối để sắp xếp có nghĩa là tính toán tại thời điểm chạy để xác định giá trị nào cần sắp xếp. Thêm thuộc tính bổ sung vào tài liệu trên .update ()
nghĩa là bạn có thể chỉ cần tham chiếu các thuộc tính đó để sắp xếp trực tiếp kết quả.