Trova un documento con un array che contiene un valore specifico


499

Se ho questo schema ...

person = {
    name : String,
    favoriteFoods : Array
}

... dove l' favoriteFoodsarray è popolato con stringhe. Come posso trovare tutte le persone che hanno il "sushi" come cibo preferito usando la mangusta?

Speravo in qualcosa del genere:

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

(So ​​che non esiste $containsin mongodb, spiegando solo cosa mi aspettavo di trovare prima di conoscere la soluzione)

Risposte:


693

Come favouriteFoodsè un semplice array di stringhe, puoi semplicemente interrogare direttamente quel campo:

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

Ma consiglierei anche di rendere esplicita la matrice di stringhe nel tuo schema:

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

La documentazione pertinente è disponibile qui: https://docs.mongodb.com/manual/tutorial/query-arrays/


19
Questo funziona anche se favouriteFoodsè:favouriteFoods:[{type:Schema.Types.ObjectId, ref:'Food'}]
k88074,

12
Come qualcuno nuovo di Mongo proveniente da un RDBMS come MySQL, scoprire che tali soluzioni funzionano in modo così semplice senza la necessità di JOIN e tabelle aggiuntive mi fa chiedermi perché non ho iniziato su Mongo prima. Ma questo non vuol dire che DBMS sia superiore rispetto all'altro: dipende dal tuo caso d'uso.
Irvin Lim,

9
Non confonderlo. Anche se è un elenco di dict, puoi comunque interrogarlo in questo modo. Campione: PersonModel.find({ favouriteFoods.text: "sushi" }, ...); person = { name : String, favouriteFoods : [{text:String}] }
Aminah Nuraini,

3
Cosa succede quando voglio trovare un array che contenga almeno due stringhe?
Aero Wang

151

Non c'è $containsoperatore in mongodb.

Puoi usare la risposta di JohnnyHK mentre funziona. L'analogia più vicina a contiene quella di mongo è $in, usando questo la tua query sarebbe simile a:

PersonModel.find({ favouriteFoods: { "$in" : ["sushi"]} }, ...);

10
È corretto? Mongodb non si aspetta una matrice di valori quando si usa $ in? come {name: {$ in: ["Paul", "Dave", "Larry", "Adam"]}}?
Ludwig Magnusson,

38
Eh? Questo non è necessario. $inviene utilizzato quando si hanno più valori di query e il documento deve corrispondere a uno di essi. Per il contrario (che è questa domanda), la risposta di JohnnyHK è corretta. Avevo intenzione di sottovalutare, ma credo che questa risposta possa essere utile per altre persone che finiscono in questa pagina.
MalcolmOcean

4
Ma questo mi ha aiutato a interrogare effettivamente con diversi valori: D Molte grazie!
Alexandre Bourlier,

11
Grazie. Questo è quello che stavo effettivamente cercando, il modo di cercare più valori:PersonModel.find({favouriteFoods: {"$in": ["sushi", "hotdog"]}})
totymedli,

@MalcolmOcean è corretto, in quanto l'operatore $ in è per il contrario, con un array come valore . Il campo essendo un array è ciò di cui si sta ponendo la domanda. Tuttavia, se sia il campo che il valore sono array, allora sia questa risposta che JohnnyHK sono rilevanti, il che significa che hai bisogno di $ in.
salta il

88

Penso che $allsarebbe più appropriato in questa situazione. Se stai cercando una persona che ama il sushi, fai:

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

Poiché potresti voler filtrare di più la tua ricerca, in questo modo:

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

$inè come OR e $allcome AND. Controlla questo: https://docs.mongodb.com/manual/reference/operator/query/all/


Siamo spiacenti, questa è una risposta errata alla mia domanda. Non sto cercando una corrispondenza esatta, ma solo array che contengano almeno il valore specificato.
Ludwig Magnusson,

18
Questa è una risposta perfettamente valida alla tua domanda! Per un valore non vi è alcuna differenza nell'uso di $ all o $ in. Se hai diversi valori come "sushi", "banane", $ all è alla ricerca di persone che hanno "sushi" E "banane" nella loro gamma di alimenti preferita, se usando $ in stai ottenendo persone che hanno "sushi" O "banane" "nel loro assortimento alimentare preferito.
Jodo,

sì, non c'è $ contiene ma $ tutto è una specie di esso
datdinhquoc

3
La migliore risposta
Nikolay Tsenkov,

65

Nel caso in cui l'array contenga oggetti, ad esempio se favouriteFoodsè un array di oggetti di quanto segue:

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

puoi usare la seguente query:

PersonModel.find({"favouriteFoods.name": "Sushi"});

2
Questa è facilmente la risposta migliore. Molto più facile da usare quando sei di fretta.
Uber Schnoz,

Questa dovrebbe essere la risposta selezionata. Se hai a che fare con una query su una matrice di documenti nidificati in MongoDB, ecco come farlo. Non sono sicuro che sia il più efficiente, ma se è tutto ciò che stai cercando di fare, questo è tutto ciò di cui hai bisogno.
Kyle L.,

32

Nel caso in cui sia necessario trovare documenti che contengono elementi NULL all'interno di una matrice di documenti secondari, ho trovato questa query che funziona abbastanza bene:

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

Questa query è tratta da questo post: array di query MongoDb con valori null

È stata un'ottima scoperta e funziona molto meglio della mia versione iniziale e sbagliata (che si è rivelata efficace solo per le matrici con un elemento):

.find({
    'MyArrayOfSubDocuments': { $not: { $size: 0 } },
    'MyArrayOfSubDocuments._id': { $exists: false }
})

3

Sebbene sia d'accordo con find () è più efficace nel tuo caso d'uso. C'è ancora $ match del framework di aggregazione, per facilitare la query di un gran numero di voci e generare un basso numero di risultati che valgono per te soprattutto per il raggruppamento e la creazione di nuovi file.

  PersonModel.aggregate([
            { 
                 "$match": { 
                     $and : [{ 'favouriteFoods' : { $exists: true, $in: [ 'sushi']}}, ........ ]  }
             },
             { $project : {"_id": 0, "name" : 1} }
            ]);

non funziona con mongodb 4.2 .. rispondi
vimmi il

Che errore stai ricevendo, ti preghiamo di fornire in dettaglio?
Amitesh,

3

L'incase di lookup_food_array è un array.

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

L'incase di lookup_food_array è stringa.

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

1

Per Loopback3 tutti gli esempi forniti non hanno funzionato per me, o con la stessa velocità dell'utilizzo dell'API REST. Ma mi ha aiutato a capire la risposta esatta di cui avevo bisogno.

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


1
Sei un salvavita, grazie! Dove è documentato e l'ho perso? Puoi pubblicare il link per favore? Grazie.
user2078023

-3

Se desideri utilizzare qualcosa come un operatore "contiene" tramite JavaScript, puoi sempre usare un'espressione regolare per quello ...

per esempio. Supponiamo che tu voglia recuperare un cliente con "Bartolomew" come nome

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

So che questo argomento è vecchio, ma per le persone future che potrebbero chiedersi la stessa domanda, un'altra soluzione incredibilmente inefficiente potrebbe essere quella di fare:

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

Questo evita tutte le ottimizzazioni di MongoDB, quindi non utilizzarle nel codice di produzione.


Per curiosità, c'è qualche vantaggio nel farlo in questo modo?
Ludwig Magnusson,

5
Questo è incredibilmente inefficiente rispetto alla risposta accettata; aggira tutta l'ottimizzazione che Mongo mette dietro le quinte per un ritrovamento diretto come nell'accettato.
inconsapevole il

1
Questa è esattamente la risposta di cui avevo bisogno nel mio caso! Grazie "utente" :)
Vasyl Boroviak,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.