2011-06-27 18 views
9

Używam mongodb na mojej platformie blogów, gdzie użytkownicy mogą tworzyć własne blogi. Wszystkie wpisy ze wszystkich blogów znajdują się w zbiorze wpisów. Dokument wpisu wygląda następująco:Mongodb: Wybierz górne N wierszy z każdej grupy

{ 
    'blog_id':xxx, 
    'timestamp':xxx, 
    'title':xxx, 
    'content':xxx 
} 

Jak wskazuje pytanie, czy istnieje sposób wyboru, powiedzmy, ostatnie 3 wpisy dla każdego bloga?

Odpowiedz

1

Jedynym sposobem, aby to zrobić w podstawowej Mongo, czy można żyć z dwóch rzeczy:

  • dodatkowe pole w dokumencie wejścia, nazwijmy to „wiek”
  • nowy wpis w blogu odbywających dodatkowa zmiana

Jeśli tak, oto jak to zrobić:

  1. Po utworzeniu ne Włóż do zwykłej wkładki, a następnie wykonaj tę aktualizację, aby zwiększyć wiek wszystkich wpisów (w tym tego, który właśnie wstawiłeś dla tego bloga):

    db.entries.update ({blogi: BLOG_ID}, {wiek: { $ inc: 1}}, false, true)

  2. Podczas odpytywania, należy użyć następującego zapytania, które zwróci ostatnie 3 wpisy dla każdego bloga:

    db.entries.find ({wiek: {$ LTE: 3}, datownik {$ gte: STARTOFMONTH, $ LT. ENDOFMONTH}}), rodzaj ({blog_id: 1, wiek: 1})

Należy pamiętać, że to rozwiązanie jest w rzeczywistości bezpieczne dla współbieżności (brak wpisów o zduplikowanych grupach wiekowych).

+0

Masz pomysł. Nie myślałem o niczym podobnym. Dodatkowa aktualizacja podczas tworzenia nowego posta nie stanowiłaby problemu. Jednak gdy użytkownik usunie wpis, będziemy musieli zaktualizować pole "wiek" wszystkich pozostałych wpisów. Ta aktualizacja może być ograniczona tylko wtedy, gdy usunięty post ma "wiek" <= 3 jednak. Czy brakuje mi czegoś? – Tacaza

+0

Tak, nie powinieneś ograniczać tej aktualizacji do wieku <3, ponieważ skończy Ci się zduplikowany wiek. Aktualizacje w miejscu są niezwykle szybkie, więc nie powinno to stanowić problemu. Usunięcie oznacza usunięcie wpisu i zmniejszenie wieku o 1 w przypadku wieku> usunięty_post.age. Powodzenia. –

+0

To absolutnie ma sens. Dzięki za Twoją sugestię! – Tacaza

0

Jest to możliwe z grupą (agregacja), ale spowoduje to skanowanie w pełnym widoku.

Czy naprawdę potrzebujesz dokładnie 3 lub czy możesz ustawić limit ... np .: maksymalnie 3 posty z ostatniego tygodnia/miesiąca?

+0

Idealnie chcę wybrać Dokładnie 3, ale max 3 posty od ostatniego miesiąca może być wystarczająco dobre, jeśli nie mogę znaleźć rozwiązanie oprócz denormalizacji danych. Czy możesz podać mi przykład tego, jak można to zrobić? Z całej mapy Mongodb zredukuj samouczki, które przeczytałem, pokazują tylko, jak obliczyć statystyki (agregacja) ... – Tacaza

1

Ta odpowiedź używając mapy zmniejszyć przez drcosta z innym pytaniem wystarczyły

In mongo, how do I use map reduce to get a group by ordered by most recent

mapper = function() { 
    emit(this.category, {top:[this.score]}); 
} 

reducer = function (key, values) { 
    var scores = []; 
    values.forEach(
    function (obj) { 
     obj.top.forEach(
     function (score) { 
      scores[scores.length] = score; 
     }); 
    }); 
    scores.sort(); 
    scores.reverse(); 
    return {top:scores.slice(0, 3)}; 
} 

function find_top_scores(categories) { 
    var query = []; 
    db.top_foos.find({_id:{$in:categories}}).forEach(
    function (topscores) { 
     query[query.length] = { 
     category:topscores._id, 
     score:{$in:topscores.value.top} 
     }; 
    }); 
    return db.foo.find({$or:query}); 
8

Musisz najpierw uporządkować dokumenty w kolekcji przez pola blog_id i timestamp, zrób wstępny grupa, która tworzy tablicę oryginalnych dokumentów w porządku malejącym. Następnie możesz wyciąć tablicę z dokumentami, aby zwrócić pierwsze 3 elementy.

Intuicja może być stosowana w tym przykładzie:

db.entries.aggregate([ 
    { '$sort': { 'blog_id': 1, 'timestamp': -1 } }, 
    {  
     '$group': { 
      '_id': '$blog_id', 
      'docs': { '$push': '$$ROOT' }, 
     } 
    }, 
    { 
     '$project': { 
      'top_three': { 
       '$slice': ['$docs', 3] 
      } 
     } 
    } 
]) 
+0

dziękuję, genialny –

Powiązane problemy