Умножить поле по значению в Mongodb

Я искал способ создания оператора обновления, который займет существующее числовое поле и изменит его с помощью выражения. Например, если у меня есть поле под названием Price, возможно ли сделать обновление, которое устанавливает цену до 50% от существующего значения?

Итак, учитывая { Price : 19.99 }

Я хотел бы сделать db.collection.update({tag : "refurb"}, {$set {Price : Price * 0.50 }}, false, true);

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

Ответ 1

Вы можете запустить серверный код с помощью db.eval().

db.eval(function() { 
    db.collection.find({tag : "refurb"}).forEach(function(e) {
        e.Price = e.Price * 0.5;
        db.collection.save(e);
    });
});

Обратите внимание, что это заблокирует БД, поэтому лучше выполнить пару операций поиска-обновления.

См. Https://docs.mongodb.com/manual/core/server-side-javascript/

Ответ 2

В новом Mongo 2.6.x есть $mul operator. Это умножило бы значение поля на число со следующим синтаксисом.

{
  $mul: { field: <number> }
}

Итак, в вашем случае вам нужно будет сделать следующее:

db.collection.update(
  { tag : "refurb"},
  { $mul: { Price : 0.5 } }
);

Ответ 3

Начиная с Mongo 4.2, db.collection.update() может принять конвейер агрегации, что в конечном итоге позволяет обновить поле на основе другого поля:

// { price: 19.99 }
// { price: 2.04  }
db.collection.update(
  {},
  [{ $set: { price: { $multiply: [ 0.5, "$price" ] } } }],
  { multi: true }
)
// { price: 9.995 }
// { price: 1.02  }
  • Первая часть {} - это запрос на совпадение, который фильтрует, какие документы обновлять (в данном случае все документы).

  • Вторая часть [{ $set: { price:... } }] - это конвейер агрегации обновлений (обратите внимание на квадратные скобки, обозначающие использование конвейера агрегации). $set - новый оператор агрегирования и псевдоним $addFields. Обратите внимание, как price изменяется напрямую на основе ее собственной стоимости ($price).

  • Не забудьте { multi: true }, иначе будет обновлен только первый соответствующий документ.

Ответ 4

Ну, это возможно при атомной операции как $set.

У вас есть несколько вариантов:

  • используйте решение eval(), предложенное pingw33n
  • получить документ, который вы хотите изменить, чтобы получить текущее значение и изменить его с помощью набора
  • Если у вас высокая скорость работы, вы можете быть уверены, что документ не изменился во время получения его значения (с использованием предыдущего решения), поэтому вы можете использовать findAndModify (см. эта страница, чтобы получить представление о том, как это сделать).

Это действительно зависит от вашего контекста: при очень низком давлении на db я бы пошел на решение pingw33n. С очень высокой скоростью работы я бы использовал третье решение.