2015-03-04 3 views
6

У меня есть следующий запрос: найти   теги в поле имени и заменить их пустым пространством - чтобы избавиться от них.
Имена строк могут иметь от 1 до многих   теги, например.Найти и заменить строки в документах Эффективно

AA aa 
AA  aa 
AA   aa 
AA    aa 
AA AA aaaaaaaa 

... подобный.

db.tests.find({'name':/.* .*/}).forEach(function(test){ 
     test.name = test.name.replace(" ",""); 
     db.tests.save(test); 
    }); 

    db.tests.find({'name':/.*  .*/}).forEach(function(test){ 
     test.name = test.name.replace("  ",""); 
     db.tests.save(test); 
    }); 

    db.tests.find({'name':/.*   .*/}).forEach(function(test){ 
     test.name = test.name.replace("   ",""); 
     db.tests.save(test); 
    }); 

Другие, чем повторять тот же шаблон запроса, есть лучшее решение для обработки этой ситуации, с точки зрения меньшего дублирования и повышения производительности?

ответ

12

Конечно, если все, что вы хотите сделать, это лишить   объектов из текста, то вы просто сделать глобальный матч и заменить:

db.tests.find({ "name": /\ /g }).forEach(function(doc) { 
    doc.name = doc.name.replace(/ /g,""); 
    db.tests.update({ "_id": doc._id },{ "$set": { "name": doc.name } }); 
}); 

Так что не должно быть никакой необходимости выписать все возможные комбинации, регулярное выражение заменит совпадение с опцией /g. Возможно также использовать /m для многострочной строки. Строка «name» содержит символы новой строки. См. Базовый regexer example.

Также рекомендуется использовать $set, чтобы изменить только поле (и), которое вы действительно хотите, а не .save() весь документ назад. Существует меньше трафика и меньше шансов переписать изменения, которые могли быть сделаны другим процессом с момента чтения документа.

В идеале вы использовали бы API с объемными операциями с версиями MongoDB версии 2.6 и выше. Это позволяет обновления до «партии», так что снова меньше трафика между клиентом и сервером:

var bulk = db.tests.initializeOrderedBulkOp(); 
var count = 0; 

db.tests.find({ "name": /\ /g }).forEach(function(doc) { 
    doc.name = doc.name.replace(/ /g,""); 
    bulk.find({ "_id": doc._id }) 
     .updateOne({ "$set": { "name": doc.name } }); 
    count++; 

    if (count % 1000 == 0) { 
     bulk.execute(); 
     bulk = db.tests.initializeOrderedBulkOp(); 
    } 
}); 

if (count % 1000 != 0) 
    bulk.execute(); 

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

+1

Нет необходимости запускать запрос обновления $ set: вы можете просто выполнить db.tests.save (doc); – alexcasalboni

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