UNPKG

4.17 kBJavaScriptView Raw
1'use strict';
2
3const { resolve } = require('./utils');
4
5const isOperator = (str) => str.startsWith('$');
6
7function equals (a, b) {
8 if (typeof a === 'string' && b instanceof RegExp) {
9 return b.test(a);
10 } else if (Array.isArray(a) && Array.isArray(b)) {
11 if (a.length !== b.length) {
12 return false;
13 }
14 for (let i = 0; i < a.length; i++) {
15 if (!equals(a[i], b[i])) {
16 return false;
17 }
18 }
19 return true;
20 }
21
22 return a === b;
23}
24
25function query (value, filter = {}) {
26 if (typeof filter === 'function') {
27 return Boolean(filter(value));
28 }
29
30 let result = 1;
31
32 for (const key in filter) {
33 if (isOperator(key)) {
34 if (key === '$eq') {
35 result &= value === filter[key];
36 } else if (key === '$ne') {
37 result &= value !== filter[key];
38 } else if (key === '$like') {
39 result &= equals(value, filter[key]);
40 } else if (key === '$regexp' || key === '$regex') {
41 if (filter[key] instanceof RegExp) {
42 result &= filter[key].test(value);
43 } else {
44 result &= false;
45 }
46 } else if (key === '$gt') {
47 result &= value > filter[key];
48 } else if (key === '$gte') {
49 result &= value >= filter[key];
50 } else if (key === '$lt') {
51 result &= value < filter[key];
52 } else if (key === '$lte') {
53 result &= value <= filter[key];
54 } else if (key === '$in') {
55 result &= filter[key].includes(value);
56 } else if (key === '$nin') {
57 result &= ! filter[key].includes(value);
58 } else if (key === '$and') {
59 for (const and of filter[key]) {
60 result &= query(value, and);
61 if (result === 0) {
62 break;
63 }
64 }
65 } else if (key === '$or') {
66 let intermediate = 0;
67 for (const or of filter[key]) {
68 intermediate |= query(value, or);
69 if (intermediate) {
70 break;
71 }
72 }
73 result &= intermediate;
74 } else if (key === '$nor') {
75 let intermediate = 0;
76 for (const nor of filter[key]) {
77 intermediate |= query(value, nor);
78 }
79 result &= ! intermediate;
80 } else if (key === '$not') {
81 result &= !query(value, filter[key]);
82 } else if (key === '$type') {
83 result &= typeof value === filter[key];
84 } else if (key === '$exists') {
85 result &= value !== undefined;
86 } else if (key === '$size') {
87 if (Array.isArray(value) && value.length === filter[key]) {
88 result &= true;
89 } else if (typeof value === 'object' &&
90 Object.keys(value).length === filter[key]) {
91 result &= true;
92 } else {
93 result &= false;
94 }
95 } else if (key === '$mod') {
96 const divisor = filter[key][0] || filter[key];
97 const remainder = filter[key][1] || 0;
98 result &= value % divisor === remainder;
99 } else if (key === '$where') {
100 if (typeof filter[key] === 'function') {
101 result &= Boolean(filter[key](value));
102 } else {
103 result &= false;
104 }
105 } else if (key === '$elemMatch') {
106 if (Array.isArray(value)) {
107 let found = false;
108 for (const item of value) {
109 if (query(item, filter[key])) {
110 found = true;
111 break;
112 }
113 }
114 result &= found;
115 } else {
116 result &= false;
117 }
118 } else if (key === '$all') {
119 if (Array.isArray(value)) {
120 for (const item of value) {
121 result &= query(item, filter[key]);
122 if (result === 0) {
123 return false;
124 }
125 }
126 } else {
127 result &= false;
128 }
129 }
130 } else if (Array.isArray(filter[key])) {
131 result &= equals(resolve(value, key), filter[key]);
132 } else if (typeof filter[key] === 'object') {
133 result &= query(resolve(value, key), filter[key]);
134 } else {
135 result &= equals(resolve(value, key), filter[key]);
136 }
137
138 if (result === 0) {
139 return false;
140 }
141 }
142
143 return Boolean(result);
144}
145
146module.exports = query;