2014-01-21 2 views
1

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

{ 
    "_id" : { 
     "clienta" : NumberLong(460011766), 
     "clientb" : NumberLong(2886729962) 
    }, 
    "states" : [ 
     [ 
      "fixed", "fixed.rotated","fixed.rotated.off" 

     ] 
    ], 
    "VBPP" : [ 
     244, 
     182, 
     184, 
     11, 
     299, 

    ], 
    "PPF" : 72.4, 
} 

Интуитивного, хотя и медленно, способ обновления этих полей чтобы расчеты самих себя (длина и дисперсии массива) с PyMongo перед преобразованием в массивах выглядит следующим образом:

records_list = [] 

cursor = db.clientAgg.find({}, {'_id' : 0, 
             'states' : 1, 
             'VBPP' : 1, 
             'PPF': 1}) 
for record in cursor: 
    records_list.append(record) 

for dicts in records_list: 
     dicts['states'] = len(dicts['states']) 
     dicts['VBPP'] = np.var(dicts['VBPP']) 

Я написал различные формы этого основного потока для оптимизации скорости, но чего в 500k словари в память y, чтобы изменить их, прежде чем преобразовывать их в массивы, чтобы пройти оценку машинного обучения, является дорогостоящим. Я пробовал различные способы для обновления записей непосредственно через курсор с вариантами следующего без успеха:

cursor = db.clientAgg.find().skip(0).limit(50000) 

def iter(): 
    for item in cursor: 
     yield item 

l = [] 
for x in iter(): 
    x['VBPP'] = np.var(x['VBPP']) 
    # Or  
    # db.clientAgg.update({'_id':x['_id']},{'$set':{'x.VBPS': somefunction as above }},upsert=False, multi=True) 

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

Если бы я мог успешно изменить коллекцию напрямую, я мог бы использовать что-то очень быстрое, как Monary или IOPro, для загрузки данных непосредственно из Mongo и в массив numpy без дополнительных накладных расходов.

Спасибо за ваше время

ответ

3

MongoDB не имеет возможности обновлять документ с расчетными значениями из полей документа; в настоящее время вы можете использовать обновление только для установки значений в константы, которые вы передаете из своего приложения. Таким образом, вы можете установить document.x на 2, но вы не можете установить document.x в document.y + document.z или любое другое вычисленное значение.

См. https://jira.mongodb.org/browse/SERVER-11345 и https://jira.mongodb.org/browse/SERVER-458 для возможных будущих функций.

В ближайшем будущем PyMongo выпустит bulk API, что позволит вам отправлять партии отдельных операций обновления в одном сетевом раунде, что улучшит вашу производительность.

Добавление:

У меня есть две другие идеи. Во-первых, запустите несколько серверов Javascript. Например, чтобы установить все документы b поля в 2 * a:

db.eval(function() { 
    var collection = db.test_collection; 
    collection.find().forEach(function(doc) { 
     var b = 2 * doc.a; 
     collection.update({_id: doc._id}, {$set: {b: b}}); 
    }); 
}); 

Вторая идея заключается в использовании the aggregation framework's $out operator, new in MongoDB 2.5.2, чтобы превратить коллекцию во вторую коллекцию, которая включает в себя вычисляемое поле:

db.test_collection.aggregate({ 
    $project: { 
     a: '$a', 
     b: {$multiply: [2, '$a']} 
    } 
}, { 
    $out: 'test_collection2' 
}); 

Обратите внимание, что $project должен явно включать все нужные поля; только _id включен по умолчанию.

Для миллиона документов на моей машине прежний подход занял 2,5 минуты, а последние 9 секунд. Таким образом, вы можете использовать структуру агрегирования для копирования своих данных из своего источника в пункт назначения, с включенными вычисленными полями. Затем, при желании, отбросьте исходную коллекцию и rename the target collection на имя источника.

Моя последняя мысль об этом заключается в том, что MongoDB 2.5.3 и более поздние версии могут передавать большие результирующие наборы из конвейера агрегации с помощью курсора. Нет причин, по которым Monary не может использовать эту возможность, поэтому вы можете подать туда запрос функции. Это позволит вам получать документы из коллекции в форме, которую вы хотите, через Monary, без фактического магазина расчетных полей в MongoDB.

+0

Интересно, спасибо за информацию. Я обыскиваю JIRA, но пропустил эти прекрасные находки. Таким образом, похоже, что мне придется принести эти объекты в память, чтобы сделать calc и написать константу в конце концов. – KLDavenport

+0

Есть ли у вас рекомендации по масштабируемым решениям с Python. Я не могу связать некоторые операторы разматывания. – KLDavenport

+0

К сожалению, автор Монари уже давно был МВД. Кажется, что нет способа перемотки курсора из метода aggregate() find() - это просто cursor.rewind(). Попытка сделать это с помощью агрегата «InvalidOperation: не может вызвать перемотку по командному курсору» – KLDavenport

Смежные вопросы