Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit 4602a64b0c6c044e99e83537631d6f8e5e0a24b5 0 parents
Olivier Lalonde authored
Showing with 240 additions and 0 deletions.
  1. +15 −0 .gitignore
  2. +39 −0 README.md
  3. +145 −0 index.js
  4. +22 −0 package.json
  5. +19 −0 test/index.js
15 .gitignore
@@ -0,0 +1,15 @@
+lib-cov
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+
+node_modules
+npm-debug.log
39 README.md
@@ -0,0 +1,39 @@
+# Install
+
+ npm install jsonselect;
+
+# Usage
+
+## select(obj, options)
+
+Options:
+
+ - `only`: string|array - List of whitespace delimited paths (returned object will only contain those paths)
+ - `except`: string|array - List of whitespace delimited paths (returned object will not contain those paths)
+
+Options can also be a string. See examples.
+
+Paths can be (almost) any [JSONPath](http://goessner.net/articles/JsonPath/).
+
+# Examples
+
+```javascript
+var select = require('jsonselect');
+
+var a = select(obj, 'username email name.first friends -friends.password');
+// equivalent to
+var a2 = select(obj, { only: 'username email name.first friends', except: 'friends.password'} );
+
+var b = select(obj, '-password'); // everything except password
+// equivalent to
+var b2 = select(obj, { except: '-password' });
+```
+
+# Using with Mongoose
+
+Coming soon!
+
+# TODO
+
+- better parser for normalized paths
+- properly delete array element using splice/concat
145 index.js
@@ -0,0 +1,145 @@
+/*
+ * @todo
+ * - memoize string parsing
+ */
+var jsonpath = require('JSONPath')
+ _ = require('underscore');
+
+
+// auto prepend annoying $. to first argument
+evalpath = function(obj, path, getpath) {
+ if (getpath) {
+ var paths = jsonpath.eval(obj, '$.' + path, { resultType: 'PATH' });
+ paths.forEach(function (path, index) {
+ paths[index] = path.substr(1); // strip leading $
+ });
+ return paths;
+ }
+
+ return jsonpath.eval(obj, '$.' + path);
+};
+
+/**
+ * A path is a [JSONPath](http://goessner.net/articles/JsonPath/)
+ *
+ * Options:
+ * - `only`: string|array - List of whitespace delimited paths (returned object will only contain those paths)
+ * - `except`: string|array - List of whitespace delimited paths (returned object will not contain those paths)
+ *
+ * @param {Object} object to filter
+ * @param {Object|String} options Options hash or string of paths prefixed by + or -.
+ */
+module.exports = function(obj, options) {
+ if (typeof options === 'string') {
+ options = parseOptions(options);
+ }
+
+ if (!(typeof options === 'object'))
+ throw new Error('Second argument must be an object or a string.');
+
+ var only = options.only || [];
+ var except = options.except || [];
+
+ if (typeof only === 'string') only = only.split(/ +/g);
+ if (typeof except === 'string') except = except.split(/ +/g);
+
+ // if only is not set, start with a copy of the original object
+ var ret = (only.length > 0) ? {} : _.extend({}, obj);
+
+ only.forEach(function(path) {
+ var normalizedPaths = evalpath(obj, path, true);
+ var values = evalpath(obj, path);
+ normalizedPaths.forEach(function (normalizedPath, index) {
+ setPath(ret, normalizedPath, values[index]);
+ });
+ });
+ except.forEach(function(path) {
+ var normalizedPaths = evalpath(obj, path, true);
+ //console.log(normalizedPaths);
+ normalizedPaths.forEach(function (normalizedPath, index) {
+ unsetPath(ret, normalizedPath);
+ });
+ });
+ return ret;
+}
+
+function setPath (obj, path, value) {
+ return modifyPath('set', obj, path, value);
+}
+function unsetPath (obj, path) {
+ return modifyPath('unset', obj, path);
+}
+function modifyPath(type, obj, path, value) {
+ //remove leading [ and trailing ]
+ path = path.substr(1, path.length - 2);
+ // @todo: tokenize better than this: might bug if we have ['a'][0]['b][b']['et\'c.']
+ path = path.split('][');
+
+ var chain = [];
+ path.forEach(function(property) {
+ // if string, remove trailing/leading '
+ if (property.indexOf('\'') !== -1) {
+ property = property.substr(1, property.length-2);
+ }
+ else {
+ property = parseInt(property);
+ }
+ chain.push(property);
+ });
+
+ var root = obj;
+ while (chain.length > 0) {
+ property = chain.shift();
+ if (chain.length === 0) {
+ if (type === 'unset') {
+ delete root[property];
+ }
+ else
+ root[property] = value;
+ break;
+ }
+ if (type === 'set' && root[property] === undefined) {
+ root[property] = (typeof property === 'number') ? [] : {};
+ }
+ root = root[property];
+ }
+ return obj;
+ console.log('done');
+ //eval('property = oldObj' + path);
+
+
+}
+
+// Recursive
+function traverse (obj, cb, parent, key) {
+ if (obj instanceof Array) {
+ obj.forEach(function(child, index) {
+ traverse(child, cb, obj, index);
+ });
+ }
+ else if (typeof obj == 'object') {
+ for(var key in obj) {
+ traverse(obj[key], cb, obj, key);
+ }
+ }
+ cb(obj, parent, key);
+}
+
+
+function parseOptions (str) {
+ var only = [], except = [];
+
+ var regex = {
+ only: /\+[^ ]+/g
+ , except: /\-[^ ]+/g
+ }
+
+ str.replace(regex.only, function(path) {
+ only.push(path.substr(1)); // strip + or -
+ });
+ str.replace(regex.except, function(path) {
+ except.push(path.substr(1)); // strip + or -
+ });
+
+ return {only: only, except: except};
+}
22 package.json
@@ -0,0 +1,22 @@
+{
+ "name": "jsonselect",
+ "version": "0.0.1",
+ "description": "Filter out JSON field using Mongoose's select syntax.",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": "",
+ "keywords": [
+ "mongoose",
+ "json",
+ "filter",
+ "utility"
+ ],
+ "author": "Olivier Lalonde <olalonde@gmail.com>",
+ "license": "BSD",
+ "dependencies": {
+ "JSONPath": "",
+ "underscore": ""
+ }
+}
19 test/index.js
@@ -0,0 +1,19 @@
+var util = require('util');
+var filter = require('../');
+
+var user = {
+ username: 'Oli'
+ , password: 'secret password'
+ , friends: [
+ { username: 'bob', password: 'bob pass' }
+ , { username: 'joe', password: 'joe pass' }
+ , { username: 'jeff', password: 'jeff pass' }
+ ]
+ , name: {
+ first: 'Olivier'
+ , last: 'Lalonde'
+ }
+}
+
+console.dir(filter(user, { only: 'friends username name.first', except: '.password' }));
+console.log(filter(user, 'username friends -friends[:2]'));
Please sign in to comment.
Something went wrong with that request. Please try again.