2010-06-28 26 views
5

Soy un novato de MongoDB y quería preguntar cómo escribir un comando de actualización que involucra upsert y list.MongoDB - upsert implica listas

Básicamente lo que quiero lograr algo como esto:

{"_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"}] 
    } 
]} 

Tenga en cuenta que:

  • proporciona un nombre de viaje y la ubicación actual
  • Si el viaje no existe ya, es debe crearse
  • viajes.nombre debe ser único para que si existe ts, se agrega a la matriz de ubicación

Esta es la consulta que escribí combinando el operador posicional con $ push.

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

Pero esto se traduce en datos de la siguiente manera:

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

Se puede ver que

  • "viajes" no es una matriz
  • que tomó "$", literalmente, y creó una clave con eso (doh!)

Hasta ahora he estado muy contento con MongoDB, pero definitivamente hay una gran curva de aprendizaje con la escritura de consultas complicadas.

Cualquier comentario será apreciado.

Gracias de antemano, Amie

+0

comprobar mi respuesta a continuación de nuevo - he editado para incluir lo que yo creo que es la solución (o al menos algo que debe llegar muy cerca) – nearlymonolith

+0

vea esto [SO Thread] (http://stackoverflow.com/questions/17994552/mongodb-update-documents-in-an-array/17995495#17995495). algo similar a esto. – user10

Respuesta

4

Puede no mezcle el operador posicional ("$") y un postre; el "$" se tratará como un nombre de campo durante el inserto. No puede hacer esto para documentos nuevos, solo uno existente.

me sugirió una estructura de la misma familia:

{"_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"}] 
    } 
} 

A continuación, se puede emitir una actualización de esta manera:

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

resultado de esto se inserta.

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

y funcionando de nuevo darle esto:

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

Esto está muy cerca de lo que traté de lograr. Gracias @Scott Hernandez. Con este esquema, ¿es posible recuperar una lista de claves en los "viajes"? ("2010-050-10", etc.) db.mycollection.find ({"application_id": "MytestApp"}, {"trips": true}) devuelve todo el hash "trips". – Grnbeagle

+0

Terminé yendo con este método. Gracias por la solución Scott! – Grnbeagle

+0

El problema es que no se puede buscar algo dentro de un viaje, como encontrar documentos con un viaje en lat 21.321231 (ya que mongo (hoy) no tiene forma de 'wild-card' de la ruta al campo). – Nick

1

vez haya incluido en CORRECTO SOLUCIÓN

Este es exactamente el problema que golpeó el aprendizaje Mongo - que busca la $addToSet operador (see docs here) que se utiliza con el update comando, junto con el operador de posición $ que estaba utilizando.

$ addToSet

{$ addToSet: {campo: valor}}

valor se suma a la matriz sólo si no es que ya están en la matriz.

La consulta se convierte en (db.pila es la colección que he usado para propósitos de prueba), ejecución de ejemplo a seguir:

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

TEST RUN (con algunas abreviaturas para el espacio de los elementos que no son importantes):

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

Gracias por la respuesta @Anthony Morelli. Intenté $ addToSet y tuve que pasar en upsert = true para insertar un viaje por primera vez. Cuando cambié el nombre del viaje a otra cosa, esto realmente crea un nuevo documento en lugar de usar el documento actual. ¿Cómo podemos especificar que "viajes" es una matriz? – Grnbeagle

+0

¿Qué consulta ejecutó específicamente? $ addToSet solo funciona con matrices, por lo que no creo que esa especificación sea el problema. – nearlymonolith

+0

Ejecuté su consulta exactamente, y no creó un documento, así que pasé en upsert = true. Después de crear el primer documento, cuando se ejecutó por segunda vez, creó un segundo documento en lugar de anexarlo al anterior. – Grnbeagle