Удалить объект из вложенного массива по нескольким критериям - PullRequest
0 голосов
/ 06 мая 2018

У меня есть этот документ схемы в mongoDB:

{
    "_id": UUID("cf397865-c000-4f51-8959-1aae84769706"),
    "CreationDateTime": ISODate("2016-05-06T05:09:14.589Z"),
    "WKT": "",
    "Distributions": [{
            "_id": UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
            "DeliveryType": 1,
            "DistributionData": [{
                    "Key": "Topic",
                    "Value": "Topics",
                    "Children": null
                }, {
                    "Key": null,
                    "Value": null,
                    "Children": null
                }, {
                    "Key": "Message",
                    "Value": "test",
                    "Children": null
                }
            ],
            "Schedules": [
                ISODate("2016-05-06T05:09:56.988Z")
            ]
        }
    ],
}

Я хочу очистить свою базу данных. Итак, чтобы я решил удалить нулевые объекты в DistributionData. Как я могу удалить объект, у которого все три атрибута имеют нулевое значение:

 {
  "Key": null,
  "Value": null,
  "Children": null
 }.

Я написал этот запрос:

db.Events.update(
    {},
    {$pull:
        {
            results: {
                $elemMatch: {
                    "Distributions[0].DistributionData" : {$in:[null]}
                }
            }
        }
     },
     { multi: true }
)

Когда я выполняю этот запрос, ничего не происходит! Я знаю $elemMatch это ошибка .. Теперь, как я могу удалить объект json, у которого все поля равны нулю в DistributionData ?? Я читаю это и это , но меня смущает ...

Редактировать

Я написал этот запрос:

db.Events.update(
    {},
    {$pull:
        {
            Distributions : {
                DistributionData:{
                $elemMatch: {
                    "Key" : null
                }
              }
            }
        }
     },
     { multi: true }
)

Этот запрос полностью удалит объект внутри Distributions, в массиве DistributionData которого был объект с нулевым ключом:

Результат

{
"_id": UUID("cf397865-c000-4f51-8959-1aae84769706"),
"CreationDateTime": ISODate("2016-05-06T05:09:14.589Z"),
"WKT" : "",
"Distributions" : [],...

1 Ответ

0 голосов
/ 06 мая 2018

Вы можете $pull «первое совпадение» из «внешнего массива», удалив «все внутренние элементы», просто выполнив:

db.Events.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$.DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

Это хорошо, если у вас когда-либо есть только одна запись в массиве "Distributions" или, по крайней мере, только у одной из этих записей есть записи дочернего массива, которые соответствуют условию. Вот как позиционный $ оператор работает со всеми версиями MongoDB.

Если данные будут иметь «множественные» совпадения во «внешнем» массиве "Distributions", тогда, если у вас MongoDB 3.6, вы можете применить оператор $[<identifier>] позиционной фильтрации *1015* , чтобы изменить все совпадающие записи:

db.Events.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$[element].DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "arrayFilters": [
      { "element.DistributionData": {
        "$elemMatch": {
          "Key": null,
          "Value": null,
          "Children": null
        }
      }}
    ]
  }
)

В этом случае опция arrayFilters определяет условие, с помощью которого мы сопоставляем записи во «внешнем» массиве, так что это может фактически применяться ко всему, что сопоставляется.

Или действительно, поскольку $pull, по сути, сам имеет эти условия, тогда вы можете поочередно просто использовать оператор позиционный все $[] в этом случае:

db.Event.updateMany(
  {
    "Distributions.DistributionData": {
      "$elemMatch": {
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  },
  {
    "$pull": {
      "Distributions.$[].DistributionData": { 
        "Key": null,
        "Value": null,
        "Children": null
      }
    }
  }
)

В любом случае документ в вопросе изменяется, удаляя внутренний элемент со всеми null ключами:

{
        "_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
        "CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
        "WKT" : "",
        "Distributions" : [
                {
                        "_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
                        "DeliveryType" : 1,
                        "DistributionData" : [
                                {
                                        "Key" : "Topic",
                                        "Value" : "Topics",
                                        "Children" : null
                                },
                                {
                                        "Key" : "Message",
                                        "Value" : "test",
                                        "Children" : null
                                }
                        ],
                        "Schedules" : [
                                ISODate("2016-05-06T05:09:56.988Z")
                        ]
                }
        ]
}

Все условия «запроса» используют $elemMatch для выбора документа. На самом деле это требуется для позиционного $ оператора , чтобы получить «индекс позиции», используемый для «первого совпадения». Хотя это на самом деле не является «требованием» к оператору с позиционной фильтрацией $[<identifier>] или оператору по позиционному все $[], это все же полезно, поэтому вы даже не рассматриваете документы для обновления, которое не будет соответствовать более поздним условиям обновления параметров $pull или arrayFilters.

Что касается самого $pull, то здесь условия фактически применяются к «каждому» элементу массива, поэтому в этой операции нет необходимости в $elemMatch, так как мы уже смотрят на уровень "элемент".

Третий пример показывает, что позиционный оператор all $[] может просто использовать эти условия $pull с учетом каждого "внутреннего" элемента массива и будет применяться только к ALL «внешние» элементы массива. Таким образом, фактическая точка выражения с позиционной фильтрацией $[<identifier>] состоит в том, чтобы «обрабатывать» только те «внешние» элементы массива, которые фактически соответствуют «внутреннему» условию. Следовательно, почему мы используем $elemMatch при рассмотрении соответствия каждого «внутреннего» элемента массива.


Если у вас, по крайней мере, нет MongoDB 3.6, то вы используете первую форму и, вероятно, будете повторять ее до тех пор, пока обновления не вернут больше никаких измененных документов, указывающих на то, что больше не осталось элементов, соответствующих условию.

По мере приближения есть гораздо более подробное описание "альтернатив". Как обновить несколько элементов массива в mongodb , но при условии, что ваши данные либо соответствуют первоначальному случаю, либо у вас действительно есть MongoDB 3.6 доступен, тогда это правильный подход.


Если вы хотите увидеть полный эффект нового синтаксиса для MongoDB 3.6. это изменение документа в вопросе, который я использовал для проверки утверждений об обновлении здесь:

{
    "_id" : UUID("cf397865-c000-4f51-8959-1aae84769706"),
    "CreationDateTime" : ISODate("2016-05-06T05:09:14.589Z"),
    "WKT" : "",
    "Distributions" : [
            {
                    "_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
                    "DeliveryType" : 1,
                    "DistributionData" : [
                            {
                                    "Key" : "Topic",
                                    "Value" : "Topics",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            },
                            {
                                    "Key" : "Message",
                                    "Value" : "test",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            }
                    ],
                    "Schedules" : [
                            ISODate("2016-05-06T05:09:56.988Z")
                    ]
            },
            {
                    "_id" : UUID("bb95bedb-4baa-4ada-90b1-0d763e70ebfe"),
                    "DeliveryType" : 1,
                    "DistributionData" : [
                            {
                                    "Key" : "Topic",
                                    "Value" : "Topics",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            },
                            {
                                    "Key" : "Message",
                                    "Value" : "test",
                                    "Children" : null
                            },
                            {
                                    "Key" : null,
                                    "Value" : null,
                                    "Children" : null
                            }
                    ],
                    "Schedules" : [
                            ISODate("2016-05-06T05:09:56.988Z")
                    ]
            }
    ]
}

Что в основном дублирует некоторые записи как "external", так и "inner", чтобы показать, как оператор удаляет все значения null.

NOTE arrayFilters указаны в аргументе "options" для .update() и аналогичных методов, синтаксис, как правило, совместим со всеми последними версиями драйверов и даже с версиями, предшествующими выпуску MongoDB 3.6. .

Однако это не относится к оболочке mongo, так как при реализации метода там («по иронии судьбы для обратной совместимости») аргумент arrayFilters не распознается и удаляется внутренним методом, который анализирует параметры в чтобы обеспечить «обратную совместимость» с предыдущими версиями сервера MongoDB и «устаревший» .update() синтаксис вызова API.

Таким образом, если вы хотите использовать команду в оболочке mongo или других продуктах, основанных на «оболочке» (особенно Robo 3T), вам нужна последняя версия из ветки разработки или производственной версии начиная с версии 3.6 или выше.

В частности, Robo 3T по-прежнему привязан к оболочке MongoDB 3.4. Таким образом, даже при подключении к работающему экземпляру MongoDB 3.6 эти опции не будут переданы на сервер из этой программы. Рекомендуется использовать только оболочку и поддерживаемые продукты, хотя есть и другие предложения, которые не имеют таких ограничений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...