чистое решение JavaScript будет выглядеть следующим образом:
function findNested(obj, key, memo) {
var i,
proto = Object.prototype,
ts = proto.toString,
hasOwn = proto.hasOwnProperty.bind(obj);
if ('[object Array]' !== ts.call(memo)) memo = [];
for (i in obj) {
if (hasOwn(i)) {
if (i === key) {
memo.push(obj[i]);
} else if ('[object Array]' === ts.call(obj[i]) || '[object Object]' === ts.call(obj[i])) {
findNested(obj[i], key, memo);
}
}
}
return memo;
}
вот как вы будете использовать эту функцию:
findNested({'aa': 1, 'bb': 2, 'cc': {'d':{'x':9}}, dd:{'d':{'y':9}}}, 'd');
, и результатом будет:
[{x: 9}, {y: 9}]
Итак, давайте начнем с упрощения набора данных, вставим список элементов в нашу коллекцию test
:
var items = [{
_id : 1,
products: [
{
countryId: 1
},
{
countryId: 1
},
{
countryId: 2
},
{
countryId: 4
},
],
sellers: [
{
countryId: 2
},
{
countryId: 2
},
{
countryId: 1
}
]
},
{
_id : 2,
products: [
{
countryId: 2
},
{
countryId: 2
},
{
countryId: 3
}
],
sellers: [
{
countryId: 3
},
{
countryId: 3
},
{
countryId: 2
},
{
countryId: 4
}
]
}];
db.test.insertMany(items);
Затем мы можем использовать этап агрегации $facet
для обработки нескольких конвейеры агрегации, так что давайте начнем с использования конвейера для productsOnCountryCount
.
Для начала нам нужно развернуть все products
в массиве, а затем сопоставить, затем на основе заданного идентификатора страны:
var countryId = 4;
db.test.aggregate([
{ "$unwind" : "$products" },
{ "$match" : { "products.countryId": countryId } }
]).pretty()
{
"_id" : 1,
"products" : {
"countryId" : 4
},
"sellers" : [
{
"countryId" : 2
},
{
"countryId" : 2
},
{
"countryId" : 1
}
]
}
Теперь мы можем просто использовать счет в конце, чтобы получить количество всех продуктов:
db.test.aggregate([
{ "$unwind" : "$products" },
{ "$match" : { "products.countryId": countryId}},
{ "$count": "productsOnCountryCount" }])
{ "productsOnCountryCount" : 1 }
Это наш первый отсортированный конвейер, теперь давайте посмотрим на unavailableProductsCount
:
Все, что нам нужно сделать, это сопоставить, где countryId находится в [ 1110], но не в массиве products
, это может быть достигнуто простым этапом $match
, тогда мы можем просто запустить подсчет сверху:
db.test.aggregate([
{ "$match" : {"sellers.countryId": countryId, "products.countryId" : { $ne: countryId } } },
{ "$count": "unavailableProductsCount" }])
{ "unavailableProductsCount" : 1 }
Теперь у нас есть два конвейера Теперь мы можем использовать стадию $facet
, чтобы соединить их вместе, а затем спроецировать их в более приятную форму:
db.test.aggregate([
{ "$facet": {
"productsOnCountryCount": [
{ "$unwind" : "$products" },
{ "$match" : { "products.countryId": countryId}},
{ "$count": "productsOnCountryCount" },
],
"unavailableProductsCount": [
{ "$match" : {"sellers.countryId": countryId, "products.countryId" : { $ne: countryId } } },
{ "$count": "unavailableProductsCount" }
]
}},
{ "$project": {
"productsOnCountryCount": { "$arrayElemAt": ["$productsOnCountryCount.productsOnCountryCount", 0] },
"unavailableProductsCount": { "$arrayElemAt": ["$unavailableProductsCount.unavailableProductsCount", 0] }
}}
]);
{ "productsOnCountryCount" : 1, "unavailableProductsCount" : 1 }
Я считаю, что лучший способ использовать $facet
- это просто разбить их на сначала трубы меньшего размера, а затем соедините их вместе в конце.