From cbbfb99e747cea70928d51b4a5f184dfe485e74b Mon Sep 17 00:00:00 2001 From: Eivind Fjeldstad Date: Fri, 28 Feb 2014 00:28:07 +0100 Subject: [PATCH] initial commit --- .gitignore | 1 + Makefile | 5 +++ Readme.md | 31 +++++++++++++++++++ index.js | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 22 ++++++++++++++ test.js | 36 ++++++++++++++++++++++ 6 files changed, 181 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 Readme.md create mode 100644 index.js create mode 100644 package.json create mode 100644 test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..93c83ef --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +test: + @./node_modules/.bin/mocha \ + --reporter spec + +.PHONY: test \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..1820183 --- /dev/null +++ b/Readme.md @@ -0,0 +1,31 @@ + +# mongo-query + +Get object diff as a MongoDB update query. + +Useful for reducing the size of your queries. +Uses (cloudup/mongo-eql)[https://github.com/cloudup/mongo-eql] to compare values and +and (cloudup/mongo-minify)[https://github.com/cloudup/mongo-minify] to optionally filter +out disallowed operations. + +## Installation + + $ npm install mongo-update + +## Example + +```js +var update = require('mongo-update'); +var query = update({ a: 'hello' }, { b: 'world' }); +// => { $set: { b: 'hello' }, $unset: { a: 1 }} +``` + +Or with a filter (see (cloudup/mongo-minify)[https://github.com/cloudup/mongo-minify] for more examples) +```js +var query = update({ a: 1, b: 2 }, { a: 2, b: 3 }, { a: 1 }); +// => { $set: { a: 2 }} +``` + +## License + + MIT diff --git a/index.js b/index.js new file mode 100644 index 0000000..8e2df97 --- /dev/null +++ b/index.js @@ -0,0 +1,86 @@ +var minify = require('mongo-minify'); +var is = require('component-type'); +var eql = require('mongo-eql'); + +/** + * Get object diff as a MongoDB update query + * + * @param {Object} a + * @param {Object} b + * @param {Object} [filter] + * @return {Object} + * @api public + */ + +module.exports = function (a, b, filter) { + var ret = {}; + filter = filter || {}; + diff(a, b, ret); + return minify(ret, filter); +} + +/** + * Traverse both objects and put ops on the `query` object + */ + +function diff (a, b, query, prefix) { + // find removed keys + for (var key in a) { + var path = join(key, prefix); + if (b[key] == null) unset(query, path); + } + + // find changed keys + for (var key in b) { + var path = join(key, prefix); + + // removed + if (b[key] == null) continue; + + // no change + if (eql(a[key], b[key])) continue; + + // new type + if (is(a[key]) != is(b[key])) { + set(query, path, b[key]); + continue; + } + + // object + if (is(a[key]) == 'object') { + diff(a[key], b[key], query, path); + continue; + } + + // default + set(query, path, b[key]); + } +} + +/** + * $set `field` to `val` + */ + +function set (query, field, val) { + query['$set'] = query['$set'] || {}; + query['$set'][field] = val; +} + +/** + * $unset `field` + */ + +function unset (query, field) { + query['$unset'] = query['$unset'] || {}; + query['$unset'][field] = 1; +}; + +/** + * Join `key` with `prefix` using dot-notation + */ + +function join (key, prefix) { + return prefix + ? prefix + '.' + key + : key; +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..684c034 --- /dev/null +++ b/package.json @@ -0,0 +1,22 @@ +{ + "name": "mongo-update", + "version": "0.0.1", + "description": "Get object diff as a MongoDB update query", + "main": "index.js", + "scripts": { + "test": "make test" + }, + "license": "MIT", + "dependencies": { + "component-type": "~1.0.0", + "mongo-eql": "~0.1.0", + "mongo-minify": "~0.1.1" + }, + "devDependencies": { + "mocha": "~1.17.1" + }, + "repository": { + "type": "git", + "url": "https://github.com/eivindfjeldstad/mongo-update.git" + } +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..c6e9420 --- /dev/null +++ b/test.js @@ -0,0 +1,36 @@ +var assert = require('assert'); +var diff = require('./'); + +describe('query()', function () { + it('should $set modified keys', function () { + var query = diff({ a: 1 }, { a: 2 }); + assert(query.$set.a == 2); + assert(!('$unset' in query)); + }); + + it('should $unset null-ish keys', function () { + var query = diff({ a: 1 }, { a: null }); + assert(query.$unset.a == 1); + assert(!('$set' in query)); + }); + + it('should work with nested keys', function () { + var query = diff({ a: { b: 1, c: 2 }}, { a: { b: 2 }}); + assert(query.$set['a.b'] == 2); + assert(query.$unset['a.c'] == 1); + }); + + describe('when given a filter', function () { + it('should minify the query', function () { + var query = diff({ a: 1, b: 2 }, { a: 2, b: 3 }, { a: 1 }); + assert(query.$set.a == 2); + assert(!('b' in query.$set)); + }); + + it('should work with nested keys', function () { + var query = diff({ a: { b: 1, c: 2 }}, { a: { b: 2 }}, { 'a.b': 1 }); + assert(query.$set['a.b'] == 2); + assert(!('$unset' in query)); + }); + }); +}) \ No newline at end of file