2012-10-14 11 views
8

Sto cercando di imparare MongoDB ed è stato fantastico finora. Tuttavia mi sono imbattuto in una situazione e non sono troppo sicuro di come risolverlo. Spero che qualcuno possa darmi una mano e grazie in anticipo.

Volevo ottenere record che il valore dell'intero array sia all'interno della query. Ad esempio:

registrare 1:

{"name" : "Mango Shake", 
"ingredients" : [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}]} 

scheda 2:

{"name" : "Mango Banana Shake", 
"ingredients" : [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}, 
        {"type" : "fruit", "name" : "banana"}]} 

registrazione 3:

{"name" : "Milk Shake", 
"ingredients" : [{"type" : "milk", "name" : "soy milk"}]} 

quindi avrei una query qualcosa come

{"ingredients" : {$all : [{"type" : "fruit", "name" : "mango"}, 
          {"type" : "milk", "name" : "soy milk"}, 
          {"type" : "fruit", "name" : "strawberry"}]}} 

perché ho mango, latte di soia e fragola. Quindi volevo sapere quali frullati posso fare. Apparentemente questo non restituisce nulla perché la query non può avere roba extra. Se uso $in, tutto tornerà, ma non posso fare mango banana shake perché non ho una banana ..

Quindi quello di cui ho solo bisogno è il primo e l'ultimo. Qualche idea? Apprezzalo :)

+0

Quindi, si desidera creare una query da quegli ingredienti che si hanno a disposizione, restituendo così un set di risultati che include solo le frullate che si potrebbero fare? – chb

+0

sì esattamente .. grazie chb –

+0

avete qualche esigenza come dovrebbe essere una singola query? posso semplicemente restituire il nome/id? –

risposta

0

Non riuscivo a pensare a un modo per farlo con find. Ma potresti farlo con un mapReduce.

La funzione della mappa consente di scorrere gli ingredienti del documento attualmente esaminato e di emetterlo solo quando tutti i campi sono gli ingredienti disponibili. Non hai bisogno di una funzione di riduzione per questo, quindi la chiave dei valori emessi dovrebbe essere la _id dei documenti bevanda.

Quando i tasti sono univoci, la funzione di riduzione non deve mai essere chiamata (la funzione di riduzione viene utilizzata per determinare cosa succede quando vengono emessi più valori per la stessa chiave). Non penso che tu possa semplicemente omettere la funzione di riduzione, quindi dovresti scrivere una che restituisca solo il primo elemento dell'array dei valori.

0

Mappa ridurre funziona se vi piace, oppure si può semplicemente utilizzare $ e con una condizione per ciascuno dei vostri ingredienti:

http://docs.mongodb.org/manual/reference/operator/and/

Ho usato $ e prima per le esigenze di query simili. Sfortunatamente, in entrambi i casi questo dovrà controllare ogni singolo documento di ricetta ogni volta che effettuerai una query che avrà gravi implicazioni sulle prestazioni se la tua raccolta diventerà grande.

Questo può essere gestito meglio con un database relazionale. Per lo meno si dovrebbe prendere in considerazione la possibilità di tenere un indice interrogabile separato degli ingredienti che hanno una mappa degli ID della ricetta che li usano. Quindi puoi recuperare solo gli ingredienti che hai e prendere l'intersezione delle ricette nel codice. Ancora una volta, poiché questo è effettivamente qualcosa che un database relazionale può fare per voi, potrebbe semplicemente essere la scelta migliore qui.

0

Si potrebbe utilizzare dot notation per accedere agli elementi specifici della matrice e verificare che ogni elemento è $in vostra disposizione lista degli ingredienti e si potrebbe costruire un $and query per farlo che per ogni elemento nel documento interrogato ma, naturalmente, che verrà non funziona dato che non sai quanti elementi ci sono nella lista degli ingredienti di ogni ricetta.

Una soluzione a questo problema consiste nell'aggiungere una matrice a ciascun documento di dimensione fissa (forse 3 elementi) in cui si inseriscono gli ingredienti meno comuni della ricetta, il riempimento (con le ripetizioni se necessario per riempire il array). Ora puoi fare una query usando $and e $in contro ogni elemento di quell'array. Questo troverà tutte le ricette che 'potrebbero' essere possibili, e dal momento che hai memorizzato gli ingredienti meno comuni dovrebbe fare un buon lavoro filtrando le ricette che non puoi fare. È quindi possibile fare il filtro finale lato client.

A volte l'aggiunta di una rappresentazione diversa dei dati può essere un buon modo per risolvere un problema di query complesso come questo, ea volte una combinazione di filtri server e lato client può essere appropriata.

0

È possibile utilizzare una funzione javascript come parametro di ricerca. Tuttavia, ritengo che MapReduce sia una soluzione molto più elegante a causa della scalabilità e del disagio generale che viene fornito inserendo l'input dell'utente nel codice che viene eseguito.

Ecco una query di esempio che sarebbe stata costruita. In sostanza, filtra ingredienti dell'elemento e controlla che l'elenco filtrato è la stessa lunghezza della lista degli ingredienti originale:

db.shakes.find(function() { 

    // Implant your query JSON object here 
    var query_obj = [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}, 
        {"type" : "fruit", "name" : "strawberry"}]; 
    return this.ingredients.filter(function(ingredient) { 
    var has_ingredients = false; 
    query_obj.forEach(function(query) { 
     has_ingredients |= (query.type == ingredient.type && query.name == ingredient.name); 
    }); 
    return has_ingredients; 
    }).length === this.ingredients.length; 
}) 
0

Non un modo diretto per farlo. puoi creare una semplice funzione javascript per risolvere la tua ricerca guardando ogni documento e il controllo degli ingredienti dell'array ha tutti e tre gli ingredienti.

db.shakes.find().forEach(

function (shakes) { 
    for (i = 0; i < shakes.ingredients.length; i++) { 
     switch (shakes.ingredients[i].name) { 
     case "banana": 
      var count = 1; 
     case "soy milk": 
      count++; 
     case "mango": 
      count++; 
     } 
     if (count == 3) { 
      print(shakes.name); 
     } 
    } 
} 
); 
0

Per portare una nuova idea, nel caso si abbia un insieme già noto di tutti gli ingredienti. Per esempio i possibili ingredienti sono solo 5:

["mango","soy milk", "strawberry", "banana", "pineapple"] 

E tu non' avere né banane né ananas

Una possibilità è quella di interrogare per gli ingredienti non si dispone di:

{"ingredients" : {$nin : [{"type" : "fruit", "name" : "banana"}, 
          {"type" : "fruit", "name" : "pineapple"}]}} 

Ciò restituisce tutti i documenti che non hanno un ingrediente che non hai, quindi i recikes con gli ingredienti che hai effettivamente.

0

Supponendo di stare bene con lo caveats of using the $where operator, è possibile utilizzarlo, in combinazione con Array.prototype.some e Array.prototype.every per ottenere i risultati desiderati.

Sembra una soluzione specifica per javascript, ma non lo è. Il javascript è iniettato direttamente in Mongo (V8). Tutti i driver di lingua dovrebbero supportarlo. So almeno che PHP does.

Dovresti riuscire a incollare questo codice direttamente nello Mongo REPL e ottenere i risultati che ti aspetti (battere il legno).

Se il database del frullato ha il potenziale per scalare oltre ciò che $where può darti, Aggregation Framework è tuo amico.

var query = { 
    "$where": function() { 
     var myIngredients = [ 
      {"type" : "fruit", "name" : "mango"}, 
      {"type" : "milk", "name" : "soy milk"}, 
      {"type" : "fruit", "name" : "strawberry"} 
     ]; 
     return obj.ingredients.every(function(milkShakeIngredient){ 
      return myIngredients.some(function(myIngredient){ 
       return (milkShakeIngredient.name === myIngredient.name && milkShakeIngredient.type === myIngredient.type); 
      }); 
     }); 
    } 
}; 

db.milkShakes.find(query);