├── .github └── workflows │ └── test.yml ├── .gitignore ├── .npmignore ├── .npmrc ├── .travis.yml ├── HEADER.md ├── History.md ├── LICENSE ├── Makefile ├── README.md ├── examples.md ├── index.js ├── lib └── index.js ├── package.json └── test ├── example.js └── index.js /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | on: 3 | pull_request: 4 | push: 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | test: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | node: [12, 14, 16, 17] 15 | os: [ubuntu-18.04, ubuntu-20.04] 16 | include: 17 | - os: ubuntu-18.04 18 | mongo-os: ubuntu1804 19 | mongo: 4.0.2 20 | - os: ubuntu-20.04 21 | mongo-os: ubuntu2004 22 | mongo: 5.0.2 23 | name: Node ${{ matrix.node }} MongoDB ${{ matrix.mongo }} 24 | steps: 25 | - uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3 26 | 27 | - name: Setup node 28 | uses: actions/setup-node@5b52f097d36d4b0b2f94ed6de710023fbb8b2236 # v3.1.0 29 | with: 30 | node-version: ${{ matrix.node }} 31 | 32 | - run: npm install 33 | 34 | - name: Setup 35 | run: | 36 | wget -q https://downloads.mongodb.org/linux/mongodb-linux-x86_64-${{ matrix.mongo-os }}-${{ matrix.mongo }}.tgz 37 | tar xf mongodb-linux-x86_64-${{ matrix.mongo-os }}-${{ matrix.mongo }}.tgz 38 | mkdir -p ./data/db/27017 ./data/db/27000 39 | printf "\n--timeout 8000" >> ./test/mocha.opts 40 | ./mongodb-linux-x86_64-${{ matrix.mongo-os }}-${{ matrix.mongo }}/bin/mongod --setParameter ttlMonitorSleepSecs=1 --fork --dbpath ./data/db/27017 --syslog --port 27017 41 | sleep 2 42 | mongod --version 43 | echo `pwd`/mongodb-linux-x86_64-${{ matrix.mongo-os }}-${{ matrix.mongo }}/bin >> $GITHUB_PATH 44 | - run: npm test 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw* 2 | node_modules/ 3 | 4 | package-lock.json 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 6 4 | - 7 5 | - 8 6 | - 9 7 | - 10 8 | before_script: 9 | - wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3.6.4.tgz 10 | - tar -zxvf mongodb-linux-x86_64-3.6.4.tgz 11 | - mkdir -p ./data/db/27017 12 | - mkdir -p ./data/db/27000 13 | - ./mongodb-linux-x86_64-3.6.4/bin/mongod --fork --nopreallocj --dbpath ./data/db/27017 --syslog --port 27017 14 | notifications: 15 | email: false 16 | -------------------------------------------------------------------------------- /HEADER.md: -------------------------------------------------------------------------------- 1 | # mongoose-double 2 | 3 | [Mongoose](http://mongoosejs.com/) type for storing MongoDB doubles [(bson type 1)](http://bsonspec.org/spec.html) 4 | 5 | [![Build Status](https://secure.travis-ci.org/mongoosejs/mongoose-double.png)](http://travis-ci.org/mongoosejs/mongoose-double) 6 | 7 | # Importing 8 | 9 | ```javascript 10 | // Using Node.js `require()` 11 | const Double = require('@mongoosejs/double'); 12 | 13 | // Using ES6 imports 14 | import Double from '@mongoosejs/double'; 15 | ``` 16 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 0.3.0 / 2022-09-04 2 | ================== 3 | * feat: support Mongoose 6.x #13 [DiegofrApps](https://github.com/DiegofrApps) 4 | 5 | 0.2.0 / 2019-11-10 6 | ================== 7 | * feat: add support for comparison query selectors `$gt`, `$lt`, etc. #8 [w33ble](https://github.com/w33ble) 8 | * feat: remove `valueOf()` and add `toBSON()` to support using arithmetic operators with doubles #7 9 | * fix: add Mongoose as peerDependency and pin minimum version 5.7.9 10 | 11 | 0.1.2 / 2018-10-16 12 | ================== 13 | * chore: add homepage to npm 14 | * docs: add examples.md 15 | 16 | 0.1.1 / 2018-05-07 17 | ================== 18 | 19 | * docs: clean up example code formatting 20 | 21 | 0.1.0 / 2018-05-01 22 | ================== 23 | 24 | * feat: add mongoose 5 support, re: Automattic/mongoose#5813 25 | 26 | 0.0.1 / 2012-11-16 27 | ================== 28 | 29 | * initial release 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012 [Aaron Heckmann](aaron.heckmann+github@gmail.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | test: 3 | @./node_modules/.bin/mocha --reporter list $(T) 4 | 5 | .PHONY: test 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mongoose-double 2 | 3 | [Mongoose](http://mongoosejs.com/) type for storing MongoDB doubles [(bson type 1)](http://bsonspec.org/spec.html) 4 | 5 | [![Build Status](https://secure.travis-ci.org/mongoosejs/mongoose-double.png)](http://travis-ci.org/mongoosejs/mongoose-double) 6 | 7 | [Read the Docs](http://plugins.mongoosejs.io/plugins/double) 8 | 9 | # Importing 10 | 11 | ```javascript 12 | // Using Node.js `require()` 13 | const Double = require('@mongoosejs/double'); 14 | 15 | // Using ES6 imports 16 | import Double from '@mongoosejs/double'; 17 | ``` 18 | -------------------------------------------------------------------------------- /examples.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Examples 4 | 5 | ## It ensures your numbers are always stored as doubles in MongoDB 6 | 7 | ```javascript 8 | const schema = new mongoose.Schema({ val: Double }); 9 | const Model = mongoose.model('Double', schema); 10 | 11 | const doc = new Model({ val: 41 }); 12 | 13 | // `doc.val` will be an object, not a number, but you can still use it as 14 | // a number 15 | assert.ok(doc.val instanceof mongoose.Types.Double); 16 | assert.ok(doc.val instanceof Number); 17 | assert.equal(typeof doc.val, 'object'); 18 | 19 | ++doc.val; 20 | 21 | return doc.save(). 22 | then(function() { 23 | return Model.findOne({ val: { $type: 'double' } }); 24 | }). 25 | then(function(doc) { 26 | assert.ok(doc); 27 | assert.equal(doc.val, 42); 28 | }). 29 | then(function() { 30 | return Model.findOne({ val: { $type: 'int' } }); 31 | }). 32 | then(function(doc) { 33 | assert.ok(!doc); 34 | }); 35 | ``` 36 | 37 | ## It bypasses the MongoDB driver's integer coercion 38 | 39 | 40 | This plugin is useful because the [MongoDB Node.js driver](https://www.npmjs.com/package/mongodb) 41 | will store the JavaScript number `5.01` as a double, but it will store `5` 42 | as an integer. There is currently no option to opt out of this behavior 43 | in the MongoDB driver. 44 | 45 | 46 | ```javascript 47 | const schema = new mongoose.Schema({ val: Number }); 48 | const Model = mongoose.model('Number', schema); 49 | 50 | return Model.create([{ val: 5.01 }, { val: 5 }]). 51 | then(function() { 52 | return Model.findOne({ val: { $type: 'double' } }); 53 | }). 54 | then(function(doc) { 55 | assert.ok(doc); 56 | assert.equal(doc.val, 5.01); 57 | }). 58 | then(function() { 59 | return Model.findOne({ val: { $type: 'int' } }); 60 | }). 61 | then(function(doc) { 62 | assert.ok(doc); 63 | assert.equal(doc.val, 5); 64 | }); 65 | ``` 66 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = exports = require('./lib'); 2 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const mongoose = require('mongoose'); 4 | 5 | class DoubleType extends Number { 6 | constructor(v) { 7 | super(v); 8 | this.value = v; 9 | this._bsontype = 'Double'; 10 | } 11 | 12 | toBSON() { 13 | return this; 14 | } 15 | } 16 | 17 | class Double extends mongoose.SchemaType { 18 | constructor(key, options) { 19 | super(key, options, 'Double'); 20 | 21 | Object.assign(this.$conditionalHandlers, { 22 | '$lt': val => this.castForQuery(val), 23 | '$lte': val => this.castForQuery(val), 24 | '$gt': val => this.castForQuery(val), 25 | '$gte': val => this.castForQuery(val), 26 | }); 27 | } 28 | 29 | cast(val) { 30 | if (val == null) { 31 | return val; 32 | } 33 | if (val._bsontype === 'Double') { 34 | return new DoubleType(val.value); 35 | } 36 | 37 | const _val = Number(val); 38 | if (isNaN(_val)) { 39 | throw new mongoose.SchemaType.CastError('Double', 40 | val + ' is not a valid double'); 41 | } 42 | return new DoubleType(_val); 43 | } 44 | } 45 | 46 | mongoose.Schema.Types.Double = Double; 47 | mongoose.Types.Double = DoubleType; 48 | 49 | module.exports = Double; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@mongoosejs/double", 3 | "private": false, 4 | "version": "0.3.0", 5 | "homepage": "http://plugins.mongoosejs.io/plugins/double", 6 | "description": "Double support for Mongoose", 7 | "main": "index.js", 8 | "scripts": { 9 | "docs": "acquit-markdown -r acquit-ignore -p './test/example.js' > examples.md", 10 | "test": "mocha test/*.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git://github.com/mongoosejs/mongoose-double.git" 15 | }, 16 | "keywords": [ 17 | "mongoose", 18 | "double", 19 | "number", 20 | "type", 21 | "schematype", 22 | "schema" 23 | ], 24 | "author": "Valeri Karpov ", 25 | "license": "MIT", 26 | "peerDependencies": { 27 | "mongoose": "^5.7.9 || 6.x" 28 | }, 29 | "devDependencies": { 30 | "acquit": "1.x", 31 | "acquit-ignore": "0.1.x", 32 | "acquit-markdown": "0.1.x", 33 | "co": "4.6.0", 34 | "mocha": "*", 35 | "mongoose": "6.x" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /test/example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Double = require('../'); 4 | const assert = require('assert'); 5 | const mongoose = require('mongoose'); 6 | 7 | if (process.env.D) { 8 | mongoose.set('debug', true); 9 | } 10 | 11 | describe('Examples', function() { 12 | before(function() { 13 | mongoose.connect('mongodb://localhost:27017/doubletest', { 14 | useNewUrlParser: true, 15 | useUnifiedTopology: true 16 | }); 17 | }); 18 | 19 | after(function() { 20 | return mongoose.disconnect(); 21 | }); 22 | 23 | beforeEach(() => mongoose.connection.dropDatabase()); 24 | 25 | it('ensures your numbers are always stored as doubles in MongoDB', function() { 26 | const schema = new mongoose.Schema({ val: Double }); 27 | const Model = mongoose.model('Double', schema); 28 | 29 | const doc = new Model({ val: 41 }); 30 | 31 | // `doc.val` will be an object, not a number, but you can still use it as 32 | // a number 33 | assert.ok(doc.val instanceof mongoose.Types.Double); 34 | assert.ok(doc.val instanceof Number); 35 | assert.equal(typeof doc.val, 'object'); 36 | 37 | ++doc.val; 38 | 39 | return doc.save(). 40 | then(function() { 41 | return Model.findOne({ val: { $type: 'double' } }); 42 | }). 43 | then(function(doc) { 44 | assert.ok(doc); 45 | assert.equal(doc.val, 42); 46 | }). 47 | then(function() { 48 | return Model.findOne({ val: { $type: 'int' } }); 49 | }). 50 | then(function(doc) { 51 | assert.ok(!doc); 52 | }); 53 | }); 54 | 55 | /** 56 | * This plugin is useful because the [MongoDB Node.js driver](https://www.npmjs.com/package/mongodb) 57 | * will store the JavaScript number `5.01` as a double, but it will store `5` 58 | * as an integer. There is currently no option to opt out of this behavior 59 | * in the MongoDB driver. 60 | */ 61 | it('bypasses the MongoDB driver\'s integer coercion', function() { 62 | const schema = new mongoose.Schema({ val: Number }); 63 | const Model = mongoose.model('Number', schema); 64 | 65 | return Model.create([{ val: 5.01 }, { val: 5 }]). 66 | then(function() { 67 | return Model.findOne({ val: { $type: 'double' } }); 68 | }). 69 | then(function(doc) { 70 | assert.ok(doc); 71 | assert.equal(doc.val, 5.01); 72 | }). 73 | then(function() { 74 | return Model.findOne({ val: { $type: 'int' } }); 75 | }). 76 | then(function(doc) { 77 | assert.ok(doc); 78 | assert.equal(doc.val, 5); 79 | }); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Double = require('../'); 4 | const assert = require('assert'); 5 | const co = require('co'); 6 | const mongoose = require('mongoose'); 7 | 8 | describe('Double', function() { 9 | let Model; 10 | 11 | before(function() { 12 | mongoose.connect('mongodb://localhost:27017/doubletest'); 13 | const schema = new mongoose.Schema({ double: Double, name: 'string' }); 14 | Model = mongoose.model('Test', schema); 15 | }); 16 | 17 | after(function() { 18 | return mongoose.disconnect(); 19 | }); 20 | 21 | describe('casts', function() { 22 | it('numbers', function() { 23 | let doc = new Model({ double: 20000000.1 }); 24 | assert.ok(doc.double instanceof mongoose.Types.Double); 25 | assert.equal(doc.double.valueOf(), 20000000.1); 26 | assert.equal(doc.double, 20000000.1); 27 | 28 | const v = new Number(20000000.0); 29 | doc = new Model({ double: v }); 30 | assert.ok(doc.double instanceof mongoose.Types.Double); 31 | assert.equal(+doc.double.value, +v); 32 | }); 33 | 34 | it('null', function() { 35 | assert.strictEqual(new Model({ double: null }).double, null); 36 | }) 37 | 38 | it('mongoose.Types.Double', function() { 39 | const doc = new Model({ double: new mongoose.Types.Double('90') }); 40 | assert.ok(doc.double instanceof mongoose.Types.Double); 41 | assert.equal(doc.double, 90); 42 | }); 43 | }); 44 | 45 | it('saves as correct type', function() { 46 | return co(function*() { 47 | const schema = new mongoose.Schema({ val: Double }); 48 | const numSchema = new mongoose.Schema({ val: Number }); 49 | const Model = mongoose.model('DoubleTest1', schema, 'doubletest1'); 50 | const NumModel = mongoose.model('DoubleTest2', numSchema, 'doubletest1'); 51 | 52 | const doc = new Model({ val: 5 }); 53 | ++doc.val; 54 | yield doc.save(); 55 | 56 | assert.ok(yield Model.findOne({ val: { $type: 1 } })); 57 | assert.ok(yield Model.findOne({ val: 6 })); 58 | assert.ok(yield NumModel.findOne({ val: 6 })); 59 | }); 60 | }); 61 | 62 | it('works in update', function() { 63 | return co(function*() { 64 | const doc = yield Model.create({ double: 1 }); 65 | 66 | yield Model.updateOne({}, { $set: { double: 2 } }); 67 | 68 | assert.ok(yield Model.findOne({ double: { $type: 1 } })); 69 | }); 70 | }); 71 | 72 | it('can be default', function() { 73 | return co(function*() { 74 | const b = new Number(1.11); 75 | const s = new mongoose.Schema({ double: { type: Double, default: b }}); 76 | const M = mongoose.model('DefaultTest', s); 77 | const doc = yield M.create({}); 78 | assert.equal(doc.double.valueOf(), 1.11); 79 | }); 80 | }); 81 | 82 | describe('comparisons', function() { 83 | let ComparisonModel; 84 | 85 | before(function() { 86 | return co(function*() { 87 | const schema = new mongoose.Schema({ val: Double }); 88 | ComparisonModel = mongoose.model('ComparisonTest', schema); 89 | const doc = new ComparisonModel({ val: 5.01 }); 90 | yield doc.save(); 91 | }); 92 | }); 93 | 94 | it('$gt', function() { 95 | return co(function*() { 96 | assert.ok(yield ComparisonModel.findOne({ val: { $gt: 5 }})); 97 | assert.ok(yield ComparisonModel.findOne({ val: { $gt: 5.0 }})); 98 | assert.strictEqual(yield ComparisonModel.findOne({ val: { $gt: 5.01 }}), null); 99 | }); 100 | }); 101 | 102 | it('$gte', function() { 103 | return co(function*() { 104 | assert.ok(yield ComparisonModel.findOne({ val: { $gte: 5 }})); 105 | assert.ok(yield ComparisonModel.findOne({ val: { $gte: 5.0 }})); 106 | assert.strictEqual(yield ComparisonModel.findOne({ val: { $gte: 5.02 }}), null); 107 | }); 108 | }); 109 | 110 | it('$lt', function() { 111 | return co(function*() { 112 | assert.ok(yield ComparisonModel.findOne({ val: { $lt: 6 }})); 113 | assert.ok(yield ComparisonModel.findOne({ val: { $lt: 5.02 }})); 114 | assert.strictEqual(yield ComparisonModel.findOne({ val: { $lt: 5.01 }}), null); 115 | }); 116 | }); 117 | 118 | it('$lte', function() { 119 | return co(function*() { 120 | assert.ok(yield ComparisonModel.findOne({ val: { $lte: 6 }})); 121 | assert.ok(yield ComparisonModel.findOne({ val: { $lte: 5.01 }})); 122 | assert.strictEqual(yield ComparisonModel.findOne({ val: { $lte: 5.0 }}), null); 123 | }); 124 | }); 125 | }); 126 | }); 127 | --------------------------------------------------------------------------------