Struktura agregacji po prostu nie „obsługuje” tablic w taki sam sposób, jak w przypadku .find()
zapytania w ogóle. Dotyczy to nie tylko operacji takich jak .sort()
, ale także z innymi operatorami, a mianowicie $slice
, chociaż ten przykład zostanie naprawiony (więcej później).
Tak więc jest prawie niemożliwe, aby poradzić sobie z czymkolwiek za pomocą formy "notacji kropkowej" z indeksem pozycji tablicy, tak jak masz. Ale jest na to sposób.
To, co „możesz” zrobić, to po prostu ustalić, czym właściwie jest „n-ty” element tablicy jako wartość, a następnie zwrócić go jako pole, które można posortować:
db.test.aggregate([
{ "$unwind": "$items" },
{ "$group": {
"_id": "$_id",
"items": { "$push": "$items" },
"itemsCopy": { "$push": "$items" },
"first": { "$first": "$items" }
}},
{ "$unwind": "$itemsCopy" },
{ "$project": {
"items": 1,
"itemsCopy": 1,
"first": 1,
"seen": { "$eq": [ "$itemsCopy", "$first" ] }
}},
{ "$match": { "seen": false } },
{ "$group": {
"_id": "$_id",
"items": { "$first": "$items" },
"itemsCopy": { "$push": "$itemsCopy" },
"first": { "$first": "$first" },
"second": { "$first": "$itemsCopy" }
}},
{ "$sort": { "second": -1 } }
])
Jest to okropne i „iterowalne” podejście, w którym zasadniczo „przechodzisz” przez każdy element tablicy, pobierając $first
dopasowanie według dokumentu z tablicy po przetworzeniu za pomocą $ odpręż się
. Następnie po $unwind
ponownie, sprawdzasz, czy te elementy tablicy są takie same jak te, które już „widziano” w zidentyfikowanych pozycjach tablicy.
To straszne i gorzej dla większej liczby pozycji, którymi chcesz się poruszać, ale daje wynik:
{ "_id" : 2, "items" : [ 0, 3, 4 ], "itemsCopy" : [ 3, 4 ], "first" : 0, "second" : 3 }
{ "_id" : 1, "items" : [ 1, 2, 0 ], "itemsCopy" : [ 2, 0 ], "first" : 1, "second" : 2 }
{ "_id" : 3, "items" : [ 2, 1, 5 ], "itemsCopy" : [ 1, 5 ], "first" : 2, "second" : 1 }
Na szczęście nadchodzące wydania MongoDB (takie jak obecnie dostępne w wydaniach deweloperskich) otrzymują "poprawkę" na to. Może nie jest to „idealna” poprawka, której pragniesz, ale rozwiązuje podstawowy problem.
Jest nowy $slice
operator dostępny dla struktury agregacji tam i zwróci wymagane elementy tablicy z indeksowanych pozycji:
db.test.aggregate([
{ "$project": {
"items": 1,
"slice": { "$slice": [ "$items",1,1 ] }
}},
{ "$sort": { "slice": -1 } }
])
Co daje:
{ "_id" : 2, "items" : [ 0, 3, 4 ], "slice" : [ 3 ] }
{ "_id" : 1, "items" : [ 1, 2, 0 ], "slice" : [ 2 ] }
{ "_id" : 3, "items" : [ 2, 1, 5 ], "slice" : [ 1 ] }
Możesz więc zauważyć, że jako „plaster” wynik nadal jest „tablicą”, jednak $sort
w ramach agregacji zawsze używał „pierwszej pozycji” tablicy w celu posortowania zawartości. Oznacza to, że przy pojedynczej wartości wyodrębnionej z indeksowanej pozycji (tak jak w długiej procedurze powyżej) wynik zostanie posortowany zgodnie z oczekiwaniami.
Końcowe przypadki tutaj są takie, jak to działa. Albo żyć z takimi operacjami, jakich potrzebujesz z góry, aby pracować z indeksowaną pozycją tablicy, albo „poczekać”, aż nowa błyszcząca wersja przyjdzie ci na ratunek z lepszymi operatorami.