2016-08-25 9 views
13

Jedną z moich kolekcji jest lista operacji (lub zadań) o pewnym statusie. Na przykład, lista dokumentów może wyglądać następującoSortowanie MongoDB z niestandardowym wyrażeniem lub funkcją

{ 
    _id: '57befe57fc956d2e3252890c', 
    task: 'Go buy milk', 
    status: 'pending' 
}, 
{ 
    _id: '57befe5efc956d2e3252890d', 
    task: 'Cook dinner', 
    status: 'complete' 
}, 
{ 
    _id: '57befe5efc956d2e3252890e', 
    task: 'Play games', 
    status: 'new' 
} 

chcę uporządkować tę listę w oparciu o ich status, gdzie new > pending > complete.

Jak mogę to zrobić w MongoDB bez konieczności tworzenia dodatkowego pola? Pytam, jak w moim przypadku, kolejność sortowania może być wstępnie skonfigurowana (tj. Użytkownicy mogą mieć swoje preferencje na przykład na pending > new > complete).

+0

Czy istnieje powód, dlaczego nie można obsługiwać t sortuje po stronie klienta? Najlepiej jest zwrócić wszystkie dokumenty, a następnie użyć filtra do utworzenia podgrup na podstawie kolejności preferencji użytkownika. https://lodash.com/docs#filter – dyouberg

+1

Można to zrobić za pomocą potoku agregacji z dwoma potokami , '$ project' i' $ sort'. '$ Project' utworzy dodatkowe pole, na przykład' score', które zostanie wypełnione na podstawie wstępnie skonfigurowanego zamówienia z operatorem '$ cond', więc na przykład dla powyższego wyniku dla' status', "nowy" będzie wynosił 3, dla oczekując 2 itd., potok '$ sort' posortuje w polu wyniku. – chridam

+1

@dyouberg Używam stronicowania, a sortowanie musi zostać wykonane przed '$ limit', po stronie serwera, w przeciwnym razie paginacja jest pomieszana. Dlaczego sugerujesz pobieranie wszystkich elementów do klienta? To złe wykorzystanie przepustowości i przetwarzania. –

Odpowiedz

6

Na podstawie tego, co powiedział @chirdam, jest to, jak wygląda implementacja. Również nowe posortowane pole nie jest obecne w żądanym wyniku.

DataSet:

{ 
    _id: '57befe57fc956d2e3252890c', 
    task: 'Go buy milk', 
    status: 'pending' 
}, 
{ 
    _id: '57befe5efc956d2e3252890d', 
    task: 'Cook dinner', 
    status: 'complete' 
}, 
{ 
    _id: '57befe5efc956d2e3252890e', 
    task: 'Play games', 
    status: 'new' 
} 

Zapytanie:

db.task.aggregate([ 
    { "$project" : { 
     "_id" : 1, 
     "task" : 1, 
     "status" : 1, 
     "order" : { 
      "$cond" : { 
       if : { "$eq" : ["$status", "new"] }, then : 1, 
       else : { "$cond" : { 
        "if" : { "$eq" : ["$status", "pending"] }, then : 2, 
        else : 3 
        } 
       } 
      } 
     } 
    } }, 
    {"$sort" : {"order" : 1} }, 
    { "$project" : { "_id" : 1, "task" : 1, "status" : 1 } } 
]) 

wyjściowa:

{ "_id" : "57befe5efc956d2e3252890e", "task" : "Play games", "status" : "new" } 
{ "_id" : "57befe57fc956d2e3252890c", "task" : "Go buy milk", "status" : "pending" } 
{ "_id" : "57befe5efc956d2e3252890d", "task" : "Cook dinner", "status" : "complete" } 
+0

możesz użyć [$ addFields] (https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/) zamiast projektu, jeśli nie chcesz ponownie dodawać istniejących pól. – mido

+3

Zamiast wielu sekwencji if-then-else można po prostu umieścić żądaną kolejność w tablicy i użyć wyrażenia "$ indexOfArray" do wygenerowania pola sortowania. Oto przykład: http://www.kamsky.org/stupid-tricks-with-mongodb/using-34-aggregation-to-return-documents-in-same-order-as-in-expression –

+0

@AsyaKamsky you powinien dodać to jako odpowiedź. Jestem pewien, że ludzie mogą przegapić twój komentarz i to całkiem dobre rozwiązanie. –