Условная проекция MongoDB, основанная на существовании запроса поддокумента в массиве

#javascript #mongodb

Вопрос:

У меня есть схема, в которой свойства могут иметь соответствующие «переопределяющие» документы, хранящиеся в массиве(«переопределяет»)

Напр.

 {
    foo:'original foo',
    overrides: [
        {property:'foo', value:'foo override'},
        {property:'bar', value:'bar override'},
    ]
}
 

Я хочу спроецировать поле для переопределенного значения, если оно существует, в противном случае исходное свойство.

Так что что-то вроде этого

 project: { overrideOrOriginal: {$cond: fooOverrideExists ? fooOverrideValue : originalFooValue }
 

Так что в этом примере я ожидал overrideOrOriginal бы равенства 'foo override' . Если — {property:'foo', value:'foo override'} subDoc не существовал в overrides массиве (или если overrides сам массив даже не существовал)…тогда я ожидал бы переопределения оригинала = ‘оригинальный фу’

Как я могу это сделать? Я подумал, что мне понадобится $exists в тандеме с $cond . Но сложность здесь в том, что я ищу подДок в массиве на основе запроса

Спасибо!

Комментарии:

1. если вы дадите ожидаемый результат, это очень поможет

2. если вы можете объяснить нам, во всех ваших документах есть это поле «foo»? или у каждого документа может быть другое название для «foo»? и вы хотите сделать это во всех документах или для определенного значения «foo»? Добавьте 2-3 документа и опубликуйте ожидаемый результат, иначе мы отправим ответы, предполагающие разные вещи

Ответ №1:

  • $ifNull чтобы проверить, является ли поле пустым, затем верните пустой массив
  • $in чтобы проверить, находится ли «foo» в overrides.property массиве
  • $indexOfArray чтобы получить индекс элемента массива в overrides.property массиве
  • $arrayElemAt чтобы получить элемент по определенному индексу, возвращаемый оператором выше
 let fooOverrideExists = "foo";
db.collection.find({},
{
  overrideOrOriginal: {
    $cond: [
      { 
        $in: [
          fooOverrideExists, 
          { $ifNull: ["$overrides.property", []] }
        ] 
      },
      {
        $arrayElemAt: [
          "$overrides.value",
          { $indexOfArray: ["$overrides.property", fooOverrideExists] }
        ]
      },
      "$foo"
    ]
  }
})
 

Игровая площадка

Комментарии:

1. Это должно сработать, но мне нужно учитывать, если $overrides.property этого не существует. Я пробовал использовать $and в сочетании с вашим $in , но продолжаю получать синтаксические ошибки. Похоже , мне нужно использовать составное выражение внутри $cond , но это тоже не удается. Есть какие-нибудь идеи?

2. Я не совсем вас понимаю, можете ли вы опубликовать пример этого случая в своем вопросе.

3. Пожалуйста, прокомментируйте $overrides это на своей игровой площадке, и вы увидите $in requires an array as a second argument, found: missing ошибку. Учет того, существует ли этот массив, должен это исправить. Итак, если его не существует, я бы хотел $cond потерпеть неудачу и просто вернуть «$foo».

4. Я обновил ответ.

5. это работает для определенного «foo», и только в том случае, если все документы во всей коллекции содержат только «foo» в качестве полей, проверьте, что этот вопрос нуждается в более четком объяснении, я думаю

Ответ №2:

Запрос

  • найдите свойство , значение ключа(kv) (оно работает для всех имен свойств)
    (предполагается, что ваша схема содержит только строковое значение, значение этого свойства).
  • проверяет, существует ли он в массиве переопределений
  • если он существует, принимает значение из массива
  • остальное сохраняет оригинал

*проверяет также случаи, когда переопределение не существует, или его пустой массив, или свойство не существует

*в случае, если вы хотите сделать это только для определенного «foo», сначала проигнорируйте big $set и используйте этот код

Тестовый код здесь

 db.collection.aggregate([
  {
    "$set": {
      "kv": {
        "$arrayElemAt": [
          {
            "$filter": {
              "input": {
                "$objectToArray": "$ROOT"
              },
              "cond": {
                "$eq": [
                  {
                    "$type": "$this.v"
                  },
                  "string"
                ]
              }
            }
          },
          0
        ]
      }
    }
  },
  {
    "$set": {
      "index": {
        "$indexOfArray": [
          "$overrides.property",
          "$kv.k"
        ]
      }
    }
  },
  {
    "$project": {
      "_id": 0,
      "overrideOrOriginal": {
        "$cond": [
          {
            "$or": [
              {
                "$eq": [
                  "$index",
                  -1
                ]
              },
              {
                "$not": [
                  "$overrides"
                ]
              }
            ]
          },
          "$kv.v",
          {
            "$arrayElemAt": [
              "$overrides.value",
              "$index"
            ]
          }
        ]
      }
    }
  }
])