diff --git a/README.md b/README.md
index c041eaf3c..f3a34a98f 100644
--- a/README.md
+++ b/README.md
@@ -98,7 +98,7 @@ If you run a MongoDB with authentification ([Docker's example here](https://gith
allowExtendedOperators |
Boolean |
false |
- Set to true to enable using MongoDB operators such as $currentDate, $inc, $max, $min, $mul, $rename, $setOnInsert, $set, $unset, $addToSet, $pop, $pullAll, $pull, $pushAll, $push , and $bit . |
+ Set to true to enable using MongoDB operators such as $currentDate, $inc, $max, $min, $mul, $rename, $setOnInsert, $set, $unset, $addToSet, $pop, $pullAll, $pull, $push , and $bit . See Update Operators section below |
enableGeoIndexing |
@@ -249,6 +249,32 @@ The .loopbackrc file is in JSON format, for example:
+## Update Operators
+
+Except the comparison and logical operators LoopBack supports in the [operator list](https://loopback.io/doc/en/lb4/Where-filter.html#operators) of `Where` filter, you can also enable [MongoDB update operators](https://docs.mongodb.com/manual/reference/operator/update/) for `update*` methods by setting the flag `allowExtendedOperators` to `true` in the datasource configuration.
+
+Here is an example of updating the price for all the products under category `furniture` if their current price is lower than 100:
+
+```
+await productRepo.updateAll({ $max: { price: 100 }}, { category: {eq: 'furniture'} // where clause goes in here });
+```
+
+For LoopBack 3 users
+
+```
+Product.updateAll(
+ { category: {eq: 'furniture'} // where clause goes in here },
+ {$max: {price: 100}},
+ options,
+ function(err, updateproducts) {
+ ...
+```
+
+
+
+{% include tip.html content="you **will not** need the dollar sign `'$'` for operators in the Where
+clause." %}
+
## Handling ObjectId
MongoDB uses `ObjectId` for its primary key, which is an object instead of a
diff --git a/lib/mongodb.js b/lib/mongodb.js
index c1f9285bc..ab46ec062 100644
--- a/lib/mongodb.js
+++ b/lib/mongodb.js
@@ -765,7 +765,6 @@ MongoDB.prototype.parseUpdateData = function(modelName, data, options) {
'$pop',
'$pullAll',
'$pull',
- '$pushAll',
'$push',
// Bitwise operator
'$bit',
@@ -993,6 +992,7 @@ MongoDB.prototype.buildWhere = function(modelName, where, options) {
const modelCtor = self._models[modelName];
if (spec) {
+ spec = trimLeadingDollarSigns(spec);
if (spec === 'between') {
query[k] = {$gte: cond[0], $lte: cond[1]};
} else if (spec === 'inq') {
@@ -1994,6 +1994,17 @@ function isObjectIDProperty(modelCtor, propDef, value, options) {
}
}
+/**
+ * Removes extra dollar sign '$' for operators
+ *
+ * @param {*} spec the operator for Where filter
+ */
+function trimLeadingDollarSigns(spec) {
+ return spec.replace(/^(\$)+/, '');
+}
+
+exports.trimLeadingDollarSigns = trimLeadingDollarSigns;
+
function sanitizeFilter(filter, options) {
options = Object.assign({}, options);
if (options && options.disableSanitization) return filter;
diff --git a/test/mongodb.test.js b/test/mongodb.test.js
index 9626279a3..2ffb09f73 100644
--- a/test/mongodb.test.js
+++ b/test/mongodb.test.js
@@ -12,6 +12,7 @@ const testUtils = require('../lib/test-utils');
const async = require('async');
const sinon = require('sinon');
const sanitizeFilter = require('../lib/mongodb').sanitizeFilter;
+const trimLeadingDollarSigns = require('../lib/mongodb').trimLeadingDollarSigns;
const GeoPoint = require('loopback-datasource-juggler').GeoPoint;
@@ -1485,7 +1486,7 @@ describe('mongodb connector', function() {
should.not.exist(err);
updatedusers.should.have.property('count', 1);
- User.find({where: {name: 'Simon'}}, function(
+ User.find({where: {name: {$eq: 'Simon'}}}, function(
err,
foundusers,
) {
@@ -3726,6 +3727,29 @@ describe('mongodb connector', function() {
});
});
+ context('trimLeadingDollarSigns', () =>{
+ it('removes an extra leading dollar sign in ths operators', () => {
+ const spec = '$eq';
+ const updatedSpec = trimLeadingDollarSigns(spec);
+ updatedSpec.should.equal('eq');
+ });
+ it('removes extra leading dollar signs in ths operators', () => {
+ const spec = '$$eq';
+ const updatedSpec = trimLeadingDollarSigns(spec);
+ updatedSpec.should.equal('eq');
+ });
+
+ it('remains the same if the input does not contain any dollar signs', () => {
+ const spec = 'eq';
+ const updatedSpec = trimLeadingDollarSigns(spec);
+ updatedSpec.should.equal(spec);
+ });
+ it('remains the same if the input does not start with dollar signs', () => {
+ const spec = 'eq$';
+ const updatedSpec = trimLeadingDollarSigns(spec);
+ updatedSpec.should.equal(spec);
+ });
+ });
context('sanitizeFilter()', () => {
it('returns filter if not an object', () => {
const input = false;