Trochę dziwne pytanie o wydajność zapytania ... Muszę uruchomić zapytanie, które oblicza całkowitą liczbę dokumentów i może również zwrócić zestaw wyników, który można ograniczyć i przesunąć.
Mam więc w sumie 57 dokumentów, a użytkownik chce, aby 10 dokumentów zostało przesuniętych o 20.
Przychodzą mi do głowy 2 sposoby na zrobienie tego, najpierw zapytanie o wszystkie 57 dokumentów (zwrócone jako tablica), a następnie użycie array.slice zwróci żądane dokumenty. Drugą opcją jest uruchomienie 2 zapytań, pierwszego przy użyciu natywnej metody „count” mongo, a następnie uruchomienie drugiego zapytania przy użyciu natywnych agregatorów $ limit i $ skip mongo.
Jak myślisz, które będzie lepiej skalowane? Robisz to wszystko w jednym zapytaniu lub uruchamiasz dwa oddzielne?
Edytować:
// 1 query
var limit = 10;
var offset = 20;
Animals.find({}, function (err, animals) {
if (err) {
return next(err);
}
res.send({count: animals.length, animals: animals.slice(offset, limit + offset)});
});
// 2 queries
Animals.find({}, {limit:10, skip:20} function (err, animals) {
if (err) {
return next(err);
}
Animals.count({}, function (err, count) {
if (err) {
return next(err);
}
res.send({count: count, animals: animals});
});
});
count()
funkcja w PHP nie bierzelimit
lubskip
pod uwagę, chyba że powiedziano tak właśnie działa jedno zapytanie limitu i pominąć, a następnie coraz licznik powinien dać najbardziej wydajnych rozwiązań tu prawdopodobnie. Jak jednak dowiesz się, że istnieje 57 dokumentów, jeśli nie wykonasz dwóch zapytań, aby policzyć to, co aktualnie tam jest? Czy masz stałą liczbę, która nigdy się nie zmienia? Jeśli nie, będziesz musiał zrobić zarówno pominięcie, jak i ograniczenie, a następnie zliczanie.db.collection.find(<query>).count();
count()
funkcja MongoDB.count()
Funkcja w MongoDB jest stosunkowo powolny, ale nadal jest dość dużo szybciej niż większość odmian po stronie klienta na większych zestawów i może być szybsze niż po stronie klienta liczenia tutaj ewentualnie. Ale ta część jest subiektywna dla twoich własnych testów. Pamiętaj, że wcześniej z łatwością policzyłem tablice o długości 10k, więc może to być szybsza strona klienta, bardzo trudno powiedzieć przy 10k elementach.Odpowiedzi:
Proponuję użyć 2 zapytań:
db.collection.count()
zwróci całkowitą liczbę elementów. Ta wartość jest przechowywana gdzieś w Mongo i nie jest obliczana.db.collection.find().skip(20).limit(10)
tutaj zakładam, że możesz użyć sortowania według jakiegoś pola, więc nie zapomnij dodać indeksu do tego pola. To zapytanie też będzie szybkie.Myślę, że nie powinieneś odpytywać wszystkich pozycji, a następnie wykonać pomiń i weź, bo później, gdy będziesz mieć duże zbiory danych, będziesz miał problemy z przesyłaniem i przetwarzaniem danych.
źródło
.skip()
instrukcja jest ciężka dla procesora, ponieważ przechodzi na początek kolekcji i dociera do wartości określonej w parametrze.skip()
. To może mieć realny wpływ na dużą kolekcję! Ale i tak nie wiem, który z nich jest najcięższy między użyciem,.skip()
czy też zebrać całą kolekcję i przyciąć za pomocą JS ... Co o tym sądzisz?.skip()
. Ta odpowiedź dotyka tego problemu i zaleca użycie filtru w polu daty. Można tego użyć z metodami.skip()
&.take()
. Wydaje się, że to dobry pomysł. Jednak mam problem z pytaniem tego OP, jak uzyskać liczbę wszystkich dokumentów. Jeśli filtr jest używany do zwalczania wpływu na wydajność.skip()
, jak możemy uzyskać dokładną liczbę? Liczba przechowywana w bazie danych nie będzie odzwierciedlać naszego przefiltrowanego zestawu danych.cursor.count()
aby zwrócić liczbę przefiltrowanych zestawów dokumentów (nie wykona zapytania, zwróci liczbę dopasowanych dokumentów). Upewnij się, że właściwości filtru i kolejności są indeksowane i wszystko będzie dobrze.cursor.count()
powinno działać tak, jak wskazał @ user854301. Jednak ostatecznie dodałem punkt końcowy do mojego interfejsu API (/api/my-colllection/stats
), którego użyłem do zwrócenia różnych statystyk dotyczących moich kolekcji za pomocą funkcji db.collection.stats Mongoose . Ponieważ naprawdę potrzebowałem tego tylko dla mojego interfejsu, po prostu zapytałem punkt końcowy, aby zwrócić te informacje niezależnie od mojej paginacji po stronie serwera.Zamiast używać dwóch oddzielnych zapytań, możesz użyć
aggregate()
w jednym zapytaniu:Zagregowane „$ facet” można pobrać szybciej, całkowitą liczbę i dane z pominięciem i ograniczeniem
db.collection.aggregate([ //{$sort: {...}} //{$match:{...}} {$facet:{ "stage1" : [ {"$group": {_id:null, count:{$sum:1}}} ], "stage2" : [ { "$skip": 0}, {"$limit": 2} ] }}, {$unwind: "$stage1"}, //output projection {$project:{ count: "$stage1.count", data: "$stage2" }} ]);
wyjście w następujący sposób: -
[{ count: 50, data: [ {...}, {...} ] }]
Zajrzyj również na https://docs.mongodb.com/manual/reference/operator/aggregation/facet/
źródło
Po samodzielnym rozwiązaniu tego problemu chciałbym skorzystać z odpowiedzi użytkownika854301.
Mongoose ^ 4.13.8 Udało mi się użyć funkcji o nazwie,
toConstructor()
która pozwoliła mi uniknąć wielokrotnego budowania zapytania po zastosowaniu filtrów. Wiem, że ta funkcja jest również dostępna w starszych wersjach, ale musisz sprawdzić dokumentację Mongoose, aby to potwierdzić.Poniższe wykorzystuje obietnice Bluebird:
let schema = Query.find({ name: 'bloggs', age: { $gt: 30 } }); // save the query as a 'template' let query = schema.toConstructor(); return Promise.join( schema.count().exec(), query().limit(limit).skip(skip).exec(), function (total, data) { return { data: data, total: total } } );
Teraz zapytanie zliczające zwróci całkowitą liczbę dopasowanych rekordów, a zwrócone dane będą podzbiorem wszystkich rekordów.
Zwróć uwagę na () wokół query (), które konstruuje zapytanie.
źródło
Jest biblioteka, która zrobi to wszystko za Ciebie, sprawdź mongoose-paginate-v2
źródło
db.collection_name.aggregate([ { '$match' : { } }, { '$sort' : { '_id' : -1 } }, { '$facet' : { metadata: [ { $count: "total" } ], data: [ { $skip: 1 }, { $limit: 10 },{ '$project' : {"_id":0} } ] // add projection here wish you re-shape the docs } } ] )
Zamiast używać dwóch zapytań, aby znaleźć całkowitą liczbę i pominąć dopasowany rekord.
$ facet to najlepszy i zoptymalizowany sposób.
źródło