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)

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/


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"]} }, ...);

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/


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"});

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 }
})

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} }
            ]);

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}

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]}}}


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);
}

-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.


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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.