1 | 'use strict';
|
2 |
|
3 | const { resolve } = require('./utils');
|
4 |
|
5 | const isOperator = (str) => { return str.startsWith('$'); };
|
6 |
|
7 | function 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 |
|
25 | function 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 |
|
146 | module.exports = query;
|