Trước hết, hãy hiển thị dữ liệu của bạn theo cách mọi người có thể sử dụng và tạo ra kết quả mong muốn:
{ value: 1,
_id: ObjectId('5cb9ea0c75c61525e0176f96'),
name: 'Test',
category: 'Development',
subcategory: 'Programming Languages',
status: 'Supported',
description: 'Test',
change:
[ { version: 1,
who: 'ATL User',
when: new Date('2019-04-19T15:30:39.912Z'),
what: 'Item Creation' },
{ version: 2,
who: 'ATL Other User',
when: new Date('2019-04-19T15:31:39.912Z'),
what: 'Name Change' } ],
}
Lưu ý rằng "when"
thực tế là ngày khác nhau nên sẽ có $ max
giá trị và chúng không chỉ giống nhau. Bây giờ chúng ta có thể xem xét các trường hợp
Trường hợp 1 - Tìm nạp "số ít" $ max
giá trị
Trường hợp cơ bản ở đây là sử dụng mã $ arrayElemAt >
và $ indexOfArray
để trả về các toán tử phù hợp $ max
giá trị:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$arrayElemAt": [
"$change",
{ "$indexOfArray": [
"$change.when",
{ "$max": "$change.when" }
]}
]
}
}}
])
Lợi nhuận:
{
"_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
"value" : 1,
"name" : "Test",
"category" : "Development",
"subcategory" : "Programming Languages",
"status" : "Supported",
"description" : "Test",
"change" : {
"version" : 2,
"who" : "ATL Other User",
"when" : ISODate("2019-04-19T15:31:39.912Z"),
"what" : "Name Change"
}
}
Về cơ bản, "$ max":"$ change.when"
trả về giá trị là "lớn nhất" từ bên trong mảng giá trị đó. Sau đó, bạn tìm thấy "chỉ mục" phù hợp của mảng giá trị đó qua $ indexOfArray
trả về đầu tiên chỉ số phù hợp được tìm thấy. Vị trí "chỉ mục" đó (thực ra chỉ là một mảng "when"
các giá trị được hoán vị theo cùng một thứ tự) sau đó được sử dụng với $ arrayElemAt
để trích xuất "toàn bộ đối tượng" từ "change"
mảng ở vị trí chỉ mục được chỉ định.
Trường hợp 2 - Trả về "nhiều" $ max
mục nhập
Điều tương tự với $ max
, ngoại trừ lần này, chúng tôi $ filter
để trả lại nhiều "có thể" các giá trị phù hợp với $ max
giá trị:
db.items.aggregate([
{ "$match": {
"subcategory": "Programming Languages", "name": { "$exists": true }
}},
{ "$addFields": {
"change": {
"$filter": {
"input": "$change",
"cond": {
"$eq": [ "$$this.when", { "$max": "$change.when" } ]
}
}
}
}}
])
Lợi nhuận:
{
"_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
"value" : 1,
"name" : "Test",
"category" : "Development",
"subcategory" : "Programming Languages",
"status" : "Supported",
"description" : "Test",
"change" : [
{
"version" : 2,
"who" : "ATL Other User",
"when" : ISODate("2019-04-19T15:31:39.912Z"),
"what" : "Name Change"
}
]
}
Vì vậy, $ max
tất nhiên là giống nhau nhưng lần này giá trị số ít do toán tử đó trả về được sử dụng trong $ eq
so sánh trong $ filter
. Thao tác này sẽ kiểm tra từng phần tử mảng và xem xét hiện tại "when"
value ( "$$ this.when"
). Trường hợp "bằng" thì phần tử được trả về.
Về cơ bản giống như cách tiếp cận đầu tiên nhưng ngoại trừ $ filter
cho phép "nhiều" các phần tử được trả về. Do đó mọi thứ với giống nhau $ max
giá trị.
Trường hợp 3 - Sắp xếp trước nội dung mảng.
Bây giờ, bạn có thể lưu ý rằng trong dữ liệu mẫu mà tôi đã bao gồm (được điều chỉnh từ dữ liệu của riêng bạn nhưng với ngày "tối đa" thực tế) giá trị "tối đa" trên thực tế là cuối cùng giá trị trong mảng. Điều này có thể tự nhiên xảy ra do $ push
(theo mặc định) "appends" đến cuối nội dung mảng hiện có. Vì vậy, "mới hơn" các mục nhập sẽ có xu hướng ở cuối của mảng.
Tất nhiên, đây là mặc định nhưng có những lý do chính đáng khiến bạn "có thể" muốn thay đổi điều đó. Trong ngắn hạn tốt nhất cách để có được "gần đây nhất" mục nhập mảng trên thực tế là trả về phần tử đầu tiên từ mảng.
Tất cả những gì bạn thực sự cần làm là đảm bảo "gần đây nhất" thực sự được thêm vào đầu tiên chứ không phải là cuối cùng . Có hai cách tiếp cận:
-
Sử dụng
$ position
để "xếp trước" các mục trong mảng: Đây là một công cụ sửa đổi đơn giản cho$ push
sử dụng0
vị trí để luôn thêm vào phía trước :db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [{ "version": 3, "who": "ATL User", "when": new Date(), "what": "Another change" }], "$position": 0 } }} )
Điều này sẽ thay đổi tài liệu thành:
{ "_id" : ObjectId("5cb9ea0c75c61525e0176f96"), "value" : 1, "name" : "Test", "category" : "Development", "subcategory" : "Programming Languages", "status" : "Supported", "description" : "Test", "change" : [ { "version" : 3, "who" : "ATL User", "when" : ISODate("2019-04-20T02:40:30.024Z"), "what" : "Another change" }, { "version" : 1, "who" : "ATL User", "when" : ISODate("2019-04-19T15:30:39.912Z"), "what" : "Item Creation" }, { "version" : 2, "who" : "ATL Other User", "when" : ISODate("2019-04-19T15:31:39.912Z"), "what" : "Name Change" } ] }
Lưu ý rằng điều này sẽ yêu cầu bạn thực sự đi và "đảo ngược" tất cả các phần tử mảng của bạn trước đó để "mới nhất" đã ở phía trước để thứ tự được duy trì. Rất may, điều này phần nào được đề cập trong cách tiếp cận thứ hai ...
-
Sử dụng
$ sort
để sửa đổi các tài liệu theo thứ tự trên mỗi mã$ push >
: Và đây là công cụ sửa đổi khác thực sự "sắp xếp lại" nguyên tử mỗi lần bổ sung mục mới. Việc sử dụng bình thường về cơ bản giống với bất kỳ mục mới nào đối với$ each
như ở trên, hoặc thậm chí chỉ là một mảng "trống" để áp dụng$ sort
chỉ với dữ liệu hiện có:db.items.updateOne( { "_id" : ObjectId("5cb9ea0c75c61525e0176f96") }, { "$push": { "change": { "$each": [], "$sort": { "when": -1 } } }} )
Kết quả trong:
{ "_id" : ObjectId("5cb9ea0c75c61525e0176f96"), "value" : 1, "name" : "Test", "category" : "Development", "subcategory" : "Programming Languages", "status" : "Supported", "description" : "Test", "change" : [ { "version" : 3, "who" : "ATL User", "when" : ISODate("2019-04-20T02:40:30.024Z"), "what" : "Another change" }, { "version" : 2, "who" : "ATL Other User", "when" : ISODate("2019-04-19T15:31:39.912Z"), "what" : "Name Change" }, { "version" : 1, "who" : "ATL User", "when" : ISODate("2019-04-19T15:30:39.912Z"), "what" : "Item Creation" } ] }
Có thể mất một phút để hiểu lý do tại sao bạn
$ push
để$ sort
một mảng như thế này, nhưng mục đích chung là khi các sửa đổi có thể được thực hiện đối với một mảng "thay đổi" một thuộc tính nhưNgày
giá trị đang được sắp xếp và bạn sẽ sử dụng một câu lệnh như vậy để phản ánh những thay đổi đó. Hoặc thực sự chỉ cần thêm các mục mới bằng mã$ sort >
và để nó hoạt động.
Vậy tại sao "cửa hàng" mảng được sắp xếp như thế này? Như đã đề cập trước đó, bạn muốn đầu tiên mục là "gần đây nhất" , và sau đó truy vấn trả về đơn giản trở thành:
db.items.find(
{
"subcategory": "Programming Languages",
"name": { "$exists": true }
},
{ "change": { "$slice": 1 } }
)
Lợi nhuận:
{
"_id" : ObjectId("5cb9ea0c75c61525e0176f96"),
"value" : 1,
"name" : "Test",
"category" : "Development",
"subcategory" : "Programming Languages",
"status" : "Supported",
"description" : "Test",
"change" : [
{
"version" : 3,
"who" : "ATL User",
"when" : ISODate("2019-04-20T02:40:30.024Z"),
"what" : "Another change"
}
]
}
Vì vậy, $ slice
sau đó có thể được sử dụng chỉ để trích xuất các mục mảng theo các chỉ mục đã biết. Về mặt kỹ thuật, bạn chỉ có thể sử dụng -1
ở đó để trả lại cuối cùng mặt hàng của mảng dù sao đi nữa, nhưng việc sắp xếp lại thứ tự nơi gần đây nhất được đặt trước cho phép những thứ khác như xác nhận sửa đổi cuối cùng được thực hiện bởi một người dùng nhất định và / hoặc các điều kiện khác như ràng buộc phạm vi ngày. tức là:
db.items.find(
{
"subcategory": "Programming Languages",
"name": { "$exists": true },
"change.0.who": "ATL User",
"change.0.when": { "$gt": new Date("2018-04-01") }
},
{ "change": { "$slice": 1 } }
)
Lưu ý ở đây rằng một cái gì đó như "change.-1.when"
là một tuyên bố bất hợp pháp, về cơ bản đó là lý do tại sao chúng tôi sắp xếp lại mảng để bạn có thể sử dụng hợp pháp 0
cho đầu tiên thay vì -1
cho cuối cùng .
Kết luận
Vì vậy, có một số điều bạn có thể làm, bằng cách sử dụng phương pháp tổng hợp để lọc nội dung mảng hoặc thông qua các biểu mẫu truy vấn chuẩn sau khi thực hiện một số sửa đổi đối với cách dữ liệu thực sự được lưu trữ. Việc sử dụng cái nào tùy thuộc vào hoàn cảnh của riêng bạn, nhưng cần lưu ý rằng bất kỳ biểu mẫu truy vấn chuẩn nào sẽ chạy nhanh hơn đáng kể so với bất kỳ thao tác nào thông qua khung tổng hợp hoặc bất kỳ toán tử được tính toán nào.