Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 9.0KB

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