Wszystkie moje rekordy mają pole o nazwie „zdjęcia”. To pole jest tablicą ciągów.
Chcę teraz 10 najnowszych rekordów, w których ta tablica NIE JEST pusta.
Grzebałem w Internecie, ale o dziwo nie znalazłem wiele na ten temat. Przeczytałem opcję $ where, ale zastanawiałem się, jak powolne jest to dla funkcji natywnych i czy istnieje lepsze rozwiązanie.
I nawet wtedy to nie działa:
ME.find({$where: 'this.pictures.length > 0'}).sort('-created').limit(10).execFind()
Nic nie zwraca. Pozostawienie this.pictures
bez bitu długości działa, ale potem zwraca również puste rekordy.
mongoengine
ME.find({ pictures: { $gt: [] } })
JEST NIEBEZPIECZNY, nawet w nowszych wersjach MongoDB. Jeśli masz indeks na polu listy i ten indeks jest wykorzystywany podczas zapytania, otrzymasz nieoczekiwane wyniki. Na przykład:db.doc.find({'nums': { $gt: [] }}).hint({ _id: 1 }).count()
zwraca właściwą liczbę, podczas gdydb.doc.find({'nums': { $gt: [] }}).hint({ nums: 1 }).count()
zwraca0
.Po kilku bardziej zaglądających, szczególnie w dokumentach mongodb i zagadkach razem, oto odpowiedź:
źródło
pictures
pola.Może to również działać dla Ciebie:
źródło
pictures.2
istnieje, alepictures.1
nie istnieje ?$exists
Operator jest wartością logiczną, a nie offset. @tenbatsu powinien używaćtrue
zamiast1
.Would there ever be a case where pictures.2 exists but pictures.1 does not?
Tak, taka sprawa może się zdarzyć.pictures
jest to dokument podrzędny, a nie tablica. np.pictures: {'2': 123}
pictures
.Podczas zapytań zależy Ci na dwóch rzeczach - dokładności i wydajności. Mając to na uwadze, przetestowałem kilka różnych podejść w MongoDB v3.0.14.
TL; DR
db.doc.find({ nums: { $gt: -Infinity }})
jest najszybszy i najbardziej niezawodny (przynajmniej w testowanej wersji MongoDB).EDYCJA: To już nie działa w MongoDB v3.6! Zobacz komentarze pod tym postem, aby znaleźć potencjalne rozwiązanie.
Ustawiać
Wstawiłem 1k dokumentów bez listy, 1k dokumentów z pustą listą i 5 dokumentów z niepustą listą.
Zdaję sobie sprawę, że nie jest to wystarczająca skala, aby traktować wydajność tak samo poważnie, jak w poniższych testach, ale wystarczy przedstawić poprawność różnych zapytań i zachowanie wybranych planów zapytań.
Testy
db.doc.find({'nums': {'$exists': true}})
zwraca złe wyniki (za to, co próbujemy osiągnąć).-
db.doc.find({'nums.0': {'$exists': true}})
zwraca poprawne wyniki, ale jest również powolne przy użyciu pełnego skanowania kolekcji (uwagaCOLLSCAN
na wyjaśnienie).-
db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}})
zwraca złe wyniki. Wynika to z nieprawidłowego skanowania indeksu, który nie przesyła żadnych dokumentów. Prawdopodobnie będzie dokładny, ale powolny bez indeksu.-
db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}})
zwraca poprawne wyniki, ale wydajność jest zła. Technicznie wykonuje skanowanie indeksu, ale następnie przesuwa wszystkie dokumenty, a następnie musi je przefiltrować).-
db.doc.find({'nums': { $exists: true, $ne: [] }})
zwraca prawidłowe wyniki i jest nieco szybszy, ale wydajność nadal nie jest idealna. Używa IXSCAN, który przesuwa dokumenty tylko z istniejącym polem listy, ale następnie musi odfiltrowywać puste listy jeden po drugim.-
db.doc.find({'nums': { $gt: [] }})
JEST NIEBEZPIECZNY, PONIEWAŻ W ZALEŻNOŚCI OD WYKORZYSTYWANEGO INDEKSU MOŻE Dawać NIEPODZIEWANE WYNIKI. Wynika to z nieprawidłowego skanowania indeksu, który nie przesyła żadnych dokumentów.-
db.doc.find({'nums.0’: { $gt: -Infinity }})
zwraca prawidłowe wyniki, ale ma niską wydajność (używa pełnego skanowania kolekcji).-
db.doc.find({'nums': { $gt: -Infinity }})
co zaskakujące, działa to bardzo dobrze! Daje właściwe wyniki i jest szybki, przesuwając 5 dokumentów z fazy skanowania indeksu.źródło
seen_events
tablicę String, która również jest indeksowana. Wyszukiwanie za pomocą{ $gt: -Infinity }
, natychmiast otrzymuję 0 dokumentów. Używając{ $exists: true, $ne: [] }
, otrzymuję bardziej prawdopodobne dokumenty o wartości 1,2db.test_collection.find({"seen_events.0": {$exists: true}})
jest zły, ponieważ używa skanowania kolekcji. 2.db.test_collection.find({seen_events: {$exists: true, $ne: []}})
jest źle, ponieważ jego IXSCAN pasuje do wszystkich dokumentów, a następnie filtrowanie odbywa się w wolnej fazie pobierania. 3. To samo dotyczydb.test_collection.find({seen_events: {$exists: true, $not: {$size: 0}}})
. 4. Wszystkie pozostałe zapytania zwracają nieprawidłowe wyniki.seen_events
zawierają ciągi, można użyć tego:db.test_collection.find({seen_events: {$gt: ''}}).count()
. Aby potwierdzić, że działa dobrze, sprawdźdb.test_collection.find({seen_events: {$gt: ''}}).explain(true).executionStats
. Prawdopodobnie można wymusić, aby widoczne zdarzenia były łańcuchami za pomocą sprawdzania poprawności schematu: docs.mongodb.com/manual/core/schema-validationPocząwszy od wersji 2.6, innym sposobem na to jest porównanie pola z pustą tablicą:
Testowanie w powłoce:
Tak więc poprawnie obejmuje dokumenty, w których
pictures
znajduje się co najmniej jeden element tablicy, i wyklucza dokumenty, w którychpictures
jest pusta tablica, a nie tablica, lub jej brakuje.źródło
db.ME.createIndex({ pictures: 1 })
a następniedb.ME.find({pictures: {$gt: []}})
zwróci zero wyników, przynajmniej w MongoDB v3.0.14Aby to osiągnąć, możesz użyć dowolnego z poniższych.
Obie dbają również o to, aby nie zwracać wyniku dla obiektów, które nie zawierają żądanego klucza:
źródło
Pobierz wszystkie i tylko dokumenty, w których „obrazy” są tablicą i nie są puste
Jeśli używasz wersji MongoDb wcześniejszej niż 3.2 , użyj
$type: 4
zamiast$type: 'array'
. Zauważ, że to rozwiązanie nie używa nawet $ size , więc nie ma problemu z indeksami („Zapytania nie mogą używać indeksów dla części zapytania $ size”)Inne rozwiązania, w tym te (zaakceptowana odpowiedź):
są błędne , ponieważ wracają do dokumentów nawet jeśli, na przykład, „zdjęcia” jest
null
,undefined
, 0, itd.źródło
Użyj
$elemMatch
operatora: zgodnie z dokumentacją$elemMatches
upewnia się, że wartość jest tablicą i że nie jest pusta. Zapytanie byłoby więc podobneME.find({ pictures: { $elemMatch: {$exists: true }}})
PS Wariant tego kodu znajduje się na kursie M121 Uniwersytetu MongoDB.
źródło
Możesz także użyć metody pomocniczej. Istnieje nad operatorem Mongo $ istnieje
źródło
użyj $ where i podaj this.field_name.length, które zwracają rozmiar pola tablicy i sprawdź to przez porównanie z liczbą. jeśli jakakolwiek tablica ma wartość inną niż wielkość tablicy musi wynosić co najmniej 1. więc wszystkie pola tablicy mają długość większą niż jeden, oznacza to, że zawiera pewne dane w tej tablicy
źródło
To proste, to zadziałało dla mnie.
źródło