mongo-dot-notation
Advanced tools
Comparing version 1.1.0 to 1.2.0
17
index.js
'use strict'; | ||
var fieldOperators = require('./lib/fields'); | ||
module.exports.isOperator = require('./lib/operator').isOperator; | ||
module.exports.flatten = require('./lib/flatten'); | ||
// copy all field operators | ||
Object.keys(fieldOperators).forEach(function(operator){ | ||
module.exports[operator] = fieldOperators[operator]; | ||
}); | ||
extend(require('./lib/field')); | ||
extend(require('./lib/array')); | ||
extend(require('./lib/bitwise')); | ||
@@ -16,2 +13,8 @@ /* | ||
*/ | ||
module.exports.Operators = fieldOperators; | ||
module.exports.Operators = require('./lib/field'); | ||
function extend(operators){ | ||
Object.keys(operators).forEach(function(operatorName){ | ||
module.exports[operatorName] = operators[operatorName]; | ||
}); | ||
} |
'use strict'; | ||
var util = require('util'); | ||
var isOperator = require('./operator').isOperator; | ||
var Operator = require('./operator'); | ||
@@ -39,3 +39,3 @@ var PrimitiveTypes = [ | ||
if (isOperator(propertyValue)) { | ||
if (Operator.isOperator(propertyValue)) { | ||
return build(updateData, propertyValue.name, | ||
@@ -61,5 +61,8 @@ propertyName, propertyValue.value()); | ||
function build(updateData, operator, propertyName, value) { | ||
if (Operator.isOperator(value)) | ||
return build(updateData, value.name, propertyName + '.' + operator, value.value()); | ||
updateData[operator] = updateData[operator] || {}; | ||
updateData[operator][propertyName] = value; | ||
return updateData; | ||
@@ -66,0 +69,0 @@ } |
'use strict'; | ||
module.exports = Operator; | ||
function Operator(name){ | ||
if (!name) | ||
throw new Error('Null argument error: name'); | ||
this.name = name; | ||
@@ -15,3 +19,7 @@ } | ||
module.exports.create = function(name){ return new Operator(name); }; | ||
module.exports.isOperator = function(obj){ return obj && obj instanceof Operator; }; | ||
Operator.isOperator = function(obj){ return obj && obj instanceof Operator; }; | ||
Operator.create = function(name, value, defaultValue){ | ||
var operator = new Operator(name); | ||
operator.value(typeof(value) === 'undefined' ? defaultValue : value); | ||
return operator; | ||
}; |
{ | ||
"name": "mongo-dot-notation", | ||
"version": "1.1.0", | ||
"description": "Convert simple objects to mongo update operators", | ||
"version": "1.2.0", | ||
"description": "Transform objects to mongo update instructions", | ||
"author": { | ||
@@ -17,5 +17,8 @@ "name": "Dumitru Deveatii", | ||
"mongo", | ||
"dot", | ||
"dot-notation", | ||
"conversion", | ||
"transform", | ||
"convert", | ||
"operators", | ||
"instructions", | ||
"json", | ||
@@ -25,3 +28,4 @@ "objects", | ||
"data", | ||
"flatten" | ||
"flatten", | ||
"update" | ||
], | ||
@@ -33,3 +37,5 @@ "license": "MIT", | ||
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly", | ||
"lint": "eslint lib" | ||
"coverage": "istanbul cover node_modules/mocha/bin/_mocha --report html", | ||
"lint": "eslint lib", | ||
"perf": "node ./test/performance.js" | ||
}, | ||
@@ -42,4 +48,4 @@ "devDependencies": { | ||
"mocha-lcov-reporter": "^1.3.0", | ||
"mongodb": "^2.2.27" | ||
"mongodb": "^2.2.28" | ||
} | ||
} |
768
README.md
mongo-dot-notation | ||
======== | ||
Convert simple objects to mongo update operators. <br/> | ||
Fast, lightweight library to transform objects to mongo update instructions using operators. | ||
@@ -9,223 +9,629 @@ [](https://npmjs.org/package/mongo-dot-notation) | ||
This lightweight library can be used to create a more readable code when working with mongo updates. | ||
You focus on updated properties of the document, rather than on mongo update instructions. | ||
##### Example: | ||
```javascript | ||
var $ = require('mongo-dot-notation'); | ||
var $ = require('mongo-dot-notation') | ||
var MongoClient = require('mongodb').MongoClient; | ||
var url = 'mongodb://localhost:27017/mydatabase'; | ||
MongoClient.connect(url, function(err, db) { | ||
if (err) throw err; | ||
// standard way | ||
var update = { | ||
$set: { | ||
'comment': 'Logged in.', | ||
'system': 'demo', | ||
'account.attempts': 0 | ||
}, | ||
$currentDate: { | ||
'login.date': true | ||
}, | ||
$inc: { | ||
'analytics.visits': 1, | ||
'account.logins': 1, | ||
}, | ||
$unset: { | ||
'account.blocked' | ||
} | ||
} | ||
updateUser(db, update, function(err, res){ | ||
db.close(); | ||
}) | ||
// using mongo-dot-notation | ||
var update = { | ||
comment: 'Logged in.', | ||
system: 'demo', | ||
login: { | ||
date: $.$currentDate() | ||
}, | ||
analytics: { | ||
visits: $.$inc() | ||
}, | ||
account: { | ||
blocked: $.$unset(), | ||
attempts: 0, | ||
logins: $.$inc() | ||
} | ||
var instructions = $.flatten({ | ||
firstName: 'John', | ||
contact: { phone: '874-478-1254' }, | ||
lastUpdate: $.$currentDate() | ||
}) | ||
/* | ||
var instructions = { | ||
$currentDate: { | ||
lastUpdate: { $type: 'date' } | ||
}, | ||
$set: { | ||
'firstName': 'John', | ||
'contact.phone': '874-478-1254' | ||
} | ||
updateUser(db, $.flatten(update), function(err, res){ | ||
db.close(); | ||
} | ||
*/ | ||
``` | ||
## Installation | ||
```bash | ||
$ npm install mongo-dot-notation --save | ||
``` | ||
## Features | ||
* Transform objects to mongo update instructions | ||
* supports embedded documents | ||
* understands mongo types (ObjectID, Int32 etc.) | ||
* Full support of mongo update operators | ||
* Field update operators | ||
* Array update operators | ||
* Bitwise update operators | ||
* Compatible with node >= 0.12 | ||
* No *npm* dependencies on mongo | ||
## Usage | ||
Using `$.flatten` and operators to transform to mongo update instructions. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
var MongoClient = require('mongodb').MongoClient | ||
var url = 'mongodb://localhost:27017/mydatabase' | ||
MongoClient.connect(url).then(function(db) { | ||
return db.collection('users').update( | ||
{ _id: 1 }, | ||
$.flatten({ | ||
comments: $.$push('Logged in.').$each().$slice(-100), | ||
env: 'demo', | ||
login: { | ||
date: $.$currentDate() | ||
}, | ||
analytics: { | ||
visits: $.$inc() | ||
}, | ||
account: { | ||
createdOn: $.$setOnInsert(new Date()), | ||
blocked: $.$unset(), | ||
attempts: 0, | ||
logins: $.$inc() | ||
} | ||
}) | ||
}) | ||
}); | ||
``` | ||
function updateUser(db, user, handler){ | ||
var collection = db.collection('users'); | ||
collection.update({username: 'johndoe@test.com'}, user, handler); | ||
} | ||
Without `mongo-dot-notation` update instructions should look like: | ||
``` javascript | ||
... | ||
return collection.update( | ||
{ _id: 1 }, | ||
{ | ||
$set: { | ||
'env': 'demo', | ||
'account.attempts': 0 | ||
}, | ||
$push: { | ||
'comments': { | ||
'$each': ['Logged in.'], | ||
'$slice': -100 | ||
} | ||
} | ||
$currentDate: { | ||
'login.date': 'date' | ||
}, | ||
$inc: { | ||
'analytics.visits': 1, | ||
'account.logins': 1, | ||
}, | ||
$unset: { | ||
'account.blocked': '' | ||
}, | ||
$setOnInsert: { | ||
'account.createdOn': new Date() | ||
} | ||
}) | ||
}) | ||
``` | ||
The current implementation supports all mongo update operators: | ||
* $inc | ||
* $mul | ||
* $rename | ||
* $setOnInsert | ||
* $set | ||
* $unset | ||
* $min | ||
* $max | ||
* $currentDate | ||
* use $currentDate for conversion to $type = 'date' | ||
* use $timestamp for conversion to $type = 'timestamp' | ||
## Tests | ||
### Installation | ||
To run the test suite make sure you have mongo 2.6+ installed locally on the default port (*27017*). | ||
Mongo is used to run integration tests. | ||
Once mongo is available, install the dependencies, then run `npm test`: | ||
Install from npm: | ||
```bash | ||
$ npm install | ||
$ npm test | ||
``` | ||
npm install mongo-dot-notation --save | ||
To calculate code coverage run `npm run-script test-ci`. | ||
## API | ||
## `.flatten()` | ||
Use `.flatten()` to transform objects: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
var instructions = $.flatten({ | ||
account: { | ||
name: 'hero' | ||
} | ||
}) | ||
// { '$set': { 'account.name': 'hero' } } | ||
``` | ||
## Usage | ||
### Convert a simple object | ||
## `.isOperator()` | ||
Checks if a given value is a `mongo-dot-notation` operator: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.isOperator(1) // false | ||
$.isOperator({}) // false | ||
$.isOperator($.$set(10)) // true | ||
``` | ||
See below the list of all supported mongo update opertors. | ||
## Mongo update operators | ||
### Field update operators | ||
#### `.$inc` | ||
See mongo [**$inc**](https://docs.mongodb.com/manual/reference/operator/update/inc/). | ||
The `$inc` operator increments a field by a specified value. If no value is provided, defaults to 1. | ||
```javascript | ||
var $ = require('mongo-dot-notation'); | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
visits: $.$inc(5) // increment current visits value by 5 | ||
}) | ||
// { '$inc': { 'visits': 5 } } | ||
``` | ||
var person = { | ||
firstName: 'John', | ||
lastName: 'Doe' | ||
}; | ||
#### `.$mul` | ||
See mongo [**$mul**](https://docs.mongodb.com/manual/reference/operator/update/mul/). | ||
var instructions = $.flatten(person) | ||
console.log(instructions); | ||
/* | ||
{ | ||
$set: { | ||
'firstName': 'John', | ||
'lastName': 'Doe' | ||
} | ||
} | ||
*/ | ||
Multiplies the value of a field by a number. (*Supported in mongo >= 2.6*) | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
price: $.$mul(0.75) // multiply current price value by 0.75 | ||
}) | ||
// { '$mul': { 'price': 0.75 } } | ||
``` | ||
### Convert an object with deep properties | ||
#### `.$rename` | ||
See mongo [**$rename**](https://docs.mongodb.com/manual/reference/operator/update/rename/). | ||
The `$rename` operator updates the name of a field. | ||
```javascript | ||
var $ = require('mongo-dot-notation'); | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
nmae: $.$rename('name') // rename nmae field to name | ||
}) | ||
// { '$rename': { 'nmae': 'name' } } | ||
``` | ||
var person = { | ||
firstName: 'John', | ||
lastName: 'Doe', | ||
address: { | ||
city: 'NY', | ||
street: 'Eighth Avenu', | ||
number: 123 | ||
#### `.$setOnInsert` | ||
See mongo [**$setOnInsert**](https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/). | ||
Assigns value to field only when the document is inserted (when an update operation is with `upsert:true`). (*Supported in mongo >= 2.4*) | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
db.collection('users').update( | ||
{ _id: 1 }, | ||
$.flatten({ | ||
createdOn: $.$setOnInsert(new Date()) // sets createdOn field only when document is inserted | ||
}), | ||
{ upsert: true }) | ||
// { '$setOnInsert': { 'createdOn': new Date() } } | ||
``` | ||
#### `.$set` | ||
See mongo [**$set**](https://docs.mongodb.com/manual/reference/operator/update/set/). | ||
The `$set` operator replaces the value of a field with the specified value. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
name: $.$set('Mike') | ||
}) | ||
// { '$set': { 'name': 'Mike' } } | ||
``` | ||
The `$set` is an implicit operator, meaning if an object is passed to `$.flatten`, it will navigate through own and embedded document fields and apply $set. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
name: 'Mike', | ||
contactDetails: { | ||
email: 'mike@test.com' | ||
} | ||
}; | ||
}) | ||
var instructions = $.flatten(person) | ||
console.log(instructions); | ||
/* | ||
{ | ||
$set: { | ||
'firstName': 'John', | ||
'lastName': 'Doe', | ||
'address.city': 'NY', | ||
'address.street': 'Eighth Avenu', | ||
'address.number': 123 | ||
} | ||
} | ||
*/ | ||
// { | ||
// '$set': { | ||
// 'name': 'Mike', | ||
// 'contactDetails.email': 'mike@test.com' | ||
// } | ||
// } | ||
``` | ||
### Using operators | ||
The `$set` operator could also be used to update an embedded document entirely: | ||
```javascript | ||
var $ = require('mongo-dot-notation'); | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
name: 'Mike', | ||
// contactDetails is updated to a new document | ||
contactDetails: $.$set({ | ||
email: 'mike@test.com' | ||
}) | ||
}) | ||
var person = { | ||
password: '1234', | ||
updated: $.$currentDate(), | ||
resetCounter: $.$inc() | ||
}; | ||
// { | ||
// '$set': { | ||
// 'name': 'Mike', | ||
// 'contactDetails': { email: 'mike@test.com' } | ||
// } | ||
// } | ||
``` | ||
var instructions = $.flatten(person) | ||
console.log(instructions); | ||
/* | ||
{ | ||
$set: { | ||
'password': '1234' | ||
}, | ||
$inc: { | ||
'resetCounter': 1 | ||
}, | ||
$currentDate: { | ||
'updated': { $type: 'date'}, | ||
} | ||
} | ||
*/ | ||
#### `.$unset` | ||
See mongo [**$unset**](https://docs.mongodb.com/manual/reference/operator/update/unset/). | ||
The `$unset` operator deletes a particular field. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
comments: $.$unset(), // remove field from document | ||
history: $.$unset() | ||
}) | ||
// { '$unset': { 'comments': '', 'history': '' } } | ||
``` | ||
Operators can also be used in inner objects: | ||
#### `.$min` | ||
See mongo [**$min**](https://docs.mongodb.com/manual/reference/operator/update/min/). | ||
The `$min` updates the value of the field to a specified value if the specified value is less than the current value of the field. | ||
```javascript | ||
var $ = require('mongo-dot-notation'); | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
low: $.$min(200) // update low to 200 if current low value is greater than 200 | ||
}) | ||
// { '$min': { 'low': 200 } } | ||
``` | ||
var pos = { | ||
coords: { | ||
x: $.$mul(2), | ||
y: $.$inc(10), | ||
z: $.$unset(), | ||
mark: [1, 2, 3] | ||
}, | ||
label: $.$rename('title') | ||
}; | ||
#### `.$max` | ||
See mongo [$max](https://docs.mongodb.com/manual/reference/operator/update/max/). | ||
var instructions = $.flatten(pos) | ||
console.log(instructions); | ||
/* | ||
{ | ||
$set: { | ||
'coords.mark': [1, 2, 3] | ||
}, | ||
$mul: { | ||
'coords.x': 2 | ||
}, | ||
$inc: { | ||
'coords.y': 10 | ||
}, | ||
$unset: { | ||
'coords.z': '' | ||
}, | ||
$rename: { | ||
'label': 'title' | ||
} | ||
} | ||
*/ | ||
The `$max` operator updates the value of the field to a specified value if the specified value is greater than the current value of the field. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
high: $.$max(450) // update high to 450 if current high value is less than 450 | ||
}) | ||
// { '$max': { 'high': 450 } } | ||
``` | ||
Operators cannot be used inside arrays. | ||
#### `.$currentDate` | ||
See mongo [**$currentDate**](https://docs.mongodb.com/manual/reference/operator/update/currentDate/). | ||
## Update operators signatures | ||
The `$currentDate` operator sets the value of a field to the current date, either as a *Date* or a *Timestamp*. | ||
If type is not specified, uses *Date* by default. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
lastUpdate: $.$currentDate() | ||
}) | ||
// { '$currentDate': { 'lastUpdated': { '$type': 'date' } } } | ||
``` | ||
See [MongoDB - Fields update operators](https://docs.mongodb.com/manual/reference/operator/update/#fields). | ||
To set as a timestamp, use: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
lastUpdate: $.$currentDate('timestamp') | ||
}) | ||
// { '$currentDate': { 'lastUpdated': { '$type': 'timestamp' } } } | ||
``` | ||
Also, for timestamp an alias operator is defined in `mongo-dot-notation`: | ||
```javascript | ||
var $ = require('mongo-dot-notation'); | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
lastUpdate: $.$timestamp() | ||
}) | ||
// { '$currentDate': { 'lastUpdated': { '$type': 'timestamp' } } } | ||
``` | ||
// $.$inc(value) increment value, defaults to 1 | ||
// $.$mul(value) multiply factor, defaults to 1 | ||
// $.$rename(name) renames a field with a given name | ||
// $.$setOnInsert(value) sets the value only when inserted | ||
// $.$set(value) sets the value. This is an implicit operator, meaning: | ||
// {a: 1} and {a: $set(1)} are equivalent | ||
// $.$unset() removes the field | ||
// $.$min(value) only updates the field if the specified value is less than the existing field value | ||
// $.$max(value) only updates the field if the specified value is greater than the existing field value | ||
// $.$currentDate() sets the value of a field to current date as a Date | ||
// $.$currentDate(type) where type=<date|timestamp> sets the value of a field to current date or timestamp | ||
// $.$timestamp() sets the value of a field to current date as a Timestamp | ||
### Array update operators | ||
#### `.$ (update)` | ||
See mongo [**$ (update)**](https://docs.mongodb.com/manual/reference/operator/update/positional/). | ||
The positional `$` operator identifies an element in an array to update without explicitly specifying the position of the element in the array. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
db.students.update( | ||
{ _id: 1, grades: 80 }, // match all elements from grades array where value equals to 80 | ||
$.flatten({ | ||
grades: $.$().$set(82) // for matched elements, update value to 82 | ||
}) | ||
) | ||
// { $set: { "grades.$" : 82 } } | ||
``` | ||
> Copyright © 2015-2017 Dumitru Deveatii, released under the MIT license | ||
In order to update the matched document's field: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
db.students.update( | ||
{ _id: 1, 'grades.grade': 80 }, | ||
$.flatten({ | ||
grades: $.$('std').$set(1.5) | ||
}) | ||
) | ||
// { $set: { "grades.$.std" : 1.5 } } | ||
``` | ||
The positional `$` operator is chainable with all mongo supported update fields. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
db.students.update( | ||
{ _id: 1, grades: 80 }, | ||
$.flatten({ | ||
grades: $.$().$mul(0.1) //multiplies matched array element by 0.1 | ||
}) | ||
) | ||
// { $mul: { "grades.$" : 0.1 } } | ||
``` | ||
Can also be used to update an array element by a given index: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
grades: $.$(0).$set(100) | ||
}) | ||
// { $set: { "grades.0" : 0.1 } } | ||
``` | ||
Same, when updating the element's field: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
months: $.$('5.avgTemp').$set(25.7) | ||
}) | ||
// { $set: { "months.5.avgTemp" : 25.7 } } | ||
``` | ||
#### `.$addToSet` | ||
See mongo [**$addToSet**](https://docs.mongodb.com/manual/reference/operator/update/addToSet). | ||
The `$addToSet` operator adds a value to an array unless the value is already present, in which case $addToSet does nothing to that array. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$addToSet(5) | ||
}) | ||
// { '$addToSet': { 'values': 5 } } | ||
``` | ||
To add each value from a given array: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$addToSet([7, 1, 4]).$each() | ||
}) | ||
// { '$addToSet': { 'values': { '$each': [7, 1, 4] } } } | ||
``` | ||
#### `.$pop` | ||
See mongo [**$pop**](https://docs.mongodb.com/manual/reference/operator/update/pop). | ||
The `$pop` operator removes the first or last element of an array. Pass $pop a value of -1 to remove the first element of an array and 1 to remove the last element in an array. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$pop() // removes by default last element | ||
}) | ||
// { '$pop': { 'values': 1 } } | ||
``` | ||
To remove first element from the array: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$pop(-1) | ||
}) | ||
// { '$pop': { 'values': -1 } } | ||
``` | ||
There are chainable `.first()` and `.last()` methods defined: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
indexes: $.$pop().first(), | ||
scores: $.$pop().last(), | ||
}) | ||
// { '$pop': { 'indexes': -1, scores: 1 } } | ||
``` | ||
#### `.$pullAll` | ||
See mongo [**$pullAll**](https://docs.mongodb.com/manual/reference/operator/update/pullAll/). | ||
The `$pullAll` operator removes all instances of the specified values from an existing array. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$pullAll([0, 1]) | ||
}) | ||
// { '$pullAll': { 'values': [0, 1] } } | ||
``` | ||
#### `.$pull` | ||
See mongo [**$pull**](https://docs.mongodb.com/manual/reference/operator/update/pull/). | ||
The `$pull` operator removes from an existing array all instances of a value or values that match a specified condition. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$pull(7) | ||
}) | ||
// { '$pull': { 'values': 7 } } | ||
``` | ||
If an array is provided, implicitly applies `$in` operator: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$pull([0, 1]) | ||
}) | ||
// { '$pull': { 'values': { '$in': [0, 1] } } } | ||
``` | ||
See mongo documentation for [conditions](https://docs.mongodb.com/manual/reference/operator/update/pull/#remove-all-items-that-match-a-specified-pull-condition). | ||
#### `.$pushAll` | ||
See mongo [**$pushAll**](https://docs.mongodb.com/manual/reference/operator/update/pushAll/). | ||
The `$pushAll` operator appends the specified values to an array. (*Note that this operator is deprecated since mongo 2.4.*) | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
values: $.$pushAll([1, 2, 3]) | ||
}) | ||
// { '$pushAll': { 'values': [1, 2, 3] } } | ||
``` | ||
#### `.$push` | ||
See mongo [**$push**](https://docs.mongodb.com/manual/reference/operator/update/push/). | ||
The `$push` operator appends a specified value to an array. Can also be used to slice and sort the array. | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
grades: $.$push({ grade: 'A' }) | ||
}) | ||
// { '$push': { 'grades': { grade: 'A' } } } | ||
``` | ||
To push several values, chain with `.$each()` | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
grades: $.$push([{ grade: 'A' }, { grade: 'B' }]).$each() | ||
}) | ||
// { | ||
// '$push': { | ||
// 'grades': { | ||
// '$each': [{ grade: 'A' }, { grade: 'B' }] | ||
// } | ||
// } | ||
// } | ||
``` | ||
To push values at a specific position use `.$position()` (*requires the use of .$each(). Supported in mongo >= 2.6*) | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
// insert as a first element in the array | ||
$.flatten({ | ||
grades: $.$push({ grade: 'A' }).$each().$position(0) | ||
}) | ||
// { | ||
// '$push': { | ||
// 'grades': { | ||
// '$each': [{ grade: 'A' }], | ||
// '$position': 0 | ||
// } | ||
// } | ||
// } | ||
``` | ||
To slice the array, use `.slice()` (*requires the use of .$each(). Supported in mongo >= 2.6*) | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
// insert the element and limit to last 10 values | ||
$.flatten({ | ||
grades: $.$push({ grade: 'A' }).$each().$slice(-10) | ||
}) | ||
// { | ||
// '$push': { | ||
// 'grades': { | ||
// '$each': [{ grade: 'A' }], | ||
// '$slice': -10 | ||
// } | ||
// } | ||
// } | ||
``` | ||
To sort the array, use `.sort()` (*requires the use of .$each(). Supported in mongo >= 2.4*) | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
// insert the element and sorts descending by grade | ||
$.flatten({ | ||
grades: $.$push({ grade: 'A' }).$each().$sort({ grade: -1}) | ||
}) | ||
// { | ||
// '$push': { | ||
// 'grades': { | ||
// '$each': [{ grade: 'A' }], | ||
// '$sort': { grade: -1} | ||
// } | ||
// } | ||
// } | ||
``` | ||
Multiple instructions can be chained: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
// insert the element, sorts descending by grade | ||
// and slices only first 10 values | ||
$.flatten({ | ||
grades: $.$push({ grade: 'A' }).$each().$sort({ grade: -1}).$slice(10) | ||
}) | ||
// { | ||
// '$push': { | ||
// 'grades': { | ||
// '$each': [{ grade: 'A' }], | ||
// '$sort': { grade: -1}, | ||
// '$slice': 10 | ||
// } | ||
// } | ||
// } | ||
``` | ||
In case the array needs only to be sorted and/or sliced, `mongo-dot-notation` defines aliases: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
grades: $.$sort({ grade: 1 }), // sames as $.push([]).$each().$sort({ grade: 1 }) | ||
scores: $.$slice(10), // sames as $.push([]).$each().$slice(1) | ||
values: $.$sort().$slice(-5) // sames as $.push([]).$each().$sort().$slice(-5) | ||
}) | ||
``` | ||
### Bitwise update operators | ||
#### `.$bit` | ||
See mongo [**$bit**](https://docs.mongodb.com/manual/reference/operator/update/bit/). | ||
The `$bit` operator performs a bitwise update of a field. The operator supports bitwise AND, bitwise OR, and bitwise XOR (i.e. exclusive or) operations. | ||
*Note XOR is supported in mongo >= 2.6* | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
owner: $.$bit().$and(7) // performs a bitwise AND | ||
user: $.$bit().$or(1) // performans a bitwise OR | ||
group: $.$bit().$xor(5) // performs a bitwise XOR | ||
}) | ||
// { | ||
// '$bit': { | ||
// 'owner': { and: 7 }, | ||
// 'user': { or: 1 }, | ||
// 'group': { xor: 5 }, | ||
// } | ||
// } | ||
``` | ||
Following aliases are defined in `mongo-dot-notation`: | ||
```javascript | ||
var $ = require('mongo-dot-notation') | ||
$.flatten({ | ||
owner: $.$and(7), // same as $.$bit().$and(7) | ||
user: $.$or(1), // same as $.$bit().$or(1) | ||
group: $.$xor(5), // same as $.$bit().$xor(5) | ||
}) | ||
``` | ||
## License | ||
[MIT](LICENSE) | ||
Copyright © 2015-2017 Dumitru Deveatii |
@@ -5,3 +5,3 @@ 'use strict'; | ||
var $ = require('../index'); | ||
var ObjectID = require('mongodb').ObjectID; | ||
var mongo = require('mongodb'); | ||
@@ -35,7 +35,2 @@ describe('# Flatten tests', function(){ | ||
it('When is ObjectID returns the reference', function () { | ||
var id = new ObjectID(); | ||
$.flatten(id).should.equal(id); | ||
}); | ||
it('When is Array returns the array', function () { | ||
@@ -52,4 +47,37 @@ var arr = [1, 2, 3]; | ||
describe('# MongoDB types', function(){ | ||
var supportedTypes = [ | ||
'Binary', | ||
'Code', | ||
'DBRef', | ||
'Decimal128', | ||
'Double', | ||
'Int32', | ||
'Long', | ||
'MaxKey', | ||
'MinKey', | ||
'ObjectID', | ||
'BSONRegExp', | ||
'Symbol', | ||
'Timestamp' | ||
]; | ||
supportedTypes.forEach(function(mongoType){ | ||
describe(mongoType, function(){ | ||
it('Flatten directly', function(){ | ||
var id = new mongo[mongoType](); | ||
$.flatten(id).should.equal(id); | ||
}); | ||
it('Flatten as nested property', function(){ | ||
var data = {field: new mongo[mongoType]()}; | ||
$.flatten(data).should.have | ||
.property('$set').that.have | ||
.property('field').that.equals(data.field); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('# Simple objects', function () { | ||
it('When has `a` string property sets the property', function () { | ||
@@ -81,3 +109,30 @@ var obj = { a: 'test' }; | ||
describe('Empty values', function() { | ||
describe('# Nested operators ($ positional operator)', function () { | ||
it('When positional operator with $set', function () { | ||
var obj = { points: $.$().$set(10) }; | ||
$.flatten(obj).should.deep.equal({ $set: { 'points.$': 10 } }); | ||
}); | ||
it('When positional operator with value', function () { | ||
var obj = { points: $.$().value('test') }; | ||
$.flatten(obj).should.deep.equal({ $set: { 'points.$': 'test' } }); | ||
}); | ||
it('When positional operator with $inc', function () { | ||
var obj = { points: $.$().$inc(-1) }; | ||
$.flatten(obj).should.deep.equal({ $inc: { 'points.$': -1 } }); | ||
}); | ||
it('When positional operator with field specified', function () { | ||
var obj = { points: $.$('std').$set(0.5) }; | ||
$.flatten(obj).should.deep.equal({ $set: { 'points.$.std': 0.5 } }); | ||
}); | ||
it('When positional operator with field specified in nested object', function () { | ||
var obj = {stats: { group: { points: $.$('std').$set(0.5) }}}; | ||
$.flatten(obj).should.deep.equal({ $set: { 'stats.group.points.$.std': 0.5 } }); | ||
}); | ||
}); | ||
describe('# Empty values', function() { | ||
@@ -299,3 +354,3 @@ it('When property value is null sets to null', function () { | ||
it('When has many inner properties with operators', function () { | ||
var id = new ObjectID(); | ||
var id = new mongo.ObjectID(); | ||
var obj = { | ||
@@ -302,0 +357,0 @@ id: id, |
'use strict'; | ||
var MongoClient = require('mongodb').MongoClient; | ||
var MongoTimestamp = require('mongodb').Timestamp; | ||
var mongodb = require('mongodb'); | ||
var MongoClient = mongodb.MongoClient; | ||
var MongoTimestamp = mongodb.Timestamp; | ||
var MongoUrl = 'mongodb://localhost:27017/integration'; | ||
@@ -11,16 +12,21 @@ | ||
describe('# Integration tests', function() { | ||
describe('Fields operators', function() { | ||
var db; | ||
var db; | ||
before(function(done){ | ||
MongoClient.connect(MongoUrl) | ||
.then(function(x){ | ||
db = x; | ||
done(); | ||
}); | ||
}); | ||
after(function(){ return db.close(); }); | ||
describe('# Fields operators', function() { | ||
var collection; | ||
var searchCriteria = { email: 'john.doe@test.com' }; | ||
var getUser = function(){ return collection.findOne(searchCriteria); } | ||
before(function(){ collection = db.collection('users'); }); | ||
before(function(done){ | ||
MongoClient.connect(MongoUrl) | ||
.then(function(x){ | ||
db = x; | ||
collection = db.collection('users'); | ||
done(); | ||
}); | ||
}); | ||
beforeEach(function(){ | ||
@@ -42,7 +48,5 @@ return collection.insertOne({ | ||
after(function(){ return db.close(); }); | ||
it('Updates name', function() { | ||
it('Update name', function() { | ||
return collection.update(searchCriteria, $.flatten({ 'firstName': 'Jack' })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ | ||
@@ -53,105 +57,294 @@ return expect(x).to.have.property('firstName').that.equals('Jack'); | ||
it('Updates name to null', function () { | ||
it('Update name to null', function () { | ||
return collection.update(searchCriteria, $.flatten({ 'firstName': null })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('firstName').that.is.null; }); | ||
}); | ||
it('updates address city', function () { | ||
it('Update address city', function () { | ||
return collection.update(searchCriteria, $.flatten({ 'address': { 'city': 'Boston' } })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x.address).to.have.property('city').that.equals('Boston'); }); | ||
}); | ||
it('adds new address property: number', function () { | ||
it('Add new `number` property to address', function () { | ||
return collection.update(searchCriteria, $.flatten({ 'address': { 'number': 9 } })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ expect(x.address).to.have.property('number').that.equals(9); }); | ||
}); | ||
it('increments age by default with one', function () { | ||
it('Increment age with default value', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$inc() })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('age').that.equals(31); }); | ||
}); | ||
it('increments age by 5', function () { | ||
it('Increment age with 5', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$inc(5) })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('age').that.equals(35); }); | ||
}); | ||
it('multiply age by default with one', function () { | ||
it('Multiply age with default value', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$mul() })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ expect(x).to.have.property('age').that.equals(30); }); | ||
}); | ||
it('multiply age by two', function () { | ||
it('Multiply age by two', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$mul(2) })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('age').that.equals(60); }); | ||
}); | ||
it('rename firstName to first_name', function () { | ||
it('Rename firstName to first_name', function () { | ||
return collection.update(searchCriteria, $.flatten({ firstName: $.$rename('first_name') })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('first_name').that.equals('John'); }); | ||
}); | ||
it('unset lastName', function () { | ||
it('Unset lastName', function () { | ||
return collection.update(searchCriteria, $.flatten({ lastName: $.$unset() })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.not.have.property('lastName'); }); | ||
}); | ||
it('min when less then', function () { | ||
it('Update age to min when less than current value', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$min(27) })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('age').that.equals(27); }); | ||
}); | ||
it('min when greater then', function () { | ||
it('Update age to min when greater than current value', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$min(47) })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('age').that.equals(30); }); | ||
}); | ||
it('max when less then', function () { | ||
it('Update age to max when less than current value', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$max(27) })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('age').that.equals(30); }); | ||
}); | ||
it('max when greater then', function () { | ||
it('Update age to max when greater than current value', function () { | ||
return collection.update(searchCriteria, $.flatten({ age: $.$max(47) })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('age').that.equals(47); }); | ||
}); | ||
it('sets current date', function () { | ||
it('Set `updatedOn` to current date', function () { | ||
return collection.update(searchCriteria, $.flatten({ updatedOn: $.$currentDate() })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('updatedOn').that.respondTo('getTime'); }); | ||
}); | ||
it('sets current timestamp', function () { | ||
it('Set `time` to current timestamp', function () { | ||
return collection.update(searchCriteria, $.flatten({ time: $.$timestamp() })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.have.property('time').that.is.instanceOf(MongoTimestamp); }); | ||
}); | ||
it('setOnInsert when updated results in no changes', function () { | ||
it('Update `pass` with SetOnInsert', function () { | ||
return collection.update(searchCriteria, $.flatten({ pass: $.$setOnInsert('change-me-next-time') })) | ||
.then(function(){ return collection.findOne(searchCriteria); }) | ||
.then(getUser) | ||
.then(function(x){ return expect(x).to.not.have.property('pass'); }); | ||
}); | ||
it('setOnInsert when inserted results in no changes', function () { | ||
it('Insert `pass` with SetOnInsert', function () { | ||
var criteria = {email: 'test@test.com'}; | ||
return collection.update(criteria, $.flatten({ pass: $.$setOnInsert('change-me-next-time') }), {upsert: true}) | ||
var value = 'change-me-next-time'; | ||
return collection.update(criteria, $.flatten({ pass: $.$setOnInsert(value) }), {upsert: true}) | ||
.then(function(){ return collection.findOne(criteria); }) | ||
.then(function(x){ return expect(x).to.have.property('pass').that.equals('change-me-next-time'); }); | ||
.then(function(x){ return expect(x).to.have.property('pass').that.equals(value); }); | ||
}); | ||
}); | ||
describe('# Array operators', function(){ | ||
//{ _id: 1, scores: [ 0, 2, 5, 5, 1, 0 ] } | ||
var collection; | ||
var searchCriteria = { _id: 1 }; | ||
var getUser = function(){ return collection.findOne(searchCriteria); } | ||
before(function(){ collection = db.collection('users'); }); | ||
beforeEach(function(){ return collection.insertOne({ _id: 1, scores: [ 0, 2, 5, 5, 1, 0 ] }); }); | ||
afterEach(function(){ return collection.remove(); }); | ||
it('Increment value position found', function(){ | ||
return collection.update({ _id: 1, scores: {'$lt': 2} }, $.flatten({ scores: $.$().$inc(10) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.include(10, 2, 5, 5, 11, 10).and.have.lengthOf(6); }); | ||
}); | ||
it('Unset value position found', function(){ | ||
return collection.update({ _id: 1, scores: {'$eq': 0} }, $.flatten({ scores: $.$().$unset() })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.include(null, 2, 5, 5, 1, null).and.have.lengthOf(6); }); | ||
}); | ||
it('Set field value position found', function(){ | ||
return collection.remove() | ||
.then(function(){ | ||
collection.insertOne({ | ||
_id: 1, | ||
grades: [ | ||
{ grade: 80, mean: 75, std: 8 }, | ||
{ grade: 85, mean: 90, std: 5 }, | ||
{ grade: 90, mean: 85, std: 3 } | ||
] | ||
}); | ||
}) | ||
.then(function(){ return collection.update({_id: 1, 'grades.grade': 85 }, $.flatten({ grades: $.$('std').$set(6) })); }) | ||
.then(function(){ return collection.findOne({_id: 1, 'grades.grade': 85 }); }) | ||
.then(function(x){ | ||
return x.grades.should.deep.equal([ | ||
{ grade: 80, mean: 75, std: 8 }, | ||
{ grade: 85, mean: 90, std: 6 }, | ||
{ grade: 90, mean: 85, std: 3 } | ||
]); | ||
}); | ||
}); | ||
it('Add new element to set', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$addToSet(9) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.include(9).and.have.lengthOf(7); }); | ||
}); | ||
it('Add existing element to set', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$addToSet(2) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.include(2).and.have.lengthOf(6); }); | ||
}); | ||
it('Add array elements each', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$addToSet([2, 9]).$each() })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.include(9).and.have.lengthOf(7); }); | ||
}); | ||
it('Pop first element', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pop().first() })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([2, 5, 5, 1, 0]).and.have.lengthOf(5); }); | ||
}); | ||
it('Pop last element', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pop().last() })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 2, 5, 5, 1]).and.have.lengthOf(5); }); | ||
}); | ||
it('Pull all values of 5', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pullAll(5) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 2, 1, 0]).and.have.lengthOf(4); }); | ||
}); | ||
it('Pull all existing values', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pullAll([ 0, 2, 5, 5, 1, 0 ]) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.have.lengthOf(0); }); | ||
}); | ||
it('Pull value 5', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pull(5) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 2, 1, 0]).and.have.lengthOf(4); }); | ||
}); | ||
it('Pull values gte 2', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pull({$gte: 2}) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 1, 0]).and.have.lengthOf(3); }); | ||
}); | ||
it('Pull an array of values', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pull([0, 1]) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([2, 5, 5]).and.have.lengthOf(3); }); | ||
}); | ||
it('Push all values [2, 3]', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$pushAll([2, 3]) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 2, 5, 5, 1, 0, 2, 3]).and.have.lengthOf(8); }); | ||
}); | ||
it('Push 9', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push(9) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 2, 5, 5, 1, 0, 9]).and.have.lengthOf(7); }); | ||
}); | ||
it('Push 9 at 0-position', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push(9).$each().$position(0) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([9, 0, 2, 5, 5, 1, 0]).and.have.lengthOf(7); }); | ||
}); | ||
it('Push 9 and slice last 3 values', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push(9).$each().$slice(-3) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([1, 0, 9]).and.have.lengthOf(3); }); | ||
}); | ||
it('Push 9 and sort ASC', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push(9).$each().$sort() })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 0, 1, 2, 5, 5, 9]).and.have.lengthOf(7); }); | ||
}); | ||
it('Push 9 and sort DESC', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push(9).$each().$sort(-1) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([9, 5, 5, 2, 1, 0, 0]).and.have.lengthOf(7); }); | ||
}); | ||
it('Push [9, 99], sort ASC and slice last 3', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push([9, 99]).$each().$sort().$slice(-3) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([5, 9, 99]).and.have.lengthOf(3); }); | ||
}); | ||
it('Push [9, 99], sort ASC and slice first 3', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push([9, 99]).$each().$sort().$slice(3) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 0, 1]).and.have.lengthOf(3); }); | ||
}); | ||
it('Push [9, 99], at position 1 and slice first 3', function () { | ||
return collection.update(searchCriteria, $.flatten({ scores: $.$push([9, 99]).$each().$position(1).$slice(3) })) | ||
.then(getUser) | ||
.then(function(x){ return x.scores.should.deep.equal([0, 9, 99]).and.have.lengthOf(3); }); | ||
}); | ||
}); | ||
describe('# Bitwise operators', function(){ | ||
var collection; | ||
var searchCriteria = { map: 'NY' }; | ||
var getPoint = function(){ return collection.findOne(searchCriteria); } | ||
before(function(){ collection = db.collection('users'); }); | ||
beforeEach(function(){ return collection.insertOne({ map: 'NY', value: 11 }); }); | ||
afterEach(function(){ return collection.remove(); }); | ||
it('Perform bitwise AND operation 11 & 7 = 1011 & 0111 = 0011 = 3', function () { | ||
return collection.update(searchCriteria, $.flatten({ value: $.$and(7) })) | ||
.then(getPoint) | ||
.then(function(x){ return x.value.should.equal(3); }); | ||
}); | ||
it('Perform bitwise OR operation 11 | 7 = 1011 & 0111 = 1111 = 15', function () { | ||
return collection.update(searchCriteria, $.flatten({ value: $.$or(7) })) | ||
.then(getPoint) | ||
.then(function(x){ return x.value.should.equal(15); }); | ||
}); | ||
it('Perform bitwise XOR operation 11 | 7 = 1011 & 0111 = 1100 = 12', function () { | ||
return collection.update(searchCriteria, $.flatten({ value: $.$xor(7) })) | ||
.then(getPoint) | ||
.then(function(x){ return x.value.should.equal(12); }); | ||
}); | ||
}); | ||
}); |
'use strict'; | ||
var should = require('chai').should(); | ||
var expect = require('chai').expect; | ||
var $ = require('../index'); | ||
var Operator = require('../lib/operator'); | ||
describe('# Update operators', function () { | ||
describe('$inc', function () { | ||
it('When argument is undefined defaults to 1', function () { | ||
$.$inc().value().should.equal(1); | ||
describe('# Internals', function(){ | ||
describe('# Operator', function(){ | ||
it('Defines isOperator methos', function(){ | ||
Operator.should.itself.respondTo('isOperator'); | ||
}); | ||
it('When argument is set uses its value', function () { | ||
var value = 123; | ||
$.$inc(value).value().should.equal(value); | ||
it('When null name throws', function(){ | ||
expect(function(){ new Operator(); }).to.throw(); | ||
}); | ||
}); | ||
describe('$mul', function () { | ||
it('When argument is undefined defaults to 1', function () { | ||
$.$mul().value().should.equal(1); | ||
it('Name is set', function(){ | ||
new Operator('inc').should.have.property('name'). | ||
that.equals('inc'); | ||
}); | ||
it('When argument is set uses its value', function () { | ||
var value = 10; | ||
$.$mul(value).value().should.equal(value); | ||
it('When value not set defaults to undefined', function(){ | ||
expect(new Operator('test').value()).to.be.undefined; | ||
}); | ||
}); | ||
describe('$rename', function(){ | ||
it('Has expected value', function () { | ||
var value = 'test'; | ||
$.$rename(value).value().should.equal(value); | ||
it('When value set to null returns null value', function(){ | ||
expect(new Operator('test').value(null).value()).to.be.null; | ||
}); | ||
}); | ||
describe('$setOnInsert', function(){ | ||
it('Has expected value', function () { | ||
var value = {x: 10, y: 20}; | ||
$.$setOnInsert(value).value().should.equal(value); | ||
it('When value set returns same value', function(){ | ||
var val = {}; | ||
new Operator('test').value(val).value().should.equal(val); | ||
}); | ||
}); | ||
describe('$set', function(){ | ||
it('Has expected value', function () { | ||
var value = { x: 10, y: 20 }; | ||
$.$set(value).value().should.equal(value); | ||
it('When value set chains self', function(){ | ||
var op = new Operator('test'); | ||
op.value({}).should.equal(op); | ||
}); | ||
}); | ||
}); | ||
describe('$unset', function(){ | ||
it('Has empty string value', function () { | ||
$.$unset().value().should.equal(''); | ||
describe('# Update operators', function () { | ||
describe('# Field', function(){ | ||
describe('$inc', function () { | ||
it('Is operator', function () { | ||
$.isOperator($.$inc()).should.be.true; | ||
}); | ||
it('Has $inc name', function () { | ||
$.$inc().name.should.equal('$inc'); | ||
}); | ||
it('When argument is undefined defaults to 1', function () { | ||
$.$inc().value().should.equal(1); | ||
}); | ||
it('When argument is set uses its value', function () { | ||
var value = 123; | ||
$.$inc(value).value().should.equal(value); | ||
}); | ||
}); | ||
}); | ||
describe('$min', function(){ | ||
it('Has expected value', function () { | ||
var value = 10; | ||
$.$min(value).value().should.equal(value); | ||
describe('$mul', function () { | ||
it('Is operator', function () { | ||
$.isOperator($.$mul()).should.be.true; | ||
}); | ||
it('Has $mul name', function () { | ||
$.$mul().name.should.equal('$mul'); | ||
}); | ||
it('When argument is undefined defaults to 1', function () { | ||
$.$mul().value().should.equal(1); | ||
}); | ||
it('When argument is set uses its value', function () { | ||
var value = 10; | ||
$.$mul(value).value().should.equal(value); | ||
}); | ||
}); | ||
}); | ||
describe('$max', function(){ | ||
it('Has expected value', function () { | ||
var value = 10; | ||
$.$max(value).value().should.equal(value); | ||
describe('$rename', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$rename('field')).should.be.true; | ||
}); | ||
it('Has $rename name', function () { | ||
$.$rename('field').name.should.equal('$rename'); | ||
}); | ||
it('Has expected value', function () { | ||
var value = 'test'; | ||
$.$rename(value).value().should.equal(value); | ||
}); | ||
}); | ||
}); | ||
describe('$currentDate', function(){ | ||
it('When argument is undefined defaults to date type', function () { | ||
$.$currentDate().value().should.be.deep.equal({ $type: 'date' }); | ||
describe('$setOnInsert', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$setOnInsert(1)).should.be.true; | ||
}); | ||
it('Has $setOnInsert name', function () { | ||
$.$setOnInsert(10).name.should.equal('$setOnInsert'); | ||
}); | ||
it('Has expected value', function () { | ||
var value = {x: 10, y: 20}; | ||
$.$setOnInsert(value).value().should.equal(value); | ||
}); | ||
}); | ||
it('When argument is set uses its value', function () { | ||
$.$currentDate('timestamp').value() | ||
.should.be.deep.equal({ $type: 'timestamp' }); | ||
describe('$set', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$set(1)).should.be.true; | ||
}); | ||
it('Has $set name', function () { | ||
$.$set(10).name.should.equal('$set'); | ||
}); | ||
it('Has expected value', function () { | ||
var value = { x: 10, y: 20 }; | ||
$.$set(value).value().should.equal(value); | ||
}); | ||
}); | ||
describe('$unset', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$unset()).should.be.true; | ||
}); | ||
it('Has $unset name', function () { | ||
$.$unset().name.should.equal('$unset'); | ||
}); | ||
it('Has empty string value', function () { | ||
$.$unset().value().should.equal(''); | ||
}); | ||
}); | ||
describe('$min', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$min(1)).should.be.true; | ||
}); | ||
it('Has $min name', function () { | ||
$.$min(1).name.should.equal('$min'); | ||
}); | ||
it('Has expected value', function () { | ||
var value = 10; | ||
$.$min(value).value().should.equal(value); | ||
}); | ||
}); | ||
describe('$max', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$max(1)).should.be.true; | ||
}); | ||
it('Has $max name', function () { | ||
$.$max(1).name.should.equal('$max'); | ||
}); | ||
it('Has expected value', function () { | ||
var value = 10; | ||
$.$max(value).value().should.equal(value); | ||
}); | ||
}); | ||
describe('$currentDate', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$currentDate()).should.be.true; | ||
}); | ||
it('Has $currentDate name', function () { | ||
$.$currentDate().name.should.equal('$currentDate'); | ||
}); | ||
it('When argument is undefined defaults to date type', function () { | ||
$.$currentDate().value().should.be.deep.equal({ $type: 'date' }); | ||
}); | ||
it('When argument is set uses its value', function () { | ||
$.$currentDate('timestamp').value() | ||
.should.be.deep.equal({ $type: 'timestamp' }); | ||
}); | ||
}); | ||
describe('$timestamp *', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$timestamp()).should.be.true; | ||
}); | ||
it('Has $currentDate name', function () { | ||
$.$timestamp().name.should.equal('$currentDate'); | ||
}); | ||
it('Has timestamp type value', function () { | ||
$.$timestamp().value().should.be.deep.equal({ $type: 'timestamp' }); | ||
}); | ||
}); | ||
}); | ||
describe('# Array', function(){ | ||
describe('$', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$()).should.be.true; | ||
}); | ||
it('Has $ name when no field specified', function(){ | ||
$.$().name.should.equal('$'); | ||
}); | ||
it('Appends field to name when field specified', function(){ | ||
$.$('std').name.should.equal('$.std'); | ||
}); | ||
it('Appends index when number specified', function(){ | ||
$.$(3).name.should.equal('3'); | ||
}); | ||
describe('$timestamp', function(){ | ||
it('Has timestamp type value', function () { | ||
$.$timestamp().value().should.be.deep.equal({ $type: 'timestamp' }); | ||
it('Appends index when string number specified', function(){ | ||
$.$('3').name.should.equal('3'); | ||
}); | ||
it('Appends index with field when mathes number', function(){ | ||
$.$('3.std').name.should.equal('3.std'); | ||
}); | ||
it('Appends index with field when mathes 0', function(){ | ||
$.$('0.std').name.should.equal('0.std'); | ||
}); | ||
it('Value throws when not set', function(){ | ||
expect(function(){ $.$().value(); }).to.throw(); | ||
}); | ||
it('Value throws when not supported operator is set', function(){ | ||
expect(function(){ $.$().value($.$setOnInsert(10)); }).to.throw(/setOnInsert/); | ||
}); | ||
it('When value set defaults to $set', function(){ | ||
$.$().value(101).value().value().should.equal(101); | ||
}); | ||
it('When value set with $set with null', function(){ | ||
expect($.$().value($.$set(null)).value().value()).to.be.null; | ||
}); | ||
it('When value set to null', function(){ | ||
var operator = $.$unset(); | ||
expect($.$().value($.$set(null)).value().value()).to.be.null; | ||
}); | ||
it('When value set with operator returns operator', function(){ | ||
var operator = $.$unset(); | ||
$.$().value(operator).value().should.equal(operator); | ||
}); | ||
it('When chained operator', function(){ | ||
$.$().$inc(10).value().should.have.property('name').that.equals('$inc'); | ||
}); | ||
it('Is chainable with $inc', function(){ | ||
$.$().should.respondTo('$inc'); | ||
}); | ||
it('Is chainable with $mul', function(){ | ||
$.$().should.respondTo('$mul'); | ||
}); | ||
it('Is chainable with $set', function(){ | ||
$.$().should.respondTo('$set'); | ||
}); | ||
it('Is chainable with $unset', function(){ | ||
$.$().should.respondTo('$unset'); | ||
}); | ||
it('Is chainable with $min', function(){ | ||
$.$().should.respondTo('$min'); | ||
}); | ||
it('Is chainable with $max', function(){ | ||
$.$().should.respondTo('$max'); | ||
}); | ||
it('Is chainable with $currentDate', function(){ | ||
$.$().should.respondTo('$currentDate'); | ||
}); | ||
it('Is chainable with $timestamp', function(){ | ||
$.$().should.respondTo('$timestamp'); | ||
}); | ||
}); | ||
describe('$addToSet', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$addToSet(1)).should.be.true; | ||
}); | ||
it('Has $addToSet name', function () { | ||
$.$addToSet(1).name.should.equal('$addToSet'); | ||
}); | ||
it('When is null value', function () { | ||
expect($.$addToSet(null).value()).to.be.null; | ||
}); | ||
it('When is scalar value', function () { | ||
var value = 10; | ||
$.$addToSet(value).value().should.equal(value); | ||
}); | ||
it('When is null value with each', function () { | ||
$.$addToSet(null).$each().value().should.deep.equal({ '$each': [null] }); | ||
}); | ||
it('When is array value', function () { | ||
var value = [1, 2, 3]; | ||
$.$addToSet(value).value().should.equal(value); | ||
}); | ||
it('When is scalar value with each', function () { | ||
var value = 10; | ||
$.$addToSet(value).$each().value().should.deep.equal({ '$each': [10] }); | ||
}); | ||
it('When is array value with each', function () { | ||
var value = [1, 2, 3]; | ||
$.$addToSet(value).$each().value().should.deep.equal({ '$each': [1, 2, 3] }); | ||
}); | ||
}); | ||
describe('$pop', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$pop(1)).should.be.true; | ||
}); | ||
it('Has $pop name', function () { | ||
$.$pop(1).name.should.equal('$pop'); | ||
}); | ||
it('When direction not specified uses 1 by default', function(){ | ||
$.$pop().value().should.equal(1); | ||
}); | ||
it('When direction is 1 uses 1', function(){ | ||
$.$pop(1).value().should.equal(1); | ||
}); | ||
it('When direction is -1 uses -1', function(){ | ||
$.$pop(-1).value().should.equal(-1); | ||
}); | ||
it('When chained with first uses -1', function(){ | ||
$.$pop().first().value().should.equal(-1); | ||
}); | ||
it('When chained with last uses 1', function(){ | ||
$.$pop().last().value().should.equal(1); | ||
}); | ||
}); | ||
describe('$pullAll', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$pullAll(1)).should.be.true; | ||
}); | ||
it('Has $pullAll name', function () { | ||
$.$pullAll(1).name.should.equal('$pullAll'); | ||
}); | ||
it('When null value specified returns array of null element', function(){ | ||
$.$pullAll(null).value().should.deep.equal([null]); | ||
}); | ||
it('When empty array specified returns empty array', function(){ | ||
$.$pullAll([]).value().should.deep.equal([]); | ||
}); | ||
it('When value specified returns array of value element', function(){ | ||
var value = 'Test'; | ||
$.$pullAll(value).value().should.deep.equal([value]); | ||
}); | ||
it('When array specified returns array', function(){ | ||
var value = [1, 2, 3]; | ||
$.$pullAll(value).value().should.deep.equal(value); | ||
}) | ||
}); | ||
describe('$pull', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$pull(1)).should.be.true; | ||
}); | ||
it('Has $pull name', function () { | ||
$.$pull(1).name.should.equal('$pull'); | ||
}); | ||
it('When null value specified returns null', function(){ | ||
expect($.$pull(null).value()).to.equal(null); | ||
}); | ||
it('When scalar value specified returns value', function(){ | ||
var value = 100; | ||
$.$pull(value).value().should.to.equal(value); | ||
}); | ||
it('When object value specified returns value', function(){ | ||
var value = { score: 8, item: "B" }; | ||
$.$pull(value).value().should.to.deep.equal(value); | ||
}); | ||
it('When array value specified applies $in operator', function(){ | ||
var value = ['A', 'B', 'C']; | ||
$.$pull(value).value().should.to.deep.equal({ '$in': value }); | ||
}); | ||
}); | ||
describe('$pushAll', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$pushAll(1)).should.be.true; | ||
}); | ||
it('Has $pushAll name', function () { | ||
$.$pushAll(1).name.should.equal('$pushAll'); | ||
}); | ||
it('When null value specified returns null', function(){ | ||
expect($.$pushAll(null).value()).to.deep.equal([null]); | ||
}); | ||
it('When scalar value specified returns array of value', function(){ | ||
var value = 100; | ||
$.$pushAll(value).value().should.deep.equal([value]); | ||
}); | ||
it('When object value specified returns array of value', function(){ | ||
var value = { score: 8, item: 'B' }; | ||
$.$pushAll(value).value().should.deep.equal([value]); | ||
}); | ||
it('When array value specified returns array', function(){ | ||
var value = ['A', 'B', 'C']; | ||
$.$pushAll(value).value().should.deep.equal(value); | ||
}); | ||
}); | ||
describe('$push', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$push(1)).should.be.true; | ||
}); | ||
it('Has $push name', function(){ | ||
$.$push().name.should.equal('$push'); | ||
}); | ||
it('When null value specified returns null', function(){ | ||
expect($.$push(null).value()).to.equal(null); | ||
}); | ||
it('When undefined value specified returns undefined', function(){ | ||
expect($.$push().value()).to.equal(undefined); | ||
}); | ||
it('When scalar value specified returns value', function(){ | ||
var value = 9; | ||
$.$push(value).value().should.equal(value); | ||
}); | ||
it('When object value specified returns value', function(){ | ||
var value = { data: 'test'}; | ||
$.$push(value).value().should.deep.equal(value); | ||
}); | ||
it('When array value specified returns array', function(){ | ||
var value = [1, 2, 3]; | ||
$.$push(value).value().should.deep.equal(value); | ||
}); | ||
it('When no value specified with $each', function(){ | ||
$.$push().$each().value().should.deep.equal({ '$each': []}); | ||
}); | ||
it('When scalar value specified with $each', function(){ | ||
var value = 100; | ||
$.$push(value).$each().value().should.deep.equal({ '$each': [value]}); | ||
}); | ||
it('When array value specified with $each', function(){ | ||
var value = [1, 2, 3]; | ||
$.$push(value).$each().value().should.deep.equal({ '$each': value}); | ||
}); | ||
it('When using $slice without $each throws', function(){ | ||
expect(function(){ $.$push(10).$slice(1); }).to.throw(/\$slice/); | ||
}); | ||
it('When using $slice with empty value is ignored', function(){ | ||
$.$push(10).$each().$slice().value() | ||
.should.deep.equal({'$each': [10]}); | ||
}); | ||
it('When using $slice sets value', function(){ | ||
$.$push(10).$each().$slice(-3).value() | ||
.should.deep.equal({'$each': [10], '$slice': -3}); | ||
}); | ||
it('When using $sort without $each throws', function(){ | ||
expect(function(){ $.$push(10).$sort(1); }).to.throw(/\$sort/); | ||
}); | ||
it('When using $sort with empty value uses 1 by default', function(){ | ||
$.$push(10).$each().$sort().value() | ||
.should.deep.equal({'$each': [10], '$sort': 1}); | ||
}); | ||
it('When using $sort sets value', function(){ | ||
$.$push(10).$each().$sort({ val: 1 }).value() | ||
.should.deep.equal({'$each': [10], '$sort': { val: 1}}); | ||
}); | ||
it('When using $position without $each throws', function(){ | ||
expect(function(){ $.$push(10).$position(1); }).to.throw(/\$position/); | ||
}); | ||
it('When using $position with empty value is ignored', function(){ | ||
$.$push(10).$each().$position().value() | ||
.should.deep.equal({'$each': [10]}); | ||
}); | ||
it('When using $position sets value', function(){ | ||
$.$push(10).$each().$position(0).value() | ||
.should.deep.equal({'$each': [10], '$position': 0}); | ||
}); | ||
it('When using $each, $slice, $sort and $position', function(){ | ||
$.$push([{score: 9}, {score: 10}, {score: 7}]) | ||
.$each() | ||
.$position(0) | ||
.$sort({score: -1}) | ||
.$slice(3) | ||
.value() | ||
.should.deep.equal({ | ||
'$each': [{score: 9}, {score: 10}, {score: 7}], | ||
'$sort': {score: -1}, | ||
'$slice': 3, | ||
'$position': 0 | ||
}) | ||
}); | ||
}); | ||
describe('$slice *', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$slice(1)).should.be.true; | ||
}); | ||
it('Has $push name', function(){ | ||
$.$slice(1).name.should.equal('$push'); | ||
}); | ||
it('Is same as $push of empty array', function(){ | ||
$.$slice(1).value().should.deep.equal({ '$each': [], '$slice': 1 }); | ||
}); | ||
}); | ||
describe('$sort *', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$sort(1)).should.be.true; | ||
}); | ||
it('Has $push name', function(){ | ||
$.$sort(1).name.should.equal('$push'); | ||
}); | ||
it('Is same as $push of empty array', function(){ | ||
$.$sort(1).value().should.deep.equal({ '$each': [], '$sort': 1 }); | ||
}); | ||
}); | ||
}); | ||
describe('# Bitwise', function(){ | ||
describe('$bit', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$bit()).should.be.true; | ||
}); | ||
it('Has $bit name', function(){ | ||
$.$bit().name.should.equal('$bit'); | ||
}); | ||
it('Is chainable with $and', function(){ | ||
$.$bit().should.respondTo('$and'); | ||
}); | ||
it('When chained with $and sets value', function(){ | ||
$.$bit().$and(1).value().should.deep.equal({ 'and': 1 }); | ||
}); | ||
it('Is chainable with $or', function(){ | ||
$.$bit().should.respondTo('$or'); | ||
}); | ||
it('When chained with $or sets value', function(){ | ||
$.$bit().$or(1).value().should.deep.equal({ 'or': 1 }); | ||
}); | ||
it('Is chainable with $xor', function(){ | ||
$.$bit().should.respondTo('$xor'); | ||
}); | ||
it('When chained with $or sets value', function(){ | ||
$.$bit().$xor(1).value().should.deep.equal({ 'xor': 1 }); | ||
}); | ||
it('Value throws', function(){ | ||
expect(function(){ $.$bit().value(); }).to.throw(); | ||
}); | ||
}); | ||
describe('$and *', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$and(5)).should.be.true; | ||
}); | ||
it('Has $bit name', function(){ | ||
$.$and(5).name.should.equal('$bit'); | ||
}); | ||
it('When value provided', function(){ | ||
$.$and(5).value().should.deep.equal({ 'and': 5 }); | ||
}); | ||
}); | ||
describe('$or *', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$or(5)).should.be.true; | ||
}); | ||
it('Has $bit name', function(){ | ||
$.$or(5).name.should.equal('$bit'); | ||
}); | ||
it('When value provided', function(){ | ||
$.$or(5).value().should.deep.equal({ 'or': 5 }); | ||
}); | ||
}); | ||
describe('$xor *', function(){ | ||
it('Is operator', function () { | ||
$.isOperator($.$xor(5)).should.be.true; | ||
}); | ||
it('Has $bit name', function(){ | ||
$.$xor(5).name.should.equal('$bit'); | ||
}); | ||
it('When value provided', function(){ | ||
$.$xor(5).value().should.deep.equal({ 'xor': 5 }); | ||
}); | ||
}); | ||
}); | ||
}); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
78473
16
1425
636