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.