SortOp.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. var Op = require("../Op"),
  2. traverse = require("traverse");
  3. //TODO: ...write this...
  4. var SortOp = module.exports = (function(){
  5. // CONSTRUCTOR
  6. var base = Op, proto, klass = function SortOp(opts){
  7. // Parse sorts from options object
  8. if(typeof(opts) !== "object") throw new Error("the $sort key specification must be an object");
  9. this.sorts = [];
  10. for(var p in opts){
  11. if(p[0] === "$") throw new Error("$sort: FieldPath field names may not start with '$'.; code 16410");
  12. if(p === "") throw new Error("$sort: FieldPath field names may not be empty strings.; code 15998");
  13. this.sorts.push({path:p.split("."), direction:opts[p]});
  14. }
  15. console.log("SORTS FOR $sort OP:", this.sorts);
  16. this.objs = [];
  17. base.call(this, opts);
  18. };
  19. proto = klass.prototype = Object.create(base.prototype, {constructor:{value:klass}});
  20. // PRIVATE STUFF
  21. // Helpers for sorting
  22. var types = ["undefined", "null", "NaN", "number", "string", "object", "boolean", "Date"];
  23. function getTypeOf(o){
  24. if(o === undefined) return "undefined";
  25. if(o === null) return "null";
  26. if(isNaN(o)) return "NaN";
  27. if(o.constructor === Date) return "Date";
  28. return typeof(o);
  29. }
  30. // PROTOTYPE MEMBERS
  31. proto.write = function writeDeferredForSorting(obj){
  32. console.log("$sort deferring:", obj);
  33. this.objs.push(obj);
  34. };
  35. proto.end = function endSort(obj){
  36. console.log("$sort end event");
  37. if(this.objs.length){
  38. console.log("OBJS TO BE SORTED:", this.objs);
  39. this.objs.sort(function(a, b){
  40. for(var i = 0, l = this.sorts.length; i < l; i++){
  41. //TODO: this probably needs to compareDeep using traverse(a).forEach(...check b...) or similar
  42. var sort = this.sorts[i],
  43. aVal = traverse(a).get(sort.path), aType = getTypeOf(aVal),
  44. bVal = traverse(b).get(sort.path), bType = getTypeOf(bVal);
  45. // null and undefined go first
  46. if(aType !== bType){
  47. return (types.indexOf(aType) - types.indexOf(bType)) * sort.direction;
  48. }else{
  49. // don't trust type cohersion
  50. if(aType == "number") bVal = parseFloat(bVal);
  51. if(isNaN(bVal)) return 1;
  52. if(aType == "string") bVal = bVal.toString();
  53. // return sort value only if it can be determined at this level
  54. if(aVal < bVal) return -1 * sort.direction;
  55. if(aVal > bVal) return 1 * sort.direction;
  56. }
  57. }
  58. return 0;
  59. });
  60. console.log("$sort has sorted");
  61. for(var i = 0, l = this.objs.length; i < l; i++)
  62. this.queue(this.objs[i]);
  63. }
  64. this.end();
  65. };
  66. return klass;
  67. })();