2012-11-23 9 views
24

Zakładając Mam kolekcję o nazwie „posty” (w rzeczywistości jest to bardziej skomplikowane kolekcja, słupki jest zbyt proste) o następującej strukturze:

> db.posts.find() 

{ "id" : ObjectId("50ad8d451d41c8fc58000003"), "title" : "Lorem ipsum", "author" : 
"John Doe", "content" : "This is the content", "tags" : [ "SOME", "RANDOM", "TAGS" ] } 

spodziewam tej kolekcji na setki tysięcy, być może miliony, które muszę wysłać do postów według tagów i pogrupować wyniki według tagów i wyświetlić stronę w paginacji. To jest, gdy ramy agregacja przychodzi Planuję użyć metody agregat() do kwerendy kolekcji:.

db.posts.aggregate(
    { "unwind" : "$tags" }, 
    { "group" : { 
     _id: { tag: "$tags" }, 
     count: { $sum: 1 } 
    } } 
); 

Połów jest, że aby stworzyć paginator chciałbym wiedzieć długość tablicy wyjściowej. Wiem, że to zrobić:

db.posts.aggregate(
    { "unwind" : "$tags" }, 
    { "group" : { 
     _id: { tag: "$tags" }, 
     count: { $sum: 1 } 
    } } 
    { "group" : { 
     _id: null, 
     total: { $sum: 1 } 
    } } 
); 

Ale to odrzuciłoby wyjście z poprzedniego potoku (pierwsza grupa). Czy istnieje sposób połączenia obu operacji przy zachowaniu danych wyjściowych każdego potoku? Wiem, że dane wyjściowe całej operacji agregującej mogą być rzutowane na tablicę w jakimś języku, a zawartość jest zliczana, ale może istnieć możliwość, że wyjście potoku przekroczy limit 16 MB. Również wykonanie tego samego zapytania tylko w celu uzyskania liczby wydaje się być marnotrawstwem.

Czy otrzymujesz wynik dokumentu i liczysz w tym samym czasie? Każda pomoc jest doceniana.

+2

Czy naprawdę potrzebny jest całkowicie dokładne całkowitą liczbę, albo będzie przybliżenie zrobić? Z drugiej strony wygląda na to, że liczysz wszystkie posty, więc czy nie jest to tylko operacja count(), którą możesz wykonać? – cirrus

+0

W rzeczywistości liczę na grupę postów, więc count() nie zrobi. – MervS

+0

idealne rozwiązanie, aby uzyskać całość, zachowując wynik w potoku agregacji http://stackoverflow.com/a/39784851/3666966 –

Odpowiedz

30
  1. Zastosowanie $project zapisać tag i count do tmp
  2. Użyj $push lub addToSet, aby zapisać tmp na liście data.

Kod:

db.test.aggregate(
    {$unwind: '$tags'}, 
    {$group:{_id: '$tags', count:{$sum:1}}}, 
    {$project:{tmp:{tag:'$_id', count:'$count'}}}, 
    {$group:{_id:null, total:{$sum:1}, data:{$addToSet:'$tmp'}}} 
) 

wyjściowa:

{ 
    "result" : [ 
      { 
        "_id" : null, 
        "total" : 5, 
        "data" : [ 
          { 
            "tag" : "SOME", 
            "count" : 1 
          }, 
          { 
            "tag" : "RANDOM", 
            "count" : 2 
          }, 
          { 
            "tag" : "TAGS1", 
            "count" : 1 
          }, 
          { 
            "tag" : "TAGS", 
            "count" : 1 
          }, 
          { 
            "tag" : "SOME1", 
            "count" : 1 
          } 
         ] 
       } 
     ], 
     "ok" : 1 
} 
+0

Nie jestem świadomy, że możesz zawrzeć wiele pól w nowym polu w potoku '$ project'. Dokładnie tego potrzebuję. Dzięki. – MervS

+1

Czy jest możliwe sortowanie $, $ skip lub $ limit danych przy użyciu tej metody? Nie możesz ograniczyć danych przed projektem $, a nie widzę sposobu, żeby to zrobić po nie tracąc "sumy". Również {$ sort: {'data.count': 1}} nie działa tak jak zwykle w zapytaniach nieagregatowych. –

3

Nie jestem pewien, trzeba ramy agregacji za to inne niż licząc wszystkie znaczniki np

db.posts.aggregate(
    { "unwind" : "$tags" }, 
    { "group" : { 
     _id: { tag: "$tags" }, 
     count: { $sum: 1 } 
    } } 
); 

Dla Stronicowanie za pośrednictwem tagu można po prostu użyć zwykłej składni zapytań - tak:

db.posts.find({tags: "RANDOM"}).skip(10).limit(10) 
+0

To zadziała, jeśli szukam wszystkich postów z tagiem "RANDOM", ale faktycznie grupuję posty ich znaczniki i wynikowa grupa to to, czego potrzebuje stronicowanie. – MervS

+0

Ah, nie byłem tego pewien! – Ross