Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

initial commit

  • Loading branch information...
commit 4602a64b0c6c044e99e83537631d6f8e5e0a24b5 0 parents
Olivier Lalonde authored October 26, 2012
15  .gitignore
... ...
@@ -0,0 +1,15 @@
  1
+lib-cov
  2
+*.seed
  3
+*.log
  4
+*.csv
  5
+*.dat
  6
+*.out
  7
+*.pid
  8
+*.gz
  9
+
  10
+pids
  11
+logs
  12
+results
  13
+
  14
+node_modules
  15
+npm-debug.log
39  README.md
Source Rendered
... ...
@@ -0,0 +1,39 @@
  1
+# Install 
  2
+
  3
+    npm install jsonselect;
  4
+
  5
+# Usage
  6
+
  7
+## select(obj, options)
  8
+
  9
+Options:
  10
+
  11
+ - `only`: string|array - List of whitespace delimited paths (returned object will only contain those paths)
  12
+ - `except`: string|array - List of whitespace delimited paths (returned object will not contain those paths)
  13
+
  14
+Options can also be a string. See examples.
  15
+
  16
+Paths can be (almost) any [JSONPath](http://goessner.net/articles/JsonPath/).
  17
+
  18
+# Examples
  19
+
  20
+```javascript
  21
+var select = require('jsonselect');
  22
+
  23
+var a = select(obj, 'username email name.first friends -friends.password');
  24
+// equivalent to
  25
+var a2 = select(obj, { only: 'username email name.first friends', except: 'friends.password'} );
  26
+
  27
+var b = select(obj, '-password'); // everything except password
  28
+// equivalent to
  29
+var b2 = select(obj, { except: '-password' });
  30
+```
  31
+
  32
+# Using with Mongoose
  33
+
  34
+Coming soon!
  35
+
  36
+# TODO
  37
+
  38
+- better parser for normalized paths
  39
+- properly delete array element using splice/concat
145  index.js
... ...
@@ -0,0 +1,145 @@
  1
+/*
  2
+ * @todo
  3
+ *  - memoize string parsing
  4
+ */
  5
+var jsonpath = require('JSONPath')
  6
+  _ = require('underscore');
  7
+
  8
+
  9
+// auto prepend annoying $. to first argument
  10
+evalpath = function(obj, path, getpath) {
  11
+  if (getpath) {
  12
+    var paths = jsonpath.eval(obj, '$.' + path, { resultType: 'PATH' });
  13
+    paths.forEach(function (path, index) {
  14
+      paths[index] = path.substr(1); // strip leading $
  15
+    });
  16
+    return paths;
  17
+  }
  18
+
  19
+  return jsonpath.eval(obj, '$.' + path);
  20
+};
  21
+
  22
+/**
  23
+ * A path is a [JSONPath](http://goessner.net/articles/JsonPath/)
  24
+ *
  25
+ * Options:
  26
+ * - `only`: string|array - List of whitespace delimited paths (returned object will only contain those paths)
  27
+ * - `except`: string|array - List of whitespace delimited paths (returned object will not contain those paths)
  28
+ *
  29
+ * @param {Object} object to filter
  30
+ * @param {Object|String} options Options hash or string of paths prefixed by + or -.
  31
+ */
  32
+module.exports = function(obj, options) {
  33
+  if (typeof options === 'string') {
  34
+    options = parseOptions(options);
  35
+  }
  36
+
  37
+  if (!(typeof options === 'object'))
  38
+    throw new Error('Second argument must be an object or a string.');
  39
+
  40
+  var only = options.only || [];
  41
+  var except = options.except || [];
  42
+
  43
+  if (typeof only === 'string') only = only.split(/ +/g);
  44
+  if (typeof except === 'string') except = except.split(/ +/g);
  45
+
  46
+  // if only is not set, start with a copy of the original object
  47
+  var ret = (only.length > 0) ? {} : _.extend({}, obj);
  48
+
  49
+  only.forEach(function(path) {
  50
+    var normalizedPaths = evalpath(obj, path, true);
  51
+    var values = evalpath(obj, path);
  52
+    normalizedPaths.forEach(function (normalizedPath, index) {
  53
+      setPath(ret, normalizedPath, values[index]);
  54
+    });
  55
+  });
  56
+  except.forEach(function(path) {
  57
+    var normalizedPaths = evalpath(obj, path, true);
  58
+    //console.log(normalizedPaths);
  59
+    normalizedPaths.forEach(function (normalizedPath, index) {
  60
+      unsetPath(ret, normalizedPath);
  61
+    });
  62
+  });
  63
+  return ret;
  64
+}
  65
+
  66
+function setPath (obj, path, value) {
  67
+  return modifyPath('set', obj, path, value);
  68
+}
  69
+function unsetPath (obj, path) {
  70
+  return modifyPath('unset', obj, path);
  71
+}
  72
+function modifyPath(type, obj, path, value) {
  73
+  //remove leading [ and trailing ]
  74
+  path = path.substr(1, path.length - 2);
  75
+  // @todo: tokenize better than this: might bug if we have ['a'][0]['b][b']['et\'c.']
  76
+  path = path.split('][');
  77
+
  78
+  var chain = [];
  79
+  path.forEach(function(property) {
  80
+    // if string, remove trailing/leading '
  81
+    if (property.indexOf('\'') !== -1) {
  82
+      property = property.substr(1, property.length-2);
  83
+    }
  84
+    else {
  85
+      property = parseInt(property);
  86
+    }
  87
+    chain.push(property);
  88
+  });
  89
+
  90
+  var root = obj;
  91
+  while (chain.length > 0) {
  92
+    property = chain.shift();
  93
+    if (chain.length === 0) {
  94
+      if (type === 'unset') {
  95
+        delete root[property];
  96
+      }
  97
+      else
  98
+        root[property] = value;
  99
+      break;
  100
+    }
  101
+    if (type === 'set' && root[property] === undefined) {
  102
+      root[property] = (typeof property === 'number') ? [] : {};
  103
+    }
  104
+    root = root[property];
  105
+  }
  106
+  return obj;
  107
+  console.log('done');
  108
+  //eval('property = oldObj' + path);
  109
+  
  110
+  
  111
+}
  112
+
  113
+// Recursive
  114
+function traverse (obj, cb, parent, key) {
  115
+  if (obj instanceof Array) {
  116
+    obj.forEach(function(child, index) {
  117
+      traverse(child, cb, obj, index);
  118
+    });
  119
+  }
  120
+  else if (typeof obj == 'object') {
  121
+    for(var key in obj) {
  122
+      traverse(obj[key], cb, obj, key);
  123
+    }
  124
+  }
  125
+  cb(obj, parent, key);
  126
+}
  127
+
  128
+
  129
+function parseOptions (str) {
  130
+  var only = [], except = [];
  131
+
  132
+  var regex = {
  133
+    only: /\+[^ ]+/g
  134
+    , except: /\-[^ ]+/g
  135
+  }
  136
+
  137
+  str.replace(regex.only, function(path) {
  138
+    only.push(path.substr(1)); // strip + or -
  139
+  });
  140
+  str.replace(regex.except, function(path) {
  141
+    except.push(path.substr(1)); // strip + or -
  142
+  });
  143
+
  144
+  return {only: only, except: except};
  145
+}
22  package.json
... ...
@@ -0,0 +1,22 @@
  1
+{
  2
+  "name": "jsonselect",
  3
+  "version": "0.0.1",
  4
+  "description": "Filter out JSON field using Mongoose's select syntax.",
  5
+  "main": "index.js",
  6
+  "scripts": {
  7
+    "test": "echo \"Error: no test specified\" && exit 1"
  8
+  },
  9
+  "repository": "",
  10
+  "keywords": [
  11
+    "mongoose",
  12
+    "json",
  13
+    "filter",
  14
+    "utility"
  15
+  ],
  16
+  "author": "Olivier Lalonde <olalonde@gmail.com>",
  17
+  "license": "BSD",
  18
+  "dependencies": {
  19
+    "JSONPath": "",
  20
+    "underscore": ""
  21
+  }
  22
+}
19  test/index.js
... ...
@@ -0,0 +1,19 @@
  1
+var util = require('util');
  2
+var filter = require('../');
  3
+
  4
+var user = {
  5
+  username: 'Oli'
  6
+  , password: 'secret password'
  7
+  , friends: [
  8
+    { username: 'bob', password: 'bob pass' }
  9
+    , { username: 'joe', password: 'joe pass' }
  10
+    , { username: 'jeff', password: 'jeff pass' }
  11
+  ]
  12
+  , name: {
  13
+    first: 'Olivier'
  14
+    , last: 'Lalonde'
  15
+  }
  16
+}
  17
+
  18
+console.dir(filter(user, { only: 'friends username name.first', except: '.password' }));
  19
+console.log(filter(user, 'username friends -friends[:2]'));

0 notes on commit 4602a64

Please sign in to comment.
Something went wrong with that request. Please try again.