2015-02-19 7 views
10

Ho una raccolta di documenti utente, in cui ogni utente può disporre di un insieme arbitrario di proprietà. Ogni utente è associato a un documento dell'app. Ecco un utente di esempio:

{ 
    "appId": "XXXXXXX", 
    "properties": [ 
     { "name": "age", "value": 30 }, 
     { "name": "gender", "value": "female" }, 
     { "name": "alive", "value": true } 
    ] 
} 

Vorrei poter trovare/contare gli utenti in base ai valori delle loro proprietà. Ad esempio, trovami tutti gli utenti dell'app X che hanno proprietà Y> 10 e Z è uguale a true.

Ho un composto, indice multikey su questa collezione db.users.ensureIndex({ "appId": 1, "properties.name": 1, "properties.value": 1}). Questo indice sta lavorando bene per singola condizione query, es:

db.users.find({ 
    appId: 'XXXXXX', 
    properties: { 
     $elemMatch: { 
      name: 'age', 
      value: { 
       $gt: 10 
      } 
     } 
    } 
}) 

La query sopra completa in < 300ms con un insieme di utenti 1M. Tuttavia, quando provo ad aggiungere una seconda condizione, le prestazioni si riducono notevolmente (7-8s) e l'output explain() indica che l'intero indice viene sottoposto a scansione per soddisfare la query ("nscanned" : 2752228).

Query

db.users.find({ 
    appId: 'XXXXXX', 
    properties: { 
     $all: [ 
      { 
       $elemMatch: { 
        name: 'age', 
        value: { 
         $gt: 10 
        } 
       } 
      }, 
      { 
       $elemMatch: { 
        name: 'alive', 
        value: true 
       } 
      } 
     ] 
    } 
}) 

Spiegare

{ 
    "cursor" : "BtreeCursor appId_1_properties.name_1_properties.value_1", 
    "isMultiKey" : true, 
    "n" : 256, 
    "nscannedObjects" : 1000000, 
    "nscanned" : 2752228, 
    "nscannedObjectsAllPlans" : 1018802, 
    "nscannedAllPlans" : 2771030, 
    "scanAndOrder" : false, 
    "indexOnly" : false, 
    "nYields" : 21648, 
    "nChunkSkips" : 0, 
    "millis" : 7425, 
    "indexBounds" : { 
     "appId" : [ 
      [ 
       "XXXXX", 
       "XXXXX" 
      ] 
     ], 
     "properties.name" : [ 
      [ 
       { 
        "$minElement" : 1 
       }, 
       { 
        "$maxElement" : 1 
       } 
      ] 
     ], 
     "properties.value" : [ 
      [ 
       { 
        "$minElement" : 1 
       }, 
       { 
        "$maxElement" : 1 
       } 
      ] 
     ] 
    }, 
    "filterSet" : false 
} 

Presumo che ciò è dovuto al fatto Mongo non è in grado di creare limiti adeguati dal momento che sono alla ricerca di entrambi i valori booleani e interi.

La mia domanda è questa: c'è un modo migliore per strutturare i miei dati o modificare la mia query per migliorare le prestazioni e sfruttare meglio il mio indice? È possibile indicare a mongo di trattare ogni condizione separatamente, generare limiti appropriati e quindi eseguire l'intersezione dei risultati, invece di eseguire la scansione di tutti i documenti? O il mongo non è adatto a questo tipo di casi d'uso?

+0

si utilizza una versione 2.6 di MongoDB , destra? Non riesco a riprodurre questo - in 3.0-rc8, la query è veloce, cioè non esegue la scansione degli oggetti. – mnemosyn

+0

@mnemosyn sì, sto usando v2.6.7 – michaels

+0

Beh, forse 3.0-rc8 può risolvere il tuo problema? Almeno se sei disposto ad usare un candidato in produzione in produzione ... – mnemosyn

risposta

2

So che questa è una vecchia questione, ma penso che sarebbe molto meglio per strutturare i vostri dati senza il "nome" e "valore" tag:

{ 
    "appId": "XXXXXXX", 
    "properties": [ 
     { "age": 30 }, 
     { "gender: "female" }, 
     { "alive": true } 
        ] 
} 
+0

Non possibile per il nostro caso d'uso perché non conosciamo i tasti prima del tempo: sono generati dall'utente e devono essere tutti indicizzati/interrogabili. – michaels