2016-04-13 12 views
5

Wyobraź mamy następujący komplet dokumentów przechowywanych w MongoDB:Grupa Mongo dokumenty autorstwa id i uzyskać najnowszy dokument datownik

{ "fooId" : "1", "status" : "A", "timestamp" : ISODate("2016-01-01T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "1", "status" : "B", "timestamp" : ISODate("2016-01-02T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "1", "status" : "C", "timestamp" : ISODate("2016-01-03T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "2", "status" : "A", "timestamp" : ISODate("2016-01-01T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "2", "status" : "B", "timestamp" : ISODate("2016-01-02T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "3", "status" : "A", "timestamp" : ISODate("2016-01-01T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "3", "status" : "B", "timestamp" : ISODate("2016-01-02T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "3", "status" : "C", "timestamp" : ISODate("2016-01-03T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "3", "status" : "D", "timestamp" : ISODate("2016-01-04T00:00:00.000Z") "otherInfo" : "BAR", ... } 

Chciałbym, aby uzyskać najnowsze statusu dla każdego fooId na podstawie znak czasu. Dlatego mój powrót będzie wyglądać następująco:

{ "fooId" : "1", "status" : "C", "timestamp" : ISODate("2016-01-03T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "2", "status" : "B", "timestamp" : ISODate("2016-01-02T00:00:00.000Z") "otherInfo" : "BAR", ... } 
{ "fooId" : "3", "status" : "D", "timestamp" : ISODate("2016-01-04T00:00:00.000Z") "otherInfo" : "BAR", ... } 

Próbowałem go o to za pomocą agregacji za pomocą operatora group, ale część Zastanawiam się czy jest to prosty sposób, aby uzyskać całość powrócić z agregacji, aby wyglądała tak samo, jak gdybym użył kwerendy wyszukiwania? Wygląda na to, że musisz określić wszystkie pola podczas grupowania, a to nie wydaje się rozszerzalne, jeśli dokumenty mogą mieć opcjonalne pola, które mogą być dla mnie nieznane. Bieżące zapytanie mam wygląda następująco:

db.collectionName.aggregate(
    [ 
    { $sort: { timestamp: 1 } }, 
    { 
     $group: 
     { 
      _id: "$fooId", 
      timestamp: { $last: "$timestamp" }, 
      status: { "$last": "$status" }, 
      otherInfo: { "$last": "$otherInfo" }, 
     } 
    } 
    ] 
) 
+0

Jesteś robiąc to już we właściwy sposób. Oczywiście możesz użyć ['$$ ROOT'] (https://docs.mongodb.org/manual/reference/aggregation-variables/#variable.ROOT) i po prostu umieścić cały dokument w jednej właściwości, ale to nie jest ta sama struktura, teraz jest? Jeśli tak bardzo martwisz się "wypisywaniem" każdego pojedynczego pola, po prostu * "wygeneruj końcową instrukcję potoku' $ group' w kodzie "*. To bardzo prosta rzecz, a wszystkie zapytania MongoDB i wyciągi potokowe agregacji są po prostu "strukturami danych". –

Odpowiedz

1

Można użyć zmiennej systemowej $$ROOT z operatorem $last wrócić ostatniego dokumentu.

db.collectionName.aggregate([  
    { "$sort": { "timestamp": 1 } },  
    { "$group": { 
     "_id": "$fooId", 
     "last_doc": { "$last": "$$ROOT" } 
    }} 
]) 

Oczywiście będzie to ostatni dokument dla każdej grupy jako wartość pola.

{ 
     "_id" : "2", 
     "doc" : { 
       "_id" : ObjectId("570e6df92f5bb4fcc8bb177e"), 
       "fooId" : "2", 
       "status" : "B", 
       "timestamp" : ISODate("2016-01-02T00:00:00Z") 
     } 
} 

Jeżeli nie jesteś zadowolony z tego wyjścia wtedy najlepiej będzie dodać kolejny etap $group do rurociągu po prostu zwraca tablicę tych dokumentów za pomocą operatora $push akumulatora.

db.collectionName.aggregate([  
    { "$sort": { "timestamp": 1 } },  
    { "$group": { 
     "_id": "$fooId", 
     "last_doc": { "$last": "$$ROOT" } 
    }}, 
    { "$group": { 
     "_id": null, 
     "result": { "$push": "$last_doc" } 
    }} 

]) 
+0

'" $ push ":" $ doc "' lub '" $ push ":" $ last_doc "'? thx – mils

3

Jeśli robisz i agregacji, co musisz zrobić, podobny do SQL, co oznacza określenie operacji agregacji na kolumnę, jedyna opcja masz jest użyć operatora

db.test.aggregate(
    [ 
    { $sort: { timestamp: 1 } }, 
    { 
     $group: 
     { 
      _id: "$fooId", 
      timestamp: { $last: "$$ROOT" } 
     } 
    } 
    ] 
); 

Ale $$ROOT będzie zmienić wyjście trochę

{ "_id" : "1", "timestamp" : { "_id" : ObjectId("570e6be3e81c8b195818e7fa"), 
    "fooId" : "1", "status" : "A", "timestamp" :ISODate("2016-01-01T00:00:00Z"), 
    "otherInfo" : "BAR" } } 

Jeśli chcesz przywrócić oryginalny format dokumentu, prawdopodobnie trzeba etap $ projektu po tym

+0

Jeśli istnieje sposób, aby to zrobić bez agregacji, zdecydowanie byłbym zainteresowany widząc to.Dane wyjściowe za pomocą $$ ROOT są zdecydowanie niepożądane, a użycie projektu $ zachowa nazwę pola, w którym wykorzystano $ ROOT. Naprawdę by mi się spodobało, gdyby udało się sprawić, by wynik wyglądał tak, jak gdybyś zrobił normalne zapytanie. – Shark

+0

@Shark Aggregation to najlepszy wybór tutaj. Jak już wspomniałem w mojej odpowiedzi, możesz dodać kolejny etap '$ group' do potoku. – styvane

0

Choć nie ma bezpośredni sposób, aby przywrócić oryginalne dokumenty i nie widzę żadnej wartości, ale spróbuj następujące zapytanie agregacji:

db.collection.aggregate([ 
    {$sort: {fooId:1, timestamp: -1}}, 
    {$group:{_id:"$fooId", doc:{$first:"$$ROOT"}}}, 
    {$project:{_id:0, doc:["$doc"]}} 
]).forEach(function(item){ 

    printjson(item.doc[0]); 

}); 

To zapytanie będzie emitować:

{ 
    "_id" : ObjectId("570e76d5e94e6584078f02c4"), 
    "fooId" : "2", 
    "status" : "B", 
    "timestamp" : ISODate("2016-01-02T00:00:00.000+0000"), 
    "otherInfo" : "BAR" 
} 
{ 
    "_id" : ObjectId("570e76d5e94e6584078f02c8"), 
    "fooId" : "3", 
    "status" : "D", 
    "timestamp" : ISODate("2016-01-04T00:00:00.000+0000"), 
    "otherInfo" : "BAR" 
} 
{ 
    "_id" : ObjectId("570e76d5e94e6584078f02c2"), 
    "fooId" : "1", 
    "status" : "C", 
    "timestamp" : ISODate("2016-01-03T00:00:00.000+0000"), 
    "otherInfo" : "BAR" 
} 
+0

W rzeczywistości używam springframework for mongodb do wykonywania moich zapytań w Javie. W tej chwili mam kilka wyszukiwań mongociplate find, które odwzorowują otrzymane dokumenty z powrotem na obiekt collectionName.class. Chciałbym zrobić coś podobnego dla zapytania agregacji bez konieczności tworzenia kolejnego obiektu warstwy środkowej, który musiałbym następnie zmapować do kolekcjiNazwa.klasa. To główny powód, dla którego jestem wybredny w tej konkretnej rzeczy – Shark

Powiązane problemy