2015-02-19 17 views
10

Mam kolekcję dokumentów użytkownika, gdzie każdy użytkownik może mieć dowolny zestaw właściwości. Każdy użytkownik jest powiązany z dokumentem aplikacji. Oto przykładowy użytkownik:

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

Chciałbym móc znajdować/liczyć użytkowników na podstawie wartości ich właściwości. Na przykład znajdź dla mnie wszystkich użytkowników aplikacji X, które mają właściwość Y> 10, a Z jest prawdziwe.

Mam złożony, multikolorowy indeks na tej kolekcji db.users.ensureIndex({ "appId": 1, "properties.name": 1, "properties.value": 1}). Wskaźnik ten działa dobrze dla pojedynczych zapytań stanie, ex:

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

Powyższe zapytanie zakończy w < 300ms z kolekcją użytkowników 1m. Jednak gdy próbuję dodać drugi warunek, wydajność znacznie spada (7-8s), a wynik explain() wskazuje, że cały indeks jest skanowany w celu spełnienia zapytania ("nscanned" : 2752228).

Zapytanie

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

Wyjaśnić

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

Zakładam, to dlatego, że Mongo jest w stanie stworzyć odpowiednie granice, ponieważ szukam obu wartości logicznych i całkowitych.

Moje pytanie brzmi: czy istnieje lepszy sposób na uporządkowanie moich danych lub zmodyfikowanie zapytania w celu poprawy wydajności i lepszego wykorzystania mojego indeksu? Czy można nakazać mongo traktować każdy warunek osobno, generować odpowiednie granice, a następnie wykonywać przecięcia wyników, zamiast skanować wszystkie dokumenty? A może mongo po prostu nie nadaje się do tego typu przypadków?

+0

używasz wersji 2.6 MongoDB , dobrze? Nie mogę tego odtworzyć - w 3.0-rc8 zapytanie jest szybkie, tzn. Nie skanuje obiektów. – mnemosyn

+0

@mnemosyn tak, używam v2.6.7 – michaels

+0

Cóż, może 3.0-rc8 może rozwiązać twój problem? Przynajmniej jeśli chcesz użyć kandydata do wydania w wersji produkcyjnej ... – mnemosyn

Odpowiedz

2

wiem, że to jest stare pytanie, ale myślę, że byłoby znacznie lepiej zorganizować swoje dane bez „nazwa” i „wartość” tagi:

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

Nie jest możliwe w naszym przypadku użycia, ponieważ nie znamy kluczy z wyprzedzeniem - są one generowane przez użytkownika i wszystkie muszą być indeksowane/możliwe do zapytania. – michaels

Powiązane problemy