index.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. 'use strict';
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  6. exports.default = sift;
  7. exports.indexOf = indexOf;
  8. exports.compare = compare;
  9. /*
  10. *
  11. * Copryright 2018, Craig Condon
  12. * Licensed under MIT
  13. *
  14. * Filter JavaScript objects with mongodb queries
  15. */
  16. /**
  17. */
  18. function isFunction(value) {
  19. return typeof value === 'function';
  20. }
  21. /**
  22. */
  23. function isArray(value) {
  24. return Object.prototype.toString.call(value) === '[object Array]';
  25. }
  26. /**
  27. */
  28. function comparable(value) {
  29. if (value instanceof Date) {
  30. return value.getTime();
  31. } else if (isArray(value)) {
  32. return value.map(comparable);
  33. } else if (value && typeof value.toJSON === 'function') {
  34. return value.toJSON();
  35. } else {
  36. return value;
  37. }
  38. }
  39. function get(obj, key) {
  40. return isFunction(obj.get) ? obj.get(key) : obj[key];
  41. }
  42. /**
  43. */
  44. function or(validator) {
  45. return function (a, b) {
  46. if (!isArray(b) || !b.length) {
  47. return validator(a, b);
  48. }
  49. for (var i = 0, n = b.length; i < n; i++) {
  50. if (validator(a, get(b, i))) return true;
  51. }
  52. return false;
  53. };
  54. }
  55. /**
  56. */
  57. function and(validator) {
  58. return function (a, b) {
  59. if (!isArray(b) || !b.length) {
  60. return validator(a, b);
  61. }
  62. for (var i = 0, n = b.length; i < n; i++) {
  63. if (!validator(a, get(b, i))) return false;
  64. }
  65. return true;
  66. };
  67. }
  68. function validate(validator, b, k, o) {
  69. return validator.v(validator.a, b, k, o);
  70. }
  71. var OPERATORS = {
  72. /**
  73. */
  74. $eq: or(function (a, b) {
  75. return a(b);
  76. }),
  77. /**
  78. */
  79. $ne: and(function (a, b) {
  80. return !a(b);
  81. }),
  82. /**
  83. */
  84. $gt: or(function (a, b) {
  85. return compare(comparable(b), a) > 0;
  86. }),
  87. /**
  88. */
  89. $gte: or(function (a, b) {
  90. return compare(comparable(b), a) >= 0;
  91. }),
  92. /**
  93. */
  94. $lt: or(function (a, b) {
  95. return compare(comparable(b), a) < 0;
  96. }),
  97. /**
  98. */
  99. $lte: or(function (a, b) {
  100. return compare(comparable(b), a) <= 0;
  101. }),
  102. /**
  103. */
  104. $mod: or(function (a, b) {
  105. return b % a[0] == a[1];
  106. }),
  107. /**
  108. */
  109. $in: function $in(a, b) {
  110. if (b instanceof Array) {
  111. for (var i = b.length; i--;) {
  112. if (~a.indexOf(comparable(get(b, i)))) {
  113. return true;
  114. }
  115. }
  116. } else {
  117. var comparableB = comparable(b);
  118. if (comparableB === b && (typeof b === 'undefined' ? 'undefined' : _typeof(b)) === 'object') {
  119. for (var i = a.length; i--;) {
  120. if (String(a[i]) === String(b) && String(b) !== '[object Object]') {
  121. return true;
  122. }
  123. }
  124. }
  125. /*
  126. Handles documents that are undefined, whilst also
  127. having a 'null' element in the parameters to $in.
  128. */
  129. if (typeof comparableB == 'undefined') {
  130. for (var i = a.length; i--;) {
  131. if (a[i] == null) {
  132. return true;
  133. }
  134. }
  135. }
  136. /*
  137. Handles the case of {'field': {$in: [/regexp1/, /regexp2/, ...]}}
  138. */
  139. for (var i = a.length; i--;) {
  140. var validator = createRootValidator(get(a, i), undefined);
  141. var result = validate(validator, b, i, a);
  142. if (result && String(result) !== '[object Object]' && String(b) !== '[object Object]') {
  143. return true;
  144. }
  145. }
  146. return !!~a.indexOf(comparableB);
  147. }
  148. return false;
  149. },
  150. /**
  151. */
  152. $nin: function $nin(a, b, k, o) {
  153. return !OPERATORS.$in(a, b, k, o);
  154. },
  155. /**
  156. */
  157. $not: function $not(a, b, k, o) {
  158. return !validate(a, b, k, o);
  159. },
  160. /**
  161. */
  162. $type: function $type(a, b) {
  163. return b != void 0 ? b instanceof a || b.constructor == a : false;
  164. },
  165. /**
  166. */
  167. $all: function $all(a, b, k, o) {
  168. return OPERATORS.$and(a, b, k, o);
  169. },
  170. /**
  171. */
  172. $size: function $size(a, b) {
  173. return b ? a === b.length : false;
  174. },
  175. /**
  176. */
  177. $or: function $or(a, b, k, o) {
  178. for (var i = 0, n = a.length; i < n; i++) {
  179. if (validate(get(a, i), b, k, o)) return true;
  180. }return false;
  181. },
  182. /**
  183. */
  184. $nor: function $nor(a, b, k, o) {
  185. return !OPERATORS.$or(a, b, k, o);
  186. },
  187. /**
  188. */
  189. $and: function $and(a, b, k, o) {
  190. for (var i = 0, n = a.length; i < n; i++) {
  191. if (!validate(get(a, i), b, k, o)) {
  192. return false;
  193. }
  194. }
  195. return true;
  196. },
  197. /**
  198. */
  199. $regex: or(function (a, b) {
  200. return typeof b === 'string' && a.test(b);
  201. }),
  202. /**
  203. */
  204. $where: function $where(a, b, k, o) {
  205. return a.call(b, b, k, o);
  206. },
  207. /**
  208. */
  209. $elemMatch: function $elemMatch(a, b, k, o) {
  210. if (isArray(b)) {
  211. return !!~search(b, a);
  212. }
  213. return validate(a, b, k, o);
  214. },
  215. /**
  216. */
  217. $exists: function $exists(a, b, k, o) {
  218. return o.hasOwnProperty(k) === a;
  219. }
  220. };
  221. /**
  222. */
  223. var prepare = {
  224. /**
  225. */
  226. $eq: function $eq(a) {
  227. if (a instanceof RegExp) {
  228. return function (b) {
  229. return typeof b === 'string' && a.test(b);
  230. };
  231. } else if (a instanceof Function) {
  232. return a;
  233. } else if (isArray(a) && !a.length) {
  234. // Special case of a == []
  235. return function (b) {
  236. return isArray(b) && !b.length;
  237. };
  238. } else if (a === null) {
  239. return function (b) {
  240. //will match both null and undefined
  241. return b == null;
  242. };
  243. }
  244. return function (b) {
  245. return compare(comparable(b), comparable(a)) === 0;
  246. };
  247. },
  248. /**
  249. */
  250. $ne: function $ne(a) {
  251. return prepare.$eq(a);
  252. },
  253. /**
  254. */
  255. $and: function $and(a) {
  256. return a.map(parse);
  257. },
  258. /**
  259. */
  260. $all: function $all(a) {
  261. return prepare.$and(a);
  262. },
  263. /**
  264. */
  265. $or: function $or(a) {
  266. return a.map(parse);
  267. },
  268. /**
  269. */
  270. $nor: function $nor(a) {
  271. return a.map(parse);
  272. },
  273. /**
  274. */
  275. $not: function $not(a) {
  276. return parse(a);
  277. },
  278. /**
  279. */
  280. $regex: function $regex(a, query) {
  281. return new RegExp(a, query.$options);
  282. },
  283. /**
  284. */
  285. $where: function $where(a) {
  286. return typeof a === 'string' ? new Function('obj', 'return ' + a) : a;
  287. },
  288. /**
  289. */
  290. $elemMatch: function $elemMatch(a) {
  291. return parse(a);
  292. },
  293. /**
  294. */
  295. $exists: function $exists(a) {
  296. return !!a;
  297. }
  298. };
  299. /**
  300. */
  301. function search(array, validator) {
  302. for (var i = 0; i < array.length; i++) {
  303. var result = get(array, i);
  304. if (validate(validator, get(array, i))) {
  305. return i;
  306. }
  307. }
  308. return -1;
  309. }
  310. /**
  311. */
  312. function createValidator(a, validate) {
  313. return { a: a, v: validate };
  314. }
  315. /**
  316. */
  317. function nestedValidator(a, b) {
  318. var values = [];
  319. findValues(b, a.k, 0, b, values);
  320. if (values.length === 1) {
  321. var first = values[0];
  322. return validate(a.nv, first[0], first[1], first[2]);
  323. }
  324. // If the query contains $ne, need to test all elements ANDed together
  325. var inclusive = a && a.q && typeof a.q.$ne !== 'undefined';
  326. var allValid = inclusive;
  327. for (var i = 0; i < values.length; i++) {
  328. var result = values[i];
  329. var isValid = validate(a.nv, result[0], result[1], result[2]);
  330. if (inclusive) {
  331. allValid &= isValid;
  332. } else {
  333. allValid |= isValid;
  334. }
  335. }
  336. return allValid;
  337. }
  338. /**
  339. */
  340. function findValues(current, keypath, index, object, values) {
  341. if (index === keypath.length || current == void 0) {
  342. values.push([current, keypath[index - 1], object]);
  343. return;
  344. }
  345. var k = get(keypath, index);
  346. // ensure that if current is an array, that the current key
  347. // is NOT an array index. This sort of thing needs to work:
  348. // sift({'foo.0':42}, [{foo: [42]}]);
  349. if (isArray(current) && isNaN(Number(k))) {
  350. for (var i = 0, n = current.length; i < n; i++) {
  351. findValues(get(current, i), keypath, index, current, values);
  352. }
  353. } else {
  354. findValues(get(current, k), keypath, index + 1, current, values);
  355. }
  356. }
  357. /**
  358. */
  359. function createNestedValidator(keypath, a, q) {
  360. return { a: { k: keypath, nv: a, q: q }, v: nestedValidator };
  361. }
  362. /**
  363. * flatten the query
  364. */
  365. function isVanillaObject(value) {
  366. return value && value.constructor === Object;
  367. }
  368. function parse(query) {
  369. query = comparable(query);
  370. if (!query || !isVanillaObject(query)) {
  371. // cross browser support
  372. query = { $eq: query };
  373. }
  374. var validators = [];
  375. for (var key in query) {
  376. var a = query[key];
  377. if (key === '$options') {
  378. continue;
  379. }
  380. if (OPERATORS[key]) {
  381. if (prepare[key]) a = prepare[key](a, query);
  382. validators.push(createValidator(comparable(a), OPERATORS[key]));
  383. } else {
  384. if (key.charCodeAt(0) === 36) {
  385. throw new Error('Unknown operation ' + key);
  386. }
  387. validators.push(createNestedValidator(key.split('.'), parse(a), a));
  388. }
  389. }
  390. return validators.length === 1 ? validators[0] : createValidator(validators, OPERATORS.$and);
  391. }
  392. /**
  393. */
  394. function createRootValidator(query, getter) {
  395. var validator = parse(query);
  396. if (getter) {
  397. validator = {
  398. a: validator,
  399. v: function v(a, b, k, o) {
  400. return validate(a, getter(b), k, o);
  401. }
  402. };
  403. }
  404. return validator;
  405. }
  406. /**
  407. */
  408. function sift(query, array, getter) {
  409. if (isFunction(array)) {
  410. getter = array;
  411. array = void 0;
  412. }
  413. var validator = createRootValidator(query, getter);
  414. function filter(b, k, o) {
  415. return validate(validator, b, k, o);
  416. }
  417. if (array) {
  418. return array.filter(filter);
  419. }
  420. return filter;
  421. }
  422. /**
  423. */
  424. function indexOf(query, array, getter) {
  425. return search(array, createRootValidator(query, getter));
  426. };
  427. /**
  428. */
  429. function compare(a, b) {
  430. if (a === b) return 0;
  431. if ((typeof a === 'undefined' ? 'undefined' : _typeof(a)) === (typeof b === 'undefined' ? 'undefined' : _typeof(b))) {
  432. if (a > b) {
  433. return 1;
  434. }
  435. if (a < b) {
  436. return -1;
  437. }
  438. }
  439. };