Znajdź dokument z tablicą zawierającą określoną wartość

499

Jeśli mam ten schemat ...

person = {
    name : String,
    favoriteFoods : Array
}

... gdzie favoriteFoodstablica jest zapełniona ciągami. Jak mogę znaleźć wszystkie osoby, które mają „sushi” jako swoje ulubione jedzenie za pomocą mangusty?

Miałem nadzieję na coś w stylu:

PersonModel.find({ favoriteFoods : { $contains : "sushi" }, function(...) {...});

(Wiem, że nie ma $containsw mongodb, wyjaśniając tylko to, czego się spodziewałem, zanim poznałem rozwiązanie)

Ludwig Magnusson
źródło

Odpowiedzi:

693

Podobnie favouriteFoodsjak prosta tablica ciągów, możesz po prostu zapytać bezpośrednio o to pole:

PersonModel.find({ favouriteFoods: "sushi" }, ...);

Ale zaleciłbym również wyraźne zdefiniowanie tablicy ciągów w schemacie:

person = {
    name : String,
    favouriteFoods : [String]
}

Odpowiednią dokumentację można znaleźć tutaj: https://docs.mongodb.com/manual/tutorial/query-arrays/

JohnnyHK
źródło
19
Działa to również, jeśli favouriteFoods:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074,
12
Jako ktoś nowy w Mongo pochodzący z RDBMS, takiego jak MySQL, odkrycie, że takie rozwiązania działają tak prosto, bez potrzeby JOIN i dodatkowych tabel, sprawia, że ​​zastanawiam się, dlaczego wcześniej nie zacząłem na Mongo. Ale to nie znaczy, że jeden z DBMS jest lepszy od drugiego - zależy to od twojego przypadku użycia.
Irvin Lim,
9
Nie pomyl tego. Nawet jeśli jest to lista nagrań, nadal możesz wyszukiwać ją w ten sposób. Próbka: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini,
3
Co się stanie, gdy chcę znaleźć tablicę zawierającą co najmniej dwa łańcuchy?
Aero Wang,
151

W $containsmongodb nie ma operatora.

Możesz użyć odpowiedzi od JohnnyHK, gdy to działa. Najbliższa analogia do tego, co zawiera mongo, polega na $intym, że zapytanie to wyglądałoby następująco:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);
Alistair Nelson
źródło
10
Czy to jest poprawne? Czy mongodb nie oczekuje tablicy wartości podczas używania $ in? jak {name: {$ in: [„Paul”, „Dave”, „Larry”, „Adam”]}}?
Ludwig Magnusson
37
Co? To jest niepotrzebne. $injest używany, gdy masz wiele wartości zapytań, a dokument musi być zgodny z jedną z nich. W przypadku odpowiedzi odwrotnej (o to chodzi w tym pytaniu) odpowiedź JohnnyHK jest poprawna. Chciałem zagłosować, ale ta odpowiedź może być pomocna dla innych osób, które znajdą się na tej stronie.
MalcolmOcean,
4
Ale pomogło mi to faktycznie zapytać o kilka wartości: D Wielkie dzięki!
Alexandre Bourlier
10
Dzięki. Właśnie tego szukałem, sposobu na wyszukiwanie wielu wartości:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli,
@MalcolmOcean ma rację, ponieważ operator $ in jest odwrotny, mając tablicę jako wartość . Pole jest tablicą właśnie pytanie jest pytaniem o. Jeśli jednak zarówno pole, jak i wartość są tablicami, zarówno ta odpowiedź, jak i JohnnyHK są odpowiednie, co oznacza, że ​​potrzebujesz $ in.
tscizzle
87

Wydaje mi się, $allże w tej sytuacji byłoby bardziej odpowiednie. Jeśli szukasz osoby, która jest w sushi, robisz:

PersonModel.find({ favoriteFood : { $all : ["sushi"] }, ...})

Ponieważ możesz chcieć bardziej filtrować wyszukiwanie, na przykład:

PersonModel.find({ favoriteFood : { $all : ["sushi", "bananas"] }, ...})

$injest jak OR i $alljak AND. Sprawdź to: https://docs.mongodb.com/manual/reference/operator/query/all/

Pobe
źródło
Przepraszamy, to jest nieprawidłowa odpowiedź na moje pytanie. Nie szukam dokładnego dopasowania, ale tylko tablice, które zawierają co najmniej określoną wartość.
Ludwig Magnusson,
17
To jest całkowicie poprawna odpowiedź na twoje pytanie! Dla jednej wartości nie ma różnicy w użyciu $ all lub $ in. Jeśli masz kilka wartości, takich jak „sushi”, „banany”, $ all szuka osób, które mają „sushi” ORAZ „banany” w swojej ulubionej tablicy żywności, jeśli za pomocą $ dostajesz osoby, które mają banany „sushi” LUB „banany” w ich ulubionym zestawie potraw.
Jodo
tak, nie ma $ zawiera, ale $ wszystko jest w pewnym sensie
datdinhquoc
2
Najlepsza odpowiedź.
Nikolay Tsenkov
65

W przypadku, gdy tablica zawiera obiekty, na przykład jeśli tablica obejmuje następujące obiekty favouriteFoods:

{
  name: 'Sushi',
  type: 'Japanese'
}

możesz użyć następującego zapytania:

PersonModel.find({"favouriteFoods.name": "Sushi"});
Kfir Erez
źródło
2
To jest z pewnością najlepsza odpowiedź. Znacznie łatwiejszy w użyciu, gdy się spieszysz.
Uber Schnoz
To powinna być wybrana odpowiedź. Jeśli masz do czynienia z zapytaniem o tablicę zagnieżdżonych dokumentów w MongoDB, możesz to zrobić w ten sposób. Nie jestem pewien, czy jest to najbardziej wydajny, ale jeśli to wszystko, co próbujesz zrobić, to wszystko, czego potrzebujesz.
Kyle L.
32

Jeśli potrzebujesz znaleźć dokumenty zawierające elementy NULL w tablicy poddokumentów, znalazłem to zapytanie, które działa całkiem dobrze:

db.collection.find({"keyWithArray":{$elemMatch:{"$in":[null], "$exists":true}}})

To zapytanie pochodzi z tego postu: Tablica zapytań MongoDb z zerowymi wartościami

To było świetne znalezisko i działa znacznie lepiej niż moja własna początkowa i zła wersja (która okazała się działać dobrze tylko dla tablic z jednym elementem):

.find({
    'MyArrayOfSubDocuments': { $not: { $size: 0 } },
    'MyArrayOfSubDocuments._id': { $exists: false }
})
Jesus Campon
źródło
3

Chociaż zgadzanie się z funkcją find () jest najskuteczniejsze w przypadku użycia. Nadal istnieje $ dopasowanie struktury agregacji, aby ułatwić zapytanie o dużą liczbę wpisów i wygenerować małą liczbę wyników, które mają dla ciebie wartość, szczególnie dla grupowania i tworzenia nowych plików.

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);
Amitesh
źródło
nie działa z mongodb 4.2 .. odpowiedz
vimmi
Jaki masz błąd, proszę szczegółowo podać?
Amitesh
3

Incase z lookup_food_array jest tablicą.

match_stage["favoriteFoods"] = {'$elemMatch': {'$in': lookup_food_array}}

Incase z lookup_food_array jest ciągiem.

match_stage["favoriteFoods"] = {'$elemMatch': lookup_food_string}
gautam
źródło
1

W przypadku Loopback3 wszystkie podane przykłady nie działały dla mnie lub tak szybko jak przy użyciu interfejsu API REST. Ale pomogło mi to znaleźć dokładną odpowiedź, której potrzebowałem.

{"where":{"arrayAttribute":{ "all" :[String]}}}

Mark Ryan Orosa
źródło
1
Jesteś ratownikiem życia, dzięki! Gdzie to jest udokumentowane i tęskniłem? Czy możesz opublikować link? Dzięki.
user2078023
-3

Jeśli chcesz użyć czegoś takiego jak operator „zawiera” za pomocą javascript, zawsze możesz użyć do tego wyrażenia regularnego ...

na przykład. Załóżmy, że chcesz odzyskać klienta o nazwie „Bartolomew” jako nazwy

async function getBartolomew() {
    const custStartWith_Bart = await Customers.find({name: /^Bart/ }); // Starts with Bart
    const custEndWith_lomew = await Customers.find({name: /lomew$/ }); // Ends with lomew
    const custContains_rtol = await Customers.find({name: /.*rtol.*/ }); // Contains rtol

    console.log(custStartWith_Bart);
    console.log(custEndWith_lomew);
    console.log(custContains_rtol);
}
Alingenomen
źródło
-26

Wiem, że ten temat jest stary, ale dla przyszłych ludzi, którzy mogliby zastanawiać się nad tym samym pytaniem, innym niezwykle nieefektywnym rozwiązaniem mogłoby być:

PersonModel.find({$where : 'this.favouriteFoods.indexOf("sushi") != -1'});

Pozwala to uniknąć wszystkich optymalizacji MongoDB, więc nie używaj go w kodzie produkcyjnym.

użytkownik3027146
źródło
Z ciekawości, czy jest jakaś zaleta robienia tego w ten sposób?
Ludwig Magnusson
5
Jest to niezwykle nieefektywne w porównaniu z przyjętą odpowiedzią; omija całą optymalizację, którą Mongo umieszcza za kulisami, aby znaleźć proste, jak w przyjętym.
nieświadomie
1
To jest dokładnie odpowiedź, której potrzebowałem w moim przypadku! Dziękuję „użytkownikowi” :)
Vasyl Boroviak