2012-01-15 12 views
9

Según How do I update Array Elements matching criteria in a MongoDB document?Recuperar criterios de coincidencia de elementos de matriz en un documento MongoDB?

Quiero volver a insertar los elementos de la matriz, por lo que si uno no coincide, insértelo, de lo contrario, actualícelo.

Probé la respuesta a esa pregunta, y funciona bien SI el elemento de la matriz ya existe. Si el elemento no existe, entonces crea un elemento secundario de "$" debajo del campo de la matriz.

estructura Mi Mongo es el siguiente:

Widget (collection) 
--Name 
--Properties (array) 
    --Name 
    --Value 

Mi aplicación obtiene un nombre de control y una lista de las propiedades de una llamada WebService. Deseo iterar las Propiedades proporcionadas y actualizar el valor en el MongoDB si el Nombre ya existe, O insertar una nueva Propiedad en el conjunto Propiedades si no lo hace.

Respuesta

10

Lo que necesita no es posible con una sola actualización sin alguna lógica de aplicación. Tenga en cuenta que upsert como característica no es relevante para este problema específico, a menos que desee crear automáticamente nuevos documentos de widgets si no existe ninguno con el nombre proporcionado.

El problema con el que se está encontrando es que no hay ninguna funcionalidad que le permita realizar dos actualizaciones diferentes según la existencia de un elemento de matriz. Sus únicas dos opciones son:

  1. Busque el elemento, determine la existencia de propiedad (es) relevante (s), recopile una actualización apropiada con sus propiedades nuevas o de modificación y ejecútela. Esto viene con la desventaja importante de que este no es un método seguro de concurrencia. En otras palabras, si dos servicios web intentan esto al mismo tiempo, uno podría sobrescribir los cambios de cada uno.
  2. Haga que las propiedades de widgets sean documentos de nivel superior en lugar de incrustadas. Te permite usar postres para hacer lo que quieras. La desventaja obvia es que esa no es una opción muy buena en términos de diseño de esquema. No obtendría automáticamente todas las propiedades si busca un widget, por ejemplo.
+0

También he llegado a esta conclusión. Tengo aproximadamente 3500 propiedades por widget, y probablemente más de 100.000 widgets. ¿Es un diseño sensato tener las propiedades en su propia colección? – justacodemonkey

+0

"Upserts" son posibles si puede reestructurar su documento. Ver mi respuesta –

1

Al menos en php me funciona, intente adoptar a su idioma. Es necesario su colección parezca:

 {Name: x, Properties: {name1:value1, name2:value2, name3:value3}} 

     foreach ($Properties as $name => $value) 
     { 
      $propertiesArray["Properties.$name"] = $value;     
     } 

     $criteria = array('Name' => $name); 
     $operation = array('$set' => $propertiesArray); 
     $options = array('upsert' => true); 

     $collection = $this->_dbh->selectCollection('Widget'); 
     $collection->update($criteria, $operation, $options); 

Inserta nuevos nombres si no existe y actualiza los que ya existen

+0

Esto es correcto pero realmente no explicaste por qué funciona esto. El asker necesita reestructurar su documento para usar un objeto en lugar de una matriz. Ver mi respuesta para más detalles. (Además, el uso de la bandera de inserción levantará todo el documento, pero la pregunta fue específicamente sobre los elementos de la matriz). –

4

no se puede atómicamente elementos de la matriz upsert. Pero si puede reestructurar su documento para usar un objeto en lugar de una matriz, entonces esto es atómicamente posible. Usando su notación, la estructura sería

Widget (collection) 
    --Name 
    --Properties 
    --Property1 
    --Property2 
     . 
     : 

Un documento ejemplo sería

{ 
    name: 'widget-1', 
    properties: { 
     'property-1': 100, 
     'property-2': 200 
    } 
} 

Para upsert un artículo llamado 'propiedad-3' con un valor de 300, que haría

db.widgets.update(
    { name: 'widget-1' }, 
    { $set: { 'properties.property-3': 300 } } 
) 

Una desventaja es que la consulta de nombres de campo (utilizando el operador de consulta $exists) requiere el escaneo. Puede evitar esto agregando un campo de matriz adicional que almacena los nombres de las propiedades. Este campo puede ser indexado y consultado normalmente.Upserts convierten

db.widgets.update(
    { name: 'widget-1' }, 
    { 
     $set: { 'properties.property-3': 300 }, 
     $addToSet: { propertyNames: 'property-3' } 
    } 
) 

(Tenga en cuenta $addToSet es O (n).) Al quitar una propiedad, usted tiene que tirar de ella desde la matriz también.

db.widgets.update(
    { name: 'widget-1' }, 
    { 
     $unset: { 'properties.property-3': true }, 
     $pull: { propertyNames: 'property-3' } 
    } 
) 
Cuestiones relacionadas