mongodb: сопоставление элемента в массиве и обновление

#javascript #mongodb #aggregation-framework #updates

#javascript #mongodb #агрегация-фреймворк #Обновления

Вопрос:

По сути, я выполнил агрегацию, чтобы получить общее количество и список объектов, которые сформировали общее количество.

Теперь мне нужно обновить исходную таблицу с помощью агрегированного идентификатора объекта для элементов, которые вносят вклад в агрегацию. В основном формирование отношения в обоих направлениях.

 coll.aggregate([
    { "$match": {"elig": 1, "nid" : null, "cncl" : null  } },
    { "$group": {
        "_id": "$nkey",
        "cumqty": {"$sum": "$pr_qty.qty" },
        "netted" : { "$push" : "$_id" } 
    }},
    { "$project": {
        "nkey":"$nkey" ,
        "cumqty": "$cumqty",
        "netted" : "$netted" ,
        "_id" : 0 
    }},
    { "$out": aggcollnm }
])
  

Теперь в объединенной таблице есть список идентификаторов объектов, сформированный с помощью $push .

Скажем, doc1, doc2 и doc3 сформировали agg1, а в списке agg1 есть doc1, doc2 и doc3. Я хочу, чтобы doc1, doc2 и doc3 имели идентификатор agg1 в качестве идентификатора сети.

Итак, я сделал следующее

 coll.find().forEach( function(elem) {
    coll.update (
        { "_id" : elem._id },
        { "$set" : { nid : aggcoll.aggregate ( [
            { "$unwind" : "$netted" } ,
            { "$match" : { "netted" : elem._id } },
            { "$project" : { "_id" :1 } }
            ] )._firstBatch[0]
        }}
    )
})
  

Он отлично работал в меньшем наборе. Но для документов 1M сбой с приведенной ниже ошибкой.

2014-06-30T09:48:40.577 0100 Ошибка: getMore: курсор не существует на сервере, возможен перезапуск или тайм-аут? в src/mongo/shell/query.js:116 не удалось загрузить: ./netting.js

Есть ли лучший способ сделать это.

Ответ №1:

Вы используете MongoDB 2.6, поэтому есть способы более эффективного обновления, так как ваша общая обработка, похоже, обратная. Вы должны зацикливать свой «aggcoll», а затем обновлять свою цель изнутри этого:

 var batch = coll.initializeOrderedBulkOp();
counter = 0;

aggcoll.find().forEach(function(agg) {
    batch.find({ "_id": { "$in": agg.netted }}).update({ "$set: { "nid": agg._id } });
    counter  ;

    if ( counter % 1000 == 0 ) {
        batch.execute();
        counter = 0;
        batch = coll.initializeOrderedBulkOp();
    }
});

if ( counter > 0 )
    batch.execute();
  

Ваш «встроенный» агрегатный оператор был не очень эффективным способом сделать то, что вы пытались, и сильно замедлил бы работу. Здесь не только выполняется выдача обновлений для всех сопоставленных _id значений с помощью $in as, поскольку это операция «множественного» обновления, но и общее использование API массовых операций сокращает трафик на сервер и затрачиваемое время.

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

 { "$group": {
    "_id": "$nkey",
  

По какой-то причине вы изменили это так, чтобы оно больше не _id было ключом этой новой коллекции, когда вы ее записывали. Очевидно, что это поле присутствует во всех документах, из которых вы были получены, и его просто следовало оставить в качестве нового первичного ключа.