I ran into this on my recent project where we basically passed in a JSON Object as the Selector to Find/Upsert operations. Of course, MongoDB likes Dot-Notation. Typically flattening is easy, except for those pesky $ specials that Mongo uses to make it so powerful in these scenarios.
So, I wrote a quick routine that takes a JSON object and flattens it taking into account the special $ keys.
Sorry if this is the wrong way to put this up for all, but I don't really know the right place to put it in the code to suggest a patch. It isn't really a core thing, and is more of a utils type thing.
Anyways, in case it could be of use to others here it is:
console.log(testOn);
console.log('-=-=-=-=-=-');
var result = unrollJSONSelector(testOn);
console.log(result);
console.log('-=-=-=-=-=-');
console.log(result.$addToSet.a.$each);
I ran into this on my recent project where we basically passed in a JSON Object as the Selector to Find/Upsert operations. Of course, MongoDB likes Dot-Notation. Typically flattening is easy, except for those pesky $ specials that Mongo uses to make it so powerful in these scenarios.
So, I wrote a quick routine that takes a JSON object and flattens it taking into account the special $ keys.
Sorry if this is the wrong way to put this up for all, but I don't really know the right place to put it in the code to suggest a patch. It isn't really a core thing, and is more of a utils type thing.
Anyways, in case it could be of use to others here it is:
var testOn = { a: 10, b: 'hello', c: {$gt: 10}, d: { e: 'e value', f: 'f value' }, ary: [1, 2, 3, {a: 'b', b: 'c'}], $where: "this.a == this.b", $addToSet: {a: 1}, $pushAll: {a: [1, 2]}, $addToSet: {a: {$each: [1, 2]}}, $or: [{a: 1}, {b: 1}] };
var unrollJSONSelector = function(selector){ var isPlainObject = function(obj) { if (!obj || toString.call(obj) !== '[object Object]' || obj.nodeType || obj.setInterval) return false; var has_own_constructor = hasOwnProperty.call(obj, 'constructor'); var has_is_property_of_method = hasOwnProperty.call(obj.constructor.prototype, 'isPrototypeOf'); if (obj.constructor && !has_own_constructor && !has_is_property_of_method) return false; var last_key; for (key in obj) last_key = key; return typeof last_key === 'undefined' || hasOwnProperty.call(obj, last_key); }; var _process = function(node, result, path){ var name, value, isObject, isArray, isSpecial, route, index, container; if(path!=='') route = path+'.'; else route = ''; for(name in node){ value = node[name]; isObject = isPlainObject(value); isArray = Array.isArray(value); isSpecial = name.substr(0, 1)=='$'; if(isSpecial){ if(isObject){ container = result[name] = {}; for(index in value){ isObject = isPlainObject(value[index]); isArray = Array.isArray(value[index]); if(isObject) _process(value[index], container[index]={}, ''); else if(isArray) _process(value[index], container[index]=[], ''); else container[index] = value[index]; } }else if(isArray){ container = result[name] = []; for(index in value){ isObject = isPlainObject(value[index]); isArray = Array.isArray(value[index]); if(isObject||isArray) container.push(_process(value[index], {}, '')); else container.push(value[index]); } }else{ if(path){ container = result[path] = {}; container[name] = value; }else result[name] = value; } }else if(isArray){ container = result[route+name] = []; for(index in value){ isObject = isPlainObject(value[index]); isArray = Array.isArray(value[index]); if(isObject||isArray) container.push(_process(value[index], {}, '')); else container.push(value[index]); } }else if(isObject){ _process(value, result, route+name); }else{ result[route+name] = value; } } return result; };
return _process(selector, {}, ''); };
console.log(testOn); console.log('-=-=-=-=-=-'); var result = unrollJSONSelector(testOn); console.log(result); console.log('-=-=-=-=-=-'); console.log(result.$addToSet.a.$each);