Как использовать $in или $nin в агрегации mongo $group $cond

Я хочу достичь $ sum с $cond, имея $или на свойстве:

db.collectionName.aggregate(
{
   "$group": {
     "_id":'$created_at',
     "count": {"$sum": 1},
     "count_failure": {
         "$sum": {
           "$cond": [
               {
                 "$id":
                  { "$in": [ 0,100,101,102,103,104,105 ] }
               }, 
               1,
               0
              ] 
           }
         }
    }  
 }
)

но ошибка говорит: Invalid operator "$id"

что случилось с синтаксисом? Или я пишу запрос неправильно.

В настоящее время я достигаю этого:

db.collectionName.aggregate(
{
   "$group": {
     "_id":'$created_at',
     "count": {"$sum": 1},
     "count_failure": {
         "$sum": {
           "$cond": [
               {
                 "$or":[
                  { "$eq": [ "$id", 0 ] },
                  { "$eq": [ "$id", 100 ]},
                  { "$eq": [ "$id", 101 ]},
                  { "$eq": [ "$id", 102 ]},
                  { "$eq": [ "$id", 103 ]},
                  { "$eq": [ "$id", 104 ]},
                  { "$eq": [ "$id", 105 ]}
                 ]
               }, 
               1,
               0
              ] 
           }
         }
   }  
 }
)

3 ответов


по сравнению с $setIsSubset является более коротким вариантом, чем $or условие, которое вы используете, хотя оно по-прежнему в основном действительно для того, что вы делаете.

единственный улов с $setIsSubset заключается в том, что каждый аргумент является массивом, поэтому вам нужно преобразовать один элемент в один массив элементов. Это достаточно легко с помощью $map:

db.collectionName.aggregate([
    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$setIsSubset": [
                        { "$map": {
                            "input": ["A"],
                            "as": "el",
                            "in": "$id"
                        }},
                        [ 0,100,101,102,103,104,105 ],
                    ]},
                    1,
                    0
                ]
            }
        }
    }}    
])

или, если хотите, сопоставьте массив аргументов с сингулярным значением вместо этого, с $anyElementTrue:

db.collectionName.aggregate([
    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$anyElementTrue": { "$map": {
                        "input": [ 0,100,101,102,103,104,105 ],
                        "as": "el",
                        "in": { "$eq": [ "$$el", "$id" ] }
                    }}},
                    1,
                    0
                ]
            }
        }
    }}
])

здесь $map скорее пересекает аргументы, чтобы соответствовать сингулярному, а не принуждает сингулярное в массив.

и конечно, так как любая форма является, по сути, поставляя true/false до $cond тогда вы можете просто изменить логику с $not там, где требуется:

db.collectionName.aggregate([
    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$not": [{ "$anyElementTrue": { "$map": {
                        "input": [ 0,100,101,102,103,104,105 ],
                        "as": "el",
                        "in": { "$eq": [ "$$el", "$id" ] }
                    }}}]},
                    1,
                    0
                ]
            }
        }
    }}
])

это действительно зависит от того, как вы смотрите на это, но просто как предоставленные аргументы вы действительно ничего не получаете по сравнению с оригинальной формой с $or. Это может выглядеть немного чище и" легче вводить", но обычно я не буду" вводить " такую логику в конвейер агрегации напрямую, а скорее генерировать эту часть структуры на основе простого списка в первую очередь:

я.е

var failList = [ 0,100,101,102,103,104,105 ];

var orCondition = failList.map(function(el) { 
    return { "$eq": [ "$id", el ] }
})

а затем просто используя повторно отображенное содержимое массива в определении конвейера:

    { "$group": {
        "_id": "$createdAt",
        "count": { "$sum": 1 },
        "count_failure": {
            "$sum": {
                "$cond": [
                    { "$or": orCondition },
                    1,
                    0
                ]
            }
        }
    }}
])

независимо от того, как вы смотрите на это, помните это всего лишь структуры данных и у вас есть основные процессы манипулирования. Как внутри обработки трубопровода, так и в самой конструкции трубопровода.


Я думаю, что $in не является оператором конвейера агрегации.


сначала вы должны создать список $id С помощью $map в проекте, а затем использовать setIsSubset в группы, как показано ниже :

db.collectionName.aggregate([{
  "$project": {
    "idsArray": {
      "$map": {
        "input": ["A"],
        "as": "el",
        "in": {
          "$cond": [{
              "$eq": ["$$el", "A"]
            }, "$id",
            null
          ]
        }
      }
    }
  }
}, {
  "$group": {
    "_id": "$created_at",
    "count": {
      "$sum": 1
    },
    "count_failure": {
      "$sum": {
        "$cond": [{
            "$setIsSubset": ["$idsArray", [
              0,
              100,
              101,
              102,
              103,
              104,
              105
            ]]
          },
          1,
          0
        ]
      }
    }
  }
}])