2012-01-15 16 views
9

come da How do I update Array Elements matching criteria in a MongoDB document?Elementi di array Upsert che corrispondono ai criteri in un documento MongoDB?

Voglio aggiornare gli elementi dell'array, quindi se uno non corrisponde, inserirlo, altrimenti aggiornarlo.

Ho provato la risposta a questa domanda e funziona correttamente SE l'elemento dell'array esiste già. Se l'elemento non esiste, crea un figlio di "$" sotto il campo dell'array.

mio Mongo struttura è la seguente:

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

La mia applicazione ottiene un nome del widget e un elenco di proprietà da una chiamata WebService. Desidero ripetere le Proprietà fornite e aggiornare il valore in MongoDB se il Nome esiste già, OPPURE inserire una nuova Proprietà nell'array Proprietà se non lo è.

risposta

10

Ciò che è necessario non è possibile utilizzare un singolo aggiornamento senza alcuna logica lato applicazione. Si noti che l'upsert come caratteristica non è rilevante per questo specifico problema a meno che non si voglia creare automaticamente nuovi documenti Widget se nessuno esiste con il nome fornito.

Il problema che si sta verificando è che non esiste alcuna funzionalità che consente di eseguire due aggiornamenti diversi a seconda dell'esistenza di un elemento di matrice. Le tue uniche due opzioni sono:

  1. Trova l'elemento, determina l'esistenza di proprietà pertinenti, compila un aggiornamento appropriato con il tuo nuovo o modifica le proprietà ed eseguilo. Ciò comporta l'importante svantaggio che questo non è un metodo di concorrenza simultanea. In altre parole, se due servizi Web lo tentano allo stesso modo, potrebbero sovrascrivere i cambiamenti di ciascuno.
  2. Crea le proprietà del widget documenti di livello superiore anziché incorporati. Ti permette di usare gli upserts per fare ciò che vuoi. Il lato negativo evidente è che questa non è una buona opzione in termini di design dello schema. Ad esempio, non otterrai automaticamente tutte le proprietà se recuperi un widget.
+0

Sono arrivato anche a questa conclusione. Ho circa 3500 proprietà per widget e probabilmente oltre 100.000 widget. È un design ragionevole avere le proprietà nella propria collezione? – justacodemonkey

+0

"Upserts" sono possibili se è possibile ristrutturare il documento. Vedi la mia risposta. –

1

Almeno in php funziona per me, prova ad adottare nella tua lingua. è necessaria la raccolta assomigliare:

 {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); 

Inserisce nuovi nomi, se non esiste e aggiorna quelli già ci

+0

Questo è corretto ma non hai davvero spiegato perché questo funziona. Il richiedente deve ristrutturare il proprio documento per utilizzare un oggetto anziché un array. Vedi la mia risposta per maggiori dettagli. (Inoltre, l'uso del flag di upsert farà risalire l'intero documento, ma la domanda riguardava specificamente gli elementi dell'array.) –

4

Non puoi atomicamente elementi dell'array upsert. Ma se riesci a ristrutturare il tuo documento per usare un oggetto invece di un array, allora questo è atomicamente possibile. Utilizzando il la notazione, la struttura sarebbe

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

Un documento esempio potrebbe essere

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

Per upsert una voce denominata 'struttura a 3' con un valore di 300, faresti

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

Uno svantaggio è che l'interrogazione per i nomi dei campi (utilizzando l'operatore di query $exists) richiede la scansione. È possibile aggirare questo aggiungendo un campo array aggiuntivo che memorizza i nomi delle proprietà. Questo campo può essere indicizzato e interrogato normalmente.Upserts diventano

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

(Tenete a mente $addToSet è O (n).) Quando si rimuove una proprietà, è necessario tirarlo dalla matrice pure.

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