MongoDB сканирует весь индекс при использовании $all и $elemMatch

У меня есть набор пользовательских документов, где каждый пользователь может иметь произвольный набор свойств. Каждый пользователь связан с документом приложения. Вот пример пользователя:

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

Я хотел бы иметь возможность находить/подсчитывать пользователей на основе значений их свойств. Например, найдите всех пользователей для приложения X, у которых есть свойство Y > 10 и Z равно true.

У меня есть составной, мультикидный индекс в этой коллекции db.users.ensureIndex({ "appId": 1, "properties.name": 1, "properties.value": 1}). Этот индекс хорошо работает для запросов с одним условием, например:

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

Вышеупомянутый запрос завершается в < 300 мс с коллекцией пользователей 1М. Однако, когда я пытаюсь добавить второе условие, производительность значительно ухудшается (7-8 с), а вывод explain() указывает, что весь индекс сканируется для выполнения запроса ("nscanned" : 2752228).

Запрос

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

Поясните

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

Я предполагаю, что это потому, что Mongo не может создать подходящие границы, так как я ищу как логические, так и целые значения.

Мой вопрос заключается в следующем: есть ли лучший способ структурирования моих данных или изменить мой запрос для повышения производительности и лучше использовать мой индекс? Можно ли поручить манго обрабатывать каждое условие отдельно, сгенерировать соответствующие границы, а затем выполнить пересечение результатов вместо сканирования всех документов? Или монго просто не подходит для этого типа использования?

Ответ 1

Я знаю, что это старый вопрос, но я думаю, что было бы гораздо лучше структурировать ваши данные без тегов "name" и "value":

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