MongoDB удаляет элементы в зависимости от элемента перед (повторением)

#arrays #mongodb #mongoose #mongodb-query

#массивы #mongodb #mongoose #mongodb-запрос

Вопрос:

Я хочу удалить элементы ($ unset) из моих объектов MongoDB с условием, если у того же объекта есть аналогичный элемент. Мой объект:

 {
   "_id": "5eabf8b144345b36b00bfbaa",
   "ranktime": [{
      "pos":"2",
      "datum":"Mon May 05 2020 12:22:52 GMT 0200 (GMT 02:00)",
      "source":"SOURCE2"
    },{
      "pos":"1",
      "datum":"Fri May 01 2020 12:23:10 GMT 0200 (GMT 02:00)",
      "source":"SOURCE1"
    },{
      "pos":"37",
      "datum":"Fri May 01 2020 12:25:14 GMT 0200 (GMT 02:00)",
      "source":"SOURCE2"
    },{
      "pos":"12",
      "datum":"Fri May 01 2020 12:25:14 GMT 0200 (GMT 02:00)",
      "source":"SOURCE2"
    },{
      "pos":"37",
      "datum":"Fri May 01 2020 18:45:27 GMT 0200 (GMT 02:00)",
      "source":"SOURCE2"
    }]
}

  

Итак, я хочу удалить запись в, ranktime если ranktime.source == "SOURCE2" и если дата такая же, как с предыдущим объектом. На самом деле мне приходится перебирать отдельные элементы ranktime . Возможно ли это в MongoDB?

Ожидаемый результат был бы:

 {
   "_id": "5eabf8b144345b36b00bfbaa",
   "ranktime": [{
      "pos":"2",
      "datum":"Mon May 05 2020 12:22:52 GMT 0200 (GMT 02:00)",
      "source":"SOURCE2"
    },{
      "pos":"1",
      "datum":"Fri May 01 2020 12:23:10 GMT 0200 (GMT 02:00)",
      "source":"SOURCE1"
    },{
      "pos":"37",
      "datum":"Fri May 01 2020 12:25:14 GMT 0200 (GMT 02:00)",
      "source":"SOURCE2"
    }]
}

  

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

1. Какая у вас версия MongoDB? И почему был удален последний (уникальная дата)

2. Моя версия 4.2.8 и, извините, я имею в виду только ту же дату (День), игнорируя время. Вот почему удаляется последний

Ответ №1:

В принципе, вы можете использовать $reduce для обработки массива и определения предыдущего элемента с помощью операторов $let и $arrayElemAt. Новый синтаксис $set позволяет использовать агрегацию в инструкции update:

 db.col.updateMany({}, [
    {
        $set: {
            ranktime: {
                $reduce: {
                    input: "$ranktime",
                    initialValue: [],
                    in: {
                        $let: {
                            vars: { last: { $arrayElemAt: [ "$$value", -1 ] } },
                            in: {
                                $cond: [ 
                                    { 
                                        $and: [ 
                                            { "$eq": [ "$$last.source", "SOURCE2" ] },
                                            { "$eq": [ { $substr: [ "$$last.datum", 0, 15 ] }, { $substr: [ "$$this.datum", 0, 15 ] } ] },
                                        ]
                                    },
                                    "$$value",
                                    { $concatArrays: [ "$$value", [ "$$this" ] ] }
                                ]
                            }
                        }
                    }
                }
            }
        }
    }
])
  

Пример агрегации

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

1. большое вам спасибо! Это потрясающе. У меня просто другая проблема. Можно ли рассматривать не только последний элемент, но и все элементы в массиве? Может случиться так, что идентичные объекты не сохраняются один за другим. Есть ли у вас какие-либо рекомендации для этого случая? Спасибо!

2. @AlexanderRiedel вы все еще можете использовать тот же подход и ссылаться на $$value который содержит все предыдущие значения или $ranktime ссылаться на все из них

3. Хорошо, насколько я понимаю, вместо $arrayElemAt: [ "$$value", -1 ] я должен, я должен предоставить весь массив. Но настройка vars: { last: "$ranktime } не работает. Есть ли какая-либо документация об $$value переменной в MongoDB? Это не определено в $let инструкции, но в документации для $let говорится, что вы можете использовать только определенные переменные, и, похоже, это также не системная переменная? Спасибо!

4. $$value является результатом предыдущей итерации $reduce, поэтому изначально это будет пустой массив, а затем он будет содержать произведение $concatArrays

5. хорошо, значит, тогда должна сработать ссылка на $ranktime (которая имеет все значения) вместо только $$value , но я почему-то чего-то не хватает? mongoplayground.net/p/xzOFbfz3Zsa изменение находится в строке 13