sorla-db 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,289 +1,243 @@
1
- class operators {
2
- /**
3
- * Constructs the class and creates a property to store fieldname
4
- */
5
- constructor() {
6
- this._currentField = null;
7
- this._logicalOperators = ['$or', '$and', '$not', '$nor'];
8
- this._comparisonOperators = ['$lt', '$lte', '$gt', '$gte', '$eq', '$ne', '$in', '$nin'];
9
- }
10
-
11
- /**
12
- * Set current field
13
- * @param string field
14
- * @return void
15
- */
16
- set currentField(field) {
17
- this._currentField = field;
18
- }
19
-
20
- /**
21
- * Check if is any operator
22
- * @param string operator
23
- * @return boolean
24
- */
25
- isAnyOperator(operator) {
26
- return this.isLogicalOperator(operator) || this.isOperator(operator);
27
- }
28
-
29
- /**
30
- * check if is logical operator
31
- * @param string operator
32
- * @return boolean
33
- */
34
- isLogicalOperator(operator) {
35
- return this._logicalOperators.indexOf(operator) > -1;
36
- }
37
-
38
- /**
39
- * Is operators
40
- * @param string operator
41
- * @return boolean
42
- */
43
- isOperator(operator) {
44
- return this._comparisonOperators.indexOf(operator) > -1;
45
- }
46
-
47
- /**
48
- * Or operator
49
- * @param array queries
50
- * @param object doc
51
- * @return array
52
- */
53
- get $or() {
54
- return (queries, doc) => {
55
- if(!Array.isArray(queries))
56
- throw new Error('The $or operator must be an array');
57
-
58
- let result = false;
59
- queryLoop: for(const query of queries) {
60
- for(const attr of Object.keys(query)) {
61
- if(this.isLogicalOperator(attr) && this[attr](query[attr], doc))
62
- {
63
- result = true;
64
- continue queryLoop;
65
- }
66
-
67
- if(this.handleComparison(attr, query[attr], doc))
68
- {
69
- result = true;
70
- continue queryLoop;
71
- }
72
- }
73
- }
74
-
75
- return result;
76
- };
77
- }
78
-
79
- /**
80
- * And operator
81
- * @param array queries
82
- * @param object doc
83
- * @return array
84
- */
85
- get $and() {
86
- return (queries, doc) => {
87
- if(!Array.isArray(queries))
88
- throw new Error('The $and operator must be an array');
89
-
90
- let result = true;
91
- queryLoop: for(const query of queries) {
92
- for(const attr of Object.keys(query)) {
93
- if(this.isLogicalOperator(attr) && !this[attr](query[attr], doc))
94
- {
95
- result = false;
96
- continue queryLoop;
97
- }
98
-
99
- if(!this.handleComparison(attr, query[attr], doc))
100
- {
101
- result = false;
102
- continue queryLoop;
103
- }
104
- }
105
- }
106
-
107
- return result;
108
- };
109
- }
110
-
111
- /**
112
- * Not operator
113
- * @param array queries
114
- * @param object doc
115
- * @return array
116
- */
117
- get $not() {
118
- const self = this;
119
- return (queries, doc) => {
120
- if(typeof queries !== 'object')
121
- throw new Error('The $not operator must be an object');
122
-
123
- const handledComparison = self.handleComparison(this._currentField, queries, doc);
124
- return !handledComparison;
125
- };
126
- }
127
-
128
- /**
129
- * Nor operator
130
- * @param array queries
131
- * @param object doc
132
- * @return array
133
- */
134
- get $nor() {
135
- return (queries, doc) => {
136
- if(!Array.isArray(queries))
137
- throw new Error('The $nor operator must be an array');
138
-
139
- for(const query of queries) {
140
- for(const attr of Object.keys(query)) {
141
- if(this.isLogicalOperator(attr) && this[attr](query[attr], doc))
142
- return false;
143
-
144
- if(this.handleComparison(attr, query[attr], doc))
145
- return false;
146
- }
147
- }
148
-
149
- return true;
150
- };
151
- }
152
-
153
- /**
154
- * Not in operator
155
- * @param mixed doc_value
156
- * @param array queries
157
- * @return boolean
158
- */
159
- get $nin() {
160
- return (doc_value, queries) => {
161
- if(!Array.isArray(queries))
162
- throw new Error('The $nin operator must be an array');
163
-
164
- return !this.$in(doc_value, queries);
165
- }
166
- }
167
-
168
- /**
169
- * In operator
170
- * @param mixed doc_value
171
- * @param array queries
172
- * @return boolean
173
- */
174
- get $in() {
175
- return (doc_value, queries) => {
176
- if(!Array.isArray(queries))
177
- throw new Error('The $in operator must be an array');
178
-
179
- if(Array.isArray(doc_value))
180
- {
181
- for(const value of queries)
182
- {
183
- if(doc_value.indexOf(value) > -1)
184
- return true;
185
- }
186
- }
187
-
188
- return queries.indexOf(doc_value) > -1;
189
- }
190
- }
191
-
192
- /**
193
- * less than
194
- * @param mixed doc_value
195
- * @param mixed query
196
- * @return boolean
197
- */
198
- get $lt() {
199
- return (doc_value, query) => {
200
- return !Array.isArray(query) ? doc_value < query : false;
201
- }
202
- }
203
-
204
- /**
205
- * less than or equal
206
- * @param mixed doc_value
207
- * @param mixed query
208
- * @return boolean
209
- */
210
- get $lte() {
211
- return (doc_value, query) => {
212
- return !Array.isArray(query) ? doc_value <= query : false;
213
- }
214
- }
215
-
216
- /**
217
- * greater than
218
- * @param mixed doc_value
219
- * @param mixed query
220
- * @return boolean
221
- */
222
- get $gt() {
223
- return (doc_value, query) => {
224
- return !Array.isArray(query) ? doc_value > query : false;
225
- }
226
- }
227
-
228
- /**
229
- * greater than or equal
230
- * @param mixed doc_value
231
- * @param mixed query
232
- * @return boolean
233
- */
234
- get $gte() {
235
- return (doc_value, query) => {
236
- return !Array.isArray(query) ? doc_value >= query : false;
237
- }
238
- }
239
-
240
- /**
241
- * not equal
242
- * @param mixed doc_value
243
- * @param mixed query
244
- * @return boolean
245
- */
246
- get $ne() {
247
- return (doc_value, query) => {
248
- return !Array.isArray(query) ? doc_value !== query : false;
249
- }
250
- }
251
-
252
- /**
253
- * equal
254
- * @param mixed doc_value
255
- * @param mixed query
256
- * @return boolean
257
- */
258
- get $eq() {
259
- return (doc_value, query) => {
260
- return !Array.isArray(query) ? doc_value === query : false;
261
- };
262
- }
263
-
264
- /**
265
- * Handling with comparison of columns
266
- * @param string field
267
- * @param mixed query
268
- * @param object doc
269
- * @return boolean
270
- */
271
- handleComparison(field, query, doc) {
272
- if(typeof doc[field] === 'undefined' && typeof doc[this._currentField] === 'undefined')
273
- return false;
274
-
275
- if(this.isOperator(field))
276
- return this[field](doc[this._currentField], query);
277
-
278
- if(typeof query === 'object')
279
- {
280
- const operator = Object.keys(query)[0];
281
- field = typeof field === 'undefined' ? this._currentField : field;
282
- return this[operator](doc[field], query[operator]);
283
- }
284
-
285
- return this.$eq(doc[field], query);
286
- }
287
- }
288
-
1
+ class operators {
2
+ /**
3
+ * Constructs the class with the list of supported operators
4
+ */
5
+ constructor() {
6
+ this._logicalOperators = ['$or', '$and', '$not', '$nor'];
7
+ this._comparisonOperators = ['$lt', '$lte', '$gt', '$gte', '$eq', '$ne', '$in', '$nin'];
8
+ }
9
+
10
+ /**
11
+ * Check if is any operator
12
+ * @param string operator
13
+ * @return boolean
14
+ */
15
+ isAnyOperator(operator) {
16
+ return this.isLogicalOperator(operator) || this.isOperator(operator);
17
+ }
18
+
19
+ /**
20
+ * check if is logical operator
21
+ * @param string operator
22
+ * @return boolean
23
+ */
24
+ isLogicalOperator(operator) {
25
+ return this._logicalOperators.indexOf(operator) > -1;
26
+ }
27
+
28
+ /**
29
+ * Is operators
30
+ * @param string operator
31
+ * @return boolean
32
+ */
33
+ isOperator(operator) {
34
+ return this._comparisonOperators.indexOf(operator) > -1;
35
+ }
36
+
37
+ /**
38
+ * Checks if a document matches a query. Logical operators can be nested at any depth:
39
+ * {$and: [{age: {$gte: 30}}, {$or: [{name: 'X'}, {city: {$in: ['A', 'B']}}]}]}
40
+ * @param object doc
41
+ * @param object query
42
+ * @return boolean
43
+ */
44
+ matches(doc, query) {
45
+ if(query === null || typeof query !== 'object' || Array.isArray(query))
46
+ throw new Error('The query must be an object');
47
+
48
+ for(const key of Object.keys(query)) {
49
+ if(this.isLogicalOperator(key)) {
50
+ if(!this.matchesLogical(key, query[key], doc))
51
+ return false;
52
+
53
+ continue;
54
+ }
55
+
56
+ if(!this.matchesCondition(doc[key], query[key]))
57
+ return false;
58
+ }
59
+
60
+ return true;
61
+ }
62
+
63
+ /**
64
+ * Logical operators: every query of $and, at least one of $or, none of $nor
65
+ * @param string operator
66
+ * @param array queries
67
+ * @param object doc
68
+ * @return boolean
69
+ */
70
+ matchesLogical(operator, queries, doc) {
71
+ if(operator === '$not')
72
+ throw new Error('The $not operator must be used inside a field condition, like {age: {$not: {$gt: 30}}}');
73
+
74
+ if(!Array.isArray(queries))
75
+ throw new Error(`The ${operator} operator must be an array`);
76
+
77
+ switch(operator) {
78
+ case '$and':
79
+ return queries.every(query => this.matches(doc, query));
80
+ case '$or':
81
+ return queries.some(query => this.matches(doc, query));
82
+ case '$nor':
83
+ return !queries.some(query => this.matches(doc, query));
84
+ }
85
+
86
+ return false;
87
+ }
88
+
89
+ /**
90
+ * Checks a field value against a condition: a plain value means equality, an object of operators
91
+ * must satisfy every operator ({$gte: 1, $lte: 5}), and $not inverts a condition
92
+ * @param mixed value
93
+ * @param mixed condition
94
+ * @return boolean
95
+ */
96
+ matchesCondition(value, condition) {
97
+ if(!this.isOperatorObject(condition))
98
+ return this.$eq(value, condition);
99
+
100
+ for(const operator of Object.keys(condition)) {
101
+ if(operator === '$not') {
102
+ if(this.matchesCondition(value, condition[operator]))
103
+ return false;
104
+
105
+ continue;
106
+ }
107
+
108
+ if(!this.isOperator(operator))
109
+ throw new Error(`Unknown operator ${operator}`);
110
+
111
+ if(!this[operator](value, condition[operator]))
112
+ return false;
113
+ }
114
+
115
+ return true;
116
+ }
117
+
118
+ /**
119
+ * Whether a condition is an object made of operators, like {$gt: 5}
120
+ * @param mixed condition
121
+ * @return boolean
122
+ */
123
+ isOperatorObject(condition) {
124
+ if(condition === null || typeof condition !== 'object' || Array.isArray(condition))
125
+ return false;
126
+
127
+ const keys = Object.keys(condition);
128
+ return keys.length > 0 && keys.every(key => key.startsWith('$'));
129
+ }
130
+
131
+ /**
132
+ * Not in operator
133
+ * @param mixed doc_value
134
+ * @param array queries
135
+ * @return boolean
136
+ */
137
+ get $nin() {
138
+ return (doc_value, queries) => {
139
+ if(!Array.isArray(queries))
140
+ throw new Error('The $nin operator must be an array');
141
+
142
+ return !this.$in(doc_value, queries);
143
+ }
144
+ }
145
+
146
+ /**
147
+ * In operator
148
+ * @param mixed doc_value
149
+ * @param array queries
150
+ * @return boolean
151
+ */
152
+ get $in() {
153
+ return (doc_value, queries) => {
154
+ if(!Array.isArray(queries))
155
+ throw new Error('The $in operator must be an array');
156
+
157
+ if(Array.isArray(doc_value))
158
+ {
159
+ for(const value of queries)
160
+ {
161
+ if(doc_value.indexOf(value) > -1)
162
+ return true;
163
+ }
164
+ }
165
+
166
+ return queries.indexOf(doc_value) > -1;
167
+ }
168
+ }
169
+
170
+ /**
171
+ * less than
172
+ * @param mixed doc_value
173
+ * @param mixed query
174
+ * @return boolean
175
+ */
176
+ get $lt() {
177
+ return (doc_value, query) => {
178
+ return !Array.isArray(query) ? doc_value < query : false;
179
+ }
180
+ }
181
+
182
+ /**
183
+ * less than or equal
184
+ * @param mixed doc_value
185
+ * @param mixed query
186
+ * @return boolean
187
+ */
188
+ get $lte() {
189
+ return (doc_value, query) => {
190
+ return !Array.isArray(query) ? doc_value <= query : false;
191
+ }
192
+ }
193
+
194
+ /**
195
+ * greater than
196
+ * @param mixed doc_value
197
+ * @param mixed query
198
+ * @return boolean
199
+ */
200
+ get $gt() {
201
+ return (doc_value, query) => {
202
+ return !Array.isArray(query) ? doc_value > query : false;
203
+ }
204
+ }
205
+
206
+ /**
207
+ * greater than or equal
208
+ * @param mixed doc_value
209
+ * @param mixed query
210
+ * @return boolean
211
+ */
212
+ get $gte() {
213
+ return (doc_value, query) => {
214
+ return !Array.isArray(query) ? doc_value >= query : false;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * not equal
220
+ * @param mixed doc_value
221
+ * @param mixed query
222
+ * @return boolean
223
+ */
224
+ get $ne() {
225
+ return (doc_value, query) => {
226
+ return !Array.isArray(query) ? doc_value !== query : false;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * equal
232
+ * @param mixed doc_value
233
+ * @param mixed query
234
+ * @return boolean
235
+ */
236
+ get $eq() {
237
+ return (doc_value, query) => {
238
+ return !Array.isArray(query) ? doc_value === query : false;
239
+ };
240
+ }
241
+ }
242
+
289
243
  module.exports = operators;