Есть ли что-то еще в MongoDB для $ cond при агрегировании

Поэтому мне нужно пользовательское поле, рассчитанное в MongoDB следующим образом:

if( field1 =="A")  ->customfield=10
else if(field1 =="B"  )->customfield=20
else (field1 =="C" ) ->customfield=15

Я использую агрегацию вместе с оператором $ project. Но оператор $ cond не позволяет elseif (подветвление else) и просто разрешает две статические ветки if и else. Использование вложенных elseif вызывает

"exception: field inclusion is not allowed inside of $expressions"

Вот мой запрос (который дает мне ошибку)

db.items.aggregate([ { $project :
{
     name: 1,
     customfield:
     {
         $cond: { if: { $eq: [ "$field1", "4" ] }, then: 30,
                else: {
                    if: 
                    { $eq: ["$field1","8"]}, 
                    then: 25, else: 10}}
               }
           }},{ $sort: { customfield: 1 }},{$limit:12}]);

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

Ответ 1

В современных выпусках (начиная с MongoDB 3.4) вы должны использовать $switch, который в основном является аналогом ключевых слов switch или case в других языковых реализациях:

db.items.aggregate([
  { "$project": {
    "name": 1,
    "customfield": {
      "$switch": {
        "branches": [
          { "case": { "$eq": [ "$field1", "4" ] }, "then": 30 },
          { "case": { "$eq": [ "$field1", "8" ] }, "then": 25 }
        ],
        "default": 10
      }
    }
  }},
  { "$sort": { customfield: 1 }},
  { "$limit":12 }
])

Это позволяет избежать вложения условий if..then..else, что можно сделать с помощью $cond и показано ниже. Но ниже все еще показан в качестве примера, что это всегда можно сделать, даже до того, как новый оператор даже явных ключевых слов if..then..else, поскольку исходная запись массива всегда поддерживал этот синтаксис.

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


Ключевые слова if..then..else для оператора $cond являются лишь недавним дополнением к последним версиям MongoDB на момент написания (MongoDB 2.6 было введением ключевых слов. Фактический оператор был доступен с выпуском структуры агрегации в MongoDB 2.2). Намерение было для ясности, но в этом случае это, кажется, вызвало некоторую путаницу.

Поскольку оператор if..then.else $cond действительно является троичным оператором, так же, как это было бы реализовано во многих языках программирования. Это означает, что в качестве "встроенного" условия вместо создания "блоков" логики для условий все, что не соответствует первому условию, относится к else.

Поэтому вы "вкладываете" утверждения, а не следите за блоками:

db.items.aggregate([
  { "$project": {
    "name": 1,
    "customfield": {
      "$cond": { 
        "if": { "$eq": [ "$field1", "4" ] }, 
        "then": 30,
        "else": {
          "$cond": {
            "if": { "$eq": ["$field1","8"]}, 
            "then": 25, 
            "else": 10
          }
        }
      }
    }
  }},
  { "$sort": { customfield: 1 }},
  { "$limit":12 }
]);

Или даже с оригинальной нотацией массива, которую некоторые могут предпочесть при программном построении оператора:

db.items.aggregate([
  { "$project": {
    "name": 1,
    "customfield": {
      "$cond": [
         { "$eq": [ "$field1", "4" ] }, 
         30,
         { "$cond": [
           { "$eq": ["$field1","8"] },
           25, 
           10
         ]}
      ]
    }
  }},
  { "$sort": { customfield: 1 }},
  { "$limit":12 }
]);

Тройной означает три условия, не больше, не меньше. Поэтому вся логика if..then..else должна быть вложенной.

Ответ 2

В MongoDB 3.4 появилась новая вещь, которая называется $switch именно для этого!

$switch: {
   branches: [
      { case: <expression>, then: <expression> },
      { case: <expression>, then: <expression> },
      ...
   ],
   default: <expression>
}

https://docs.mongodb.com/manual/reference/operator/aggregation/switch/

Ответ 3

если вы используете только объект, то

{
    $cond: {
        $and: [
            { if: { $eq: ["$$field1", 'A'] }, then: '10', else: 15 },
            { if: { $eq: ["$$field1", 'B'] }, then: '20', else: 15 },
            { if: { $eq: ["$$field1", 'C'] }, then: '10', else: 15 },
        ]
    }
}

А если вы используете массив, найдите данные

{
    $map: {
        input: "$fields", as: "field1", in: {
            $cond: {
                $and: [
                    { if: { $eq: ["$$field1", 'A'] }, then: '10', else: 15 },
                    { if: { $eq: ["$$field1", 'B'] }, then: '20', else: 15 },
                    { if: { $eq: ["$$field1", 'C'] }, then: '10', else: 15 },
                ]
            }
        }
    }
}