0

Я использую агрегацию MongoDB в метеор.Не удается получить правильный результат при использовании агрегации MongoDB в метеор

Элементы в базе данных выглядит следующим образом:

// item1

{ 
    products: { 
    aaa: 100, 
    bbb: 200 
    } 
} 

// item2

{ 
    products: { 
    aaa: 300, 
    bbb: 400 
    } 
} 

Мой трубопровод выглядит следующим образом

let pipeline = [{ 
    $limit: 10 
    }, { 
    $group: { 
     _id: { 
     // … 
     }, 
     total: { 
     $sum: "$products.aaa" 
     } 
    } 
    }]; 

, и это работает кт. Но когда я изменить структуру базы данных для этого

// item1

{ 
    products: [ 
    {code: "aaa", num: 100}, 
    {code: "bbb", num: 200} 
    ] 
} 

// item2

{ 
    products: [ 
    {code: "aaa", num: 300}, 
    {code: "bbb", num: 400} 
    ] 
} 

Результаты, которые я получил за total всегда 0, я думаю, что мой трубопровод неправильно. Пожалуйста, см. Комментарий внутри:

let pipeline = [{ 
    $limit: 10 
    }, { 
    $group: { 
     _id: { 
     // … 
     }, 
     total: { 
     $sum: "$products.0.num" // Neither this nor "$products[0].num" works 
     } 
    } 
    }]; 

Так как я могу написать его правильно? Благодаря

ответ

1

С MongoDB 3.2 (который не будет в комплекте с сервером метеора, но там, отметив, останавливая вас используя экземпляр сервера отдельный И на самом деле было бы рекомендовать.), Вы можете использовать $arrayElemAt с $map:

let pipeline = [ 
    { "$limit": 10 }, 
    { "$group": { 
     "_id": { 
     // … 
     }, 
     "total": { 
     "$sum": { "$arrayElemAt": [ 
      { "$map": { 
       "input": "$products", 
       "as": "product", 
       "in": "$$product.num" 
      }}, 
      0 
     ]} 
     } 
    }} 
]; 

С более старыми версиями используйте «два» этапа $group и оператор $first после обработки с помощью $unwind. И это только для «первого» значения индекса:

let pipeline = [ 
    { "$limit": 10 }, 
    { "$unwind": "$products" }, 
    { "$group": { 
     "_id": "$_id",  // The document _id 
     "otherField": { "$first": "$eachOtherFieldForGroupingId" }, 
     "productNum": { "$first": "$products.num" } 
    }}, 
    { "$group": { 
     "_id": { 
     // … 
     }, 
     "total": { 
     "$sum": "$productNum" 
     } 
    }} 
]; 

Таким образом, в последнем случае, после того, как вы $unwind вы просто хотите использовать $first, чтобы получить «первый» индекс из массива, и он также будет использоваться чтобы получить каждое поле, которое вы хотите использовать как часть ключа группировки из исходного документа. Все элементы будут скопированы для каждого элемента массива после $unwind.

В первом случае $map просто извлекает значения "num" для каждого элемента массива, тогда $arrayElemAt просто извлекает нужную позицию индекса.

Естественно, более новый метод для MongoDB 3.2 лучше. Если вам нужен другой индекс массива, вам нужно будет повторно получить элемент $first из массива и продолжать фильтровать его из результатов массива, пока не достигнет необходимого индекса.

Таким образом, хотя это возможно в более ранних версиях, работать там очень много.

+0

Мой метеорит использует монго 2.6.7. Поэтому я использую второй способ. Сначала я не понимал роль '$ eachOtherFieldForGroupingId', но теперь я получил его! Спасибо, работает отлично! –

+0

@HongboMiao Извините, если это был абстрактный термин, но было также заявление * «...и он также будет использоваться для получения каждого поля, которое вы хотите использовать как часть ключа группировки из исходного документа ». * Поместите туда, чтобы уточнить. Наиболее распространенная ошибка конвейера агрегации - это люди, которые не понимают, что если вы хотите ссылаться поле на более позднем этапе после '$ project' или' $ group', тогда на самом деле будут доступны только те поля, которые вы действительно просили, чтобы первое использование для «emit» было действительно доступно. –

+0

@HongboMiao. В будущем вы получите более четкое например, если вы действительно включаете образец документа и ** полное ** выражение вашего кода, а не '' _id ": {...}}, которое является неоднозначным и фактически не показывает ссылки, на которые ссылаются. Также указано" факт " , * "... который не будет связанным сервером с метеоритом, но стоит остановить использование отдельного экземпляра сервера * *, который, хотя может быть« простым », просто запускать связанный сервер с« метеор »внутри ваш проект в разработке, развертывания в реальном мире будут использовать внешний сервер. Вы должны привыкнуть к этому. –