2010-06-28 19 views
5

Sono un newbie MongoDB e volevo chiedere come scrivere un comando di aggiornamento che coinvolge upsert e list.MongoDB - upsert che coinvolge le liste

Fondamentalmente io voglio realizzare qualcosa di simile:

{"_id" : ObjectId("4c28f62cbf8544c60506f11d"), 
"some_other_data":"goes here", 
"trips": [ 
    {"name": "2010-05-10", 
    "loc": [{"lat":21.321231, "lng": 16.8783234, "updated_at": "Mon May 10 2010 15:24:35"}, 
     {"lat":21.321231, "lng": 16.8783234, "updated_at": "Mon May 10 2010 15:24:24"}] 
    }, 
    {"name": "2010-05-08", 
    "loc": [{"lat":21.324239, "lng": 16.8735234, "updated_at": "Mon May 8 2010 11:18:05"}, 
     {"lat":21.311234, "lng": 16.8743271, "updated_at": "Mon May 8 2010 11:17:55"}, 
     {"lat":21.321238, "lng": 16.8782219, "updated_at": "Mon May 8 2010 11:17:45"}] 
    } 
]} 

Nota che:

  • Si fornisce un nome viaggio e la posizione corrente
  • Se il viaggio non esiste già, it deve essere creato
  • trips.name deve essere univoco in modo che se è già ts, si aggiunge alla matrice posizione

Questa è la query che ho scritto che combina l'operatore posizionale con $ spinta.

db.mycollection.update({"application_id": "MyTestApp", 
          "trips.name": "2010-05-10"}, 
          {$push: {'trips.$.loc': {"lat":11, "lng":11} }}, 
          true); 

Ma questo si traduce in dati come questo:

> db.mycollection.find({"application_id":"MyTestApp"})   
{ "_id" : ObjectId("4c29044ebf8544c60506f11f"), 
"application_id" : "MyTestApp", 
"trips" : { "$" : { "loc" : [ { "lat" : 11, "lng" : 11 } ] }, 
"name" : "2010-05-10" } 
} 

si può vedere che

  • "viaggi" non è una matrice
  • ci sono voluti "$", letteralmente e ha creato una chiave con quello (doh!)

Finora sono stato abbastanza felice con MongoDB, ma c'è sicuramente una curva di apprendimento ripida con la scrittura di query complicate.

Qualsiasi feedback sarà apprezzato.

Grazie in anticipo, Amie

+0

leggi la mia risposta qui sotto ancora una volta - ho modificato per includere quello che ritengo essere la soluzione (o almeno qualcosa che dovrebbe arrivare molto vicino) – nearlymonolith

+0

vedere questo [Thread SO] (http://stackoverflow.com/questions/17994552/mongodb-update-documents-in-an-array/17995495#17995495). qualcosa di simile a questo. – user10

risposta

4

Puoi mescolare l'operatore posizionale ("$") e un upsert; il "$" sarà trattato come un nome di campo durante l'inserimento. Non puoi farlo per i nuovi documenti, solo quelli esistenti.

ho suggerito una struttura più simile a questo:

{"_id" : ObjectId("4c28f62cbf8544c60506f11d"), 
"some_other_data":"goes here", 
"trips": { 
    "2010-05-10": 
     [{"lat":21.321231, "lng": 16.8783234, "updated_at": "Mon May 10 2010 15:24:35"}, 
     {"lat":21.321231, "lng": 16.8783234, "updated_at": "Mon May 10 2010 15:24:24"}], 
    "2010-05-08": 
     [{"lat":21.324239, "lng": 16.8735234, "updated_at": "Mon May 8 2010 11:18:05"}, 
     {"lat":21.311234, "lng": 16.8743271, "updated_at": "Mon May 8 2010 11:17:55"}, 
     {"lat":21.321238, "lng": 16.8782219, "updated_at": "Mon May 8 2010 11:17:45"}] 
    } 
} 

Quindi è possibile rilasciare un aggiornamento del genere:

db.mycollection.update({application_id: "MyTestApp", "trips.2010-05-10":{$exists:true}}, 
         {$push: {"trips.2010-05-10": {lat:11, lng:11} }}, 
         true); 

risultati in questo sta inserendo.

> db.mycollection.find() 
{ "_id" : ObjectId("4c2931d17b210000000045f0"), 
    "application_id" : "MyTestApp", 
    "trips" : { "2010-05-10" : [ { "lat" : 11, "lng" : 11 } ] } } 

ed eseguirlo volta vi darà questa:

> db.mycollection.find() 
{ "_id" : ObjectId("4c2932db7b210000000045f2"), 
    "application_id" : "MyTestApp", 
    "trips" : { "2010-05-10" : 
     [ { "lat" : 11, "lng" : 11 }, 
      { "lat" : 11, "lng" : 11 } ] } } 
+0

Questo è molto vicino a quello che ho cercato di ottenere. Grazie a @Scott Hernandez. Con questo schema, è possibile recuperare un elenco di chiavi nei "viaggi"? ("2010-050-10", ecc.) Db.mycollection.find ({"application_id": "MytestApp"}, {"trips": true}) restituisce l'intero hash "trips". – Grnbeagle

+0

Ho finito per andare con questo metodo. Grazie per la soluzione Scott! – Grnbeagle

+0

Il problema è che non è possibile interrogare qualcosa durante un viaggio, come trovare documenti con un viaggio a lat 21.321231 (dato che mongo (oggi) non ha modo di "wild-card" parte del percorso per il campo). – Nick

1

modificato per includere soluzione corretta

Questo è esattamente il problema che ha colpito l'apprendimento Mongo - si sta cercando il $addToSet dell'operatore (see docs here) che è utilizzato con il Comando update, in combinazione con l'operatore di posizione $ che si stava utilizzando.

$ addToSet

{$ addToSet: {campo: valore}}

valore si aggiunge alla serie solo se il suo non è nella matrice già.

La query diventa così (db.stack è la collezione che ho usato a scopo di test), esempio del suo funzionamento a seguire:

db.stack.update({ "trips.name":"2010-05-10" }, 
       { $addToSet: { "trips.$.loc":{"lat":11, "lng":12} } } 
       ); 

ESECUZIONE DELLA PROVA (con alcune abbreviazioni per lo spazio degli elementi che non sono importanti):

#### YOUR ITEM IN THE DB 
> db.stack.find({"trips.name":"2010-05-10"}) 
{ "_id" : ObjectId("4c28f62cbf8544c60506f11d"), "some_other_data" : "goes here", 
    "trips" : [ 
    { "name" : "2010-05-10", 
     "loc" : [ { 
       "lat" : 21.321231, 
       "lng" : 16.8783234, 
       "updated_at" : "Mon May 10 2010 15:24:35" 
      }, { "lat" : 21.321231, 
       "lng" : 16.8783234, 
       "updated_at" : "Mon May 10 2010 15:24:24" 
      } ] }, 
    { "name" : "2010-05-08", 
     "loc" : [ ... ] 
    } ] } 
#### SUCCESSFULLY ADDS ITEM TO PROPER ARRAY 
> db.stack.update({"trips.name":"2010-05-10"}, {$addToSet: {"trips.$.loc":{"lat":11, "lng":11}}}); 
> db.stack.findOne() 
{ "_id" : ObjectId("4c28f62cbf8544c60506f11d"), "some_other_data" : "goes here", 
    "trips" : [ 
     { "loc" : [ 
       { "lat" : 21.321231, 
        "lng" : 16.8783234, 
        "updated_at" : "Mon May 10 2010 15:24:35" 
       }, { "lat" : 21.321231, 
        "lng" : 16.8783234, 
        "updated_at" : "Mon May 10 2010 15:24:24" 
       }, { "lat" : 11, 
        "lng" : 11 
       } 
      ], "name" : "2010-05-10" 
     }, 
     { "name" : "2010-05-08", 
      "loc" : [ ... ] 
     } ] } 
#### ON REPEAT RUN DOESN'T ADD NEW ELEMENT 
> db.stack.update({"trips.name":"2010-05-10"}, {$addToSet: {"trips.$.loc":{"lat":11, "lng":11}}}); 
> db.stack.findOne() 
{ "_id" : ObjectId("4c28f62cbf8544c60506f11d"), "some_other_data" : "goes here", 
    "trips" : [ { 
      "loc" : [ 
       { "lat" : 21.321231, 
        "lng" : 16.8783234, 
        "updated_at" : "Mon May 10 2010 15:24:35" 
       }, { "lat" : 21.321231, 
        "lng" : 16.8783234, 
        "updated_at" : "Mon May 10 2010 15:24:24" 
       }, { "lat" : 11, 
        "lng" : 11 
       } 
      ], "name" : "2010-05-10" 
     }, 
     { "name" : "2010-05-08", 
      "loc" : [ ... ] 
     } ] } 
#### BUT WILL CORRECTLY ADD ANOTHER ELEMENT TO THE SAME ARRAY IF IT'S NOT PRESENT 
> db.stack.update({"trips.name":"2010-05-10"}, {$addToSet: {"trips.$.loc":{"lat":11, "lng":12}}}); 
> db.stack.findOne() 
{ "_id" : ObjectId("4c28f62cbf8544c60506f11d"), "some_other_data" : "goes here", 
    "trips" : [ 
     { "loc" : [ 
       { "lat" : 21.321231, 
        "lng" : 16.8783234, 
        "updated_at" : "Mon May 10 2010 15:24:35" 
       }, { "lat" : 21.321231, 
        "lng" : 16.8783234, 
        "updated_at" : "Mon May 10 2010 15:24:24" 
       }, { "lat" : 11, 
        "lng" : 11 
       }, { "lat" : 11, 
        "lng" : 12 
       } 
      ], "name" : "2010-05-10" 
     }, 
     { "name" : "2010-05-08", 
      "loc" : [ ... ] 
    } ] } 
+0

Grazie per la risposta @Anthony Morelli. Ho provato $ addToSet e ho dovuto passare in upsert = true per inserire un viaggio per la prima volta. Quando ho cambiato il nome del viaggio in qualcos'altro, in realtà questo crea un nuovo documento invece di usare il documento corrente. Come possiamo specificare che "viaggi" è un array? – Grnbeagle

+0

Quale query hai eseguito in modo specifico? $ addToSet funziona solo con gli array, quindi non penso che la specifica sia il problema. – nearlymonolith

+0

Ho eseguito esattamente la query e non ha creato un documento, quindi ho passato in upsert = true. Dopo aver creato il primo documento, quando è stato eseguito per la seconda volta, ha creato un secondo documento invece di aggiungerlo al precedente .. – Grnbeagle

Problemi correlati