Обходной путь 16 МБ BSON предел для удаления нескольких документов - PullRequest
0 голосов
/ 14 ноября 2018

У меня есть такие данные MongoDB enter image description here

Пожалуйста, посмотрите на последнее поле - время, как вы можете видеть, у меня есть некоторые "дубликаты" данных, которые были отмечены цветом.

Для небольшой базы данных я могу удалить дублирующиеся значения с кодом ниже

var cursor = db.getCollection("light").aggregate([
  {$group : {
    "_id": {
      index: "$index",
      unit: "$unit",
      min: "$min",
      max: "$max",
      node: "$node",
      year: { "$year": "$time" },
      dayOfYear: { "$dayOfYear": "$time" },
      hour: { "$hour": "$time" },
      minute: { "$minute": "$time" }
    },
    _id_not_delete: { $last: "$_id" }
}}
],
{ 
    "allowDiskUse" : true
}
)

var ids_not_delete = cursor.map(function (doc) { return doc._id_not_delete; });

db.getCollection("light").remove({"_id": { "$nin": ids_not_delete }});

Но в моей базе данных более 20 миллионов записей, поэтому я получаю эту ошибку

E QUERY    [js] Error: Converting from JavaScript to BSON failed: Object size 23146644 exceeds limit of 16793600 bytes. :
Bulk/addToOperationsList@src/mongo/shell/bulk_api.js:611:28
Bulk/findOperations.remove@src/mongo/shell/bulk_api.js:743:24
DBCollection.prototype.remove@src/mongo/shell/collection.js:404:13
@(shell):1:1

Я знаю, что коренная причина

The maximum BSON document size is 16 megabytes

Я думаю, что я должен изменить приведенный ниже код, но у меня нет хорошего решения.

var ids_not_delete = cursor.map(function (doc) { return doc._id_not_delete; });

У вас есть идеи по оптимизации моего кода?

Пример документов в коллекции:

{ 
    "_id" : ObjectId("5be22d5808c08300545effee"), 
    "index" : "LIGHT", 
    "unit" : "LUX", 
    "min" : NumberInt(5), 
    "max" : NumberInt(6), 
    "avg" : 5.5, 
    "node" : "TH", 
    "time" : ISODate("2018-11-07T00:10:00.091+0000")
},
{ 
    "_id" : ObjectId("5be22b0052122e0047c3467c"), 
    "index" : "LIGHT", 
    "unit" : "LUX", 
    "min" : NumberInt(3), 
    "max" : NumberInt(5), 
    "avg" : NumberInt(4), 
    "node" : "TH", 
    "time" : ISODate("2018-11-07T00:00:00.204+0000")
},
{ 
    "_id" : ObjectId("5be22b0008c08300545eff79"), 
    "index" : "LIGHT", 
    "unit" : "LUX", 
    "min" : NumberInt(3), 
    "max" : NumberInt(5), 
    "avg" : NumberInt(4), 
    "node" : "TH", 
    "time" : ISODate("2018-11-07T00:00:00.081+0000")
}

Версия оболочки MongoDB v4.0.2

MongoDB 4.0.0

1 Ответ

0 голосов
/ 14 ноября 2018

Вы можете инвертировать агрегацию, чтобы выбрать идентификаторы, которые хотите удалить, а не те, которые хотите сохранить:

const toDelete = db.getCollection("light").aggregate([
  { $group : {
    "_id": {
      index: "$index",
      unit: "$unit",
      min: "$min",
      max: "$max",
      node: "$node",
      year: { "$year": "$time" },
      dayOfYear: { "$dayOfYear": "$time" },
      hour: { "$hour": "$time" },
      minute: { "$minute": "$time" }
    },
    ids: {$push: "$_id"}
  } },
  {$project: {_id: {$slice: ["$ids", 1, 10000]}}},
  {$unwind: "$_id"},
  {$project: {_id: 0, deleteOne: { "filter" : { "_id" : "$_id"} } } }
]).toArray()

10000 - это достаточно большое число, значительно превышающее ожидаемое количество дубликатов в группе..

Затем вы можете использовать bulkWrite :

db.getCollection("light").bulkWrite(toDelete);

Драйвер разделит массив на 100 000 пакетов для каждого.

...