1 |
|
2 |
|
3 |
|
4 |
|
5 | 'use strict';
|
6 | const queryOperators = require('./operators/Query'),
|
7 | logicalOperators = require('./operators/Logical'),
|
8 | updateOperators = require('./operators/Update'),
|
9 | MapQLDocument = require('./Document'),
|
10 | Cursor = require('./Cursor'),
|
11 | Helpers = require('./Helpers'),
|
12 | GenerateID = new (require('./GenerateID'))(),
|
13 | isEqual = require('is-equal');
|
14 |
|
15 | class MapQL extends Map {
|
16 | constructor (_map) {
|
17 | super(_map);
|
18 | }
|
19 |
|
20 | |
21 |
|
22 |
|
23 | set (key = Helpers._null, value = Helpers._null) {
|
24 | return Map.prototype.set.call(this, (value === Helpers._null ? GenerateID.next() : key), (value !== Helpers._null ? value : key));
|
25 | }
|
26 |
|
27 | |
28 |
|
29 |
|
30 |
|
31 | has (key, strict = true) {
|
32 | if (!strict) {
|
33 | return [...this.keys()].some((_key) => {
|
34 | return isEqual(key, _key);
|
35 | });
|
36 | }
|
37 | return Map.prototype.has.call(this, key);
|
38 | }
|
39 |
|
40 | |
41 |
|
42 |
|
43 |
|
44 | get (key, strict = true) {
|
45 | if (!strict) {
|
46 | for (let [_key, value] of [...this.entries()]) {
|
47 | if (isEqual(key, _key)) {
|
48 | return value;
|
49 | }
|
50 | }
|
51 | return Helpers._null;
|
52 | }
|
53 | return Map.prototype.get.call(this, key);
|
54 | }
|
55 |
|
56 | |
57 |
|
58 |
|
59 |
|
60 | compile (queries = {}, update = false) {
|
61 | let results = {
|
62 | operator: false,
|
63 | list: []
|
64 | };
|
65 | for (let key of Object.keys(queries)) {
|
66 | let isLO = this.isLogicalOperator(key);
|
67 | if (Helpers.is(queries[key], 'Object')) {
|
68 | for (let mode of Object.keys(queries[key])) {
|
69 | results.list.push([key, mode, queries[key][mode]]);
|
70 | }
|
71 |
|
72 | } else if (isLO && Array.isArray(queries[key])) {
|
73 | for (let subobj of queries[key]) {
|
74 |
|
75 | results.list.push(this.compile(subobj));
|
76 | }
|
77 |
|
78 | results.operator = key;
|
79 | } else {
|
80 | let isUQ = (update ? this.isUpdateOperator(key) : this.isQueryOperator(key));
|
81 | results.list.push([
|
82 | update ? (isUQ ? key : '$set') : (isUQ ? Helpers._null : key),
|
83 | (isUQ || update) ? key : '$eq',
|
84 | queries[key]
|
85 | ]);
|
86 | }
|
87 | }
|
88 | return results;
|
89 | }
|
90 |
|
91 | |
92 |
|
93 |
|
94 | isDocument (obj) {
|
95 | return MapQLDocument.isDocument(obj);
|
96 | }
|
97 |
|
98 | |
99 |
|
100 |
|
101 |
|
102 | static get queryOperators () {
|
103 | return queryOperators;
|
104 | }
|
105 | get queryOperators () {
|
106 | return queryOperators;
|
107 | }
|
108 | static get logicalOperators () {
|
109 | return logicalOperators;
|
110 | }
|
111 | get logicalOperators () {
|
112 | return logicalOperators;
|
113 | }
|
114 | static get updateOperators () {
|
115 | return updateOperators;
|
116 | }
|
117 | get updateOperators () {
|
118 | return updateOperators;
|
119 | }
|
120 |
|
121 | |
122 |
|
123 |
|
124 | isQueryOperator (qs = Helpers._null) {
|
125 | return this.queryOperators.hasOwnProperty(qs) === true;
|
126 | }
|
127 |
|
128 | |
129 |
|
130 |
|
131 | getQueryOperator (qs = '$_default') {
|
132 | return this.queryOperators[qs] ? this.queryOperators[qs] : this.queryOperators['$_default'];
|
133 | }
|
134 |
|
135 | |
136 |
|
137 |
|
138 | isLogicalOperator (lo = Helpers._null) {
|
139 | return this.logicalOperators.hasOwnProperty(lo) === true;
|
140 | }
|
141 |
|
142 | |
143 |
|
144 |
|
145 | getLogicalOperator (lo) {
|
146 | return this.logicalOperators[lo] ? this.logicalOperators[lo] : { fn: [].every };
|
147 | }
|
148 |
|
149 | |
150 |
|
151 |
|
152 | isUpdateOperator (uo = Helpers._null) {
|
153 | return this.updateOperators.hasOwnProperty(uo) === true;
|
154 | }
|
155 |
|
156 | |
157 |
|
158 |
|
159 | getUpdateOperator (uo = '$_default') {
|
160 | return this.updateOperators[uo] ? this.updateOperators[uo] : this.updateOperators['$_default'];
|
161 | }
|
162 |
|
163 | |
164 |
|
165 |
|
166 |
|
167 | _validate (entry = [], queries = {}) {
|
168 | return this.getLogicalOperator(queries.operator).fn.call(queries.list, (_query) => {
|
169 | if (this.isLogicalOperator(queries.operator)) {
|
170 | return this._validate(entry, _query);
|
171 | } else {
|
172 | return this.getQueryOperator(_query[1]).fn.apply(this, [
|
173 | Helpers.dotNotation(_query[0], entry[1], { autoCreate: false }),
|
174 | _query[2],
|
175 | _query[0],
|
176 | entry
|
177 | ]);
|
178 | }
|
179 | });
|
180 | }
|
181 |
|
182 | |
183 |
|
184 |
|
185 | find (queries = {}, projections = {}, one = false, bykey = false) {
|
186 | let cursor = new Cursor();
|
187 | if (Helpers.is(queries, '!Object')) {
|
188 | let value;
|
189 | if ((value = this.get(queries, false)) !== Helpers._null) {
|
190 | cursor.add(new MapQLDocument(queries, value).bykey(true));
|
191 | if (one || bykey) {
|
192 | return cursor;
|
193 | }
|
194 | }
|
195 | queries = { '$eq' : queries };
|
196 | }
|
197 | let _queries = this.compile(queries);
|
198 | if (!!_queries.list.length) {
|
199 | for (let entry of this.entries()) {
|
200 | if (this._validate(!bykey ? entry : [entry[0], entry[0]], _queries)) {
|
201 | cursor.add(new MapQLDocument(entry[0], entry[1]).bykey(bykey));
|
202 | if (one) {
|
203 | return cursor;
|
204 | }
|
205 | }
|
206 | }
|
207 | return cursor;
|
208 | } else {
|
209 | return new Cursor().add(MapQLDocument.convert(one ? [[...this.entries()][0]] : [...this.entries()]));
|
210 | }
|
211 | }
|
212 |
|
213 | |
214 |
|
215 |
|
216 | findOne (queries = {}, projections = {}) {
|
217 | return this.find(queries, projections, true);
|
218 | }
|
219 |
|
220 | |
221 |
|
222 |
|
223 | findByKey (queries = {}, projections = {}, one = false) {
|
224 | return this.find(queries, projections, one, true);
|
225 | }
|
226 |
|
227 | |
228 |
|
229 |
|
230 | findPromise (queries = {}, projections = {}, one = false) {
|
231 | return new Promise((resolve, reject) => {
|
232 | try {
|
233 | let results = this.find(queries, projections, one);
|
234 | return !!results.length ? resolve(results) : reject(new Error('No entries found.'));
|
235 | } catch (error) {
|
236 | reject(error);
|
237 | }
|
238 | });
|
239 | }
|
240 |
|
241 | |
242 |
|
243 |
|
244 |
|
245 |
|
246 | update (queries, modifiers, options = {}) {
|
247 | let opts = Object.assign({
|
248 | multi: false,
|
249 | projections: {}
|
250 | }, options),
|
251 | cursor = this[Helpers.is(queries, 'String') ? 'findByKey' : 'find'](queries, opts.projections, !opts.multi);
|
252 | if (!cursor.empty()) {
|
253 | let update = this.compile(modifiers, true);
|
254 | if (!!update.list.length) {
|
255 | for (let entry of cursor) {
|
256 | update.list.forEach((_update) => {
|
257 | this.getUpdateOperator(_update[0]).fn.apply(this, [_update[1], _update[2], entry, this]);
|
258 | });
|
259 | }
|
260 | }
|
261 | }
|
262 | return cursor;
|
263 | }
|
264 |
|
265 | |
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 | remove (queries, multi = false) {
|
272 | let removed = [];
|
273 | if (Helpers.is(queries, '!Object')) {
|
274 | for (let key of (Array.isArray(queries) ? queries : [queries])) {
|
275 | if (this.has(key) && this.delete(key)) {
|
276 | removed.push(key);
|
277 | }
|
278 | }
|
279 | } else {
|
280 | let _queries = this.compile(queries);
|
281 | if (!!_queries.list.length) {
|
282 | for (let entry of this.entries()) {
|
283 | if (this._validate(entry, _queries)) {
|
284 | if (this.delete(entry[0])) {
|
285 | if (!multi) {
|
286 | return [entry[0]];
|
287 | } else {
|
288 | removed.push(entry[0]);
|
289 | }
|
290 | }
|
291 | }
|
292 | }
|
293 | }
|
294 | }
|
295 | return removed;
|
296 | }
|
297 |
|
298 | |
299 |
|
300 |
|
301 |
|
302 |
|
303 | export (options = {}) {
|
304 | let opts = Object.assign({
|
305 | stringify: true,
|
306 | promise: false,
|
307 | pretty: false,
|
308 | }, options);
|
309 | try {
|
310 | let _export = (value) => {
|
311 | if (Helpers.is(value, 'Set')) {
|
312 | return [...value].map((k) => [_export(k), Helpers.typeToInt(Helpers.getType(k))]);
|
313 | } else if (Helpers.is(value, ['MapQL', 'Map'], false, true)) {
|
314 | return [...value].map(([k,v]) => [_export(k), _export(v), Helpers.typeToInt(Helpers.getType(k)), Helpers.typeToInt(Helpers.getType(v))]);
|
315 | } else if (Helpers.is(value, 'Array')) {
|
316 | return value.map((value) => { return [_export(value), Helpers.typeToInt(Helpers.getType(value))]; });
|
317 | } else if (Helpers.is(value, 'Object')) {
|
318 | for (let key of Object.keys(value)) {
|
319 | value[key] = convertValueByType(value[key], Helpers.getType(value[key]), _export);
|
320 | }
|
321 | } else if (isTypedArray(value)) {
|
322 | return Array.from(value);
|
323 | }
|
324 | return convertValueByType(value, Helpers.getType(value));
|
325 | },
|
326 | exported = _export(Helpers.deepClone(this, MapQL));
|
327 |
|
328 | return ((res) => {
|
329 | return (opts.provalueise ? Promise.resolve(res) : res);
|
330 | })(opts.stringify ? JSON.stringify(exported, null, (opts.pretty ? 4 : 0)) : exported);
|
331 | } catch (error) {
|
332 | return (opts.promise ? Promise.reject(error) : error);
|
333 | }
|
334 | }
|
335 |
|
336 | |
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 |
|
343 |
|
344 | import (json, options = {}) {
|
345 | let opts = Object.assign({
|
346 | promise: false
|
347 | }, options);
|
348 | try {
|
349 | (Helpers.is(json, 'String') ? JSON.parse(json) : json).map((entry) => {
|
350 | this.set(fromType(entry[0], entry[2] || ''), fromType(entry[1], entry[3] || ''));
|
351 | });
|
352 | } catch (error) {
|
353 | if (opts.promise) {
|
354 | return Promise.reject(error);
|
355 | } else {
|
356 | throw error;
|
357 | }
|
358 | }
|
359 | return (opts.promise ? Promise.resolve(this) : this);
|
360 | }
|
361 |
|
362 |
|
363 | |
364 |
|
365 |
|
366 | get [Symbol.toStringTag]() {
|
367 | return (this.constructor.name || 'MapQL');
|
368 | }
|
369 | }
|
370 |
|
371 |
|
372 |
|
373 |
|
374 | function isTypedArray (value) {
|
375 | try {
|
376 | if (ArrayBuffer.isView(value) && !(value instanceof DataView)) {
|
377 | return true;
|
378 | }
|
379 | } catch (error) { }
|
380 | return false;
|
381 | }
|
382 |
|
383 |
|
384 |
|
385 |
|
386 | function convertValueByType (value, type, _export = false) {
|
387 | let _return = ((_exp) => {
|
388 | return (v, t) => {
|
389 | return _exp ? [_exp(v), t] : v
|
390 | }
|
391 | })(_export);
|
392 |
|
393 | let typeint = Helpers.typeToInt(type);
|
394 | switch (type) {
|
395 | case 'Date':
|
396 | return _return(value.getTime(), typeint);
|
397 | case 'Number':
|
398 | return _return(isNaN(value) ? value.toString() : Number(value), typeint)
|
399 | case 'Symbol':
|
400 | return _return(String(value).slice(7, -1), typeint);
|
401 | default:
|
402 | if (_export) {
|
403 | return _return(value, typeint);
|
404 | } else {
|
405 | return _return(Helpers.is(value, ['!Null', '!Boolean', '!Object']) ? value.toString() : value, typeint);
|
406 | }
|
407 | }
|
408 | };
|
409 |
|
410 |
|
411 |
|
412 |
|
413 | function fromType (entry, type) {
|
414 | let inttype = Helpers.intToType(type);
|
415 | switch (inttype) {
|
416 | case 'MapQL': case 'Map':
|
417 | return (new MapQL()).import(entry);
|
418 | case 'Set':
|
419 | return new Set(entry.map((val) => {
|
420 | return fromType(val[0], val[1]);
|
421 | }));
|
422 | case 'Array':
|
423 | return entry.map((val) => {
|
424 | return fromType(val[0], val[1]);
|
425 | });
|
426 | case 'Object':
|
427 | return ((obj) => {
|
428 | for (let key of Object.keys(obj)) {
|
429 | obj[key] = fromType(obj[key][0], obj[key][1]);
|
430 | }
|
431 | return obj;
|
432 | })(entry);
|
433 | case 'Function':
|
434 |
|
435 | return new Function(`return ${entry};`)();
|
436 | case 'RegExp':
|
437 | return RegExp.apply(null, entry.match(/\/(.*?)\/([gimuy])?$/).slice(1));
|
438 | case 'Date':
|
439 | return new Date(entry);
|
440 | case 'Uint8Array':
|
441 | try {
|
442 | return new Uint8Array(entry);
|
443 | } catch (error) {
|
444 | try {
|
445 | return Buffer.from(entry);
|
446 | } catch (error) { return Array.from(entry); }
|
447 | }
|
448 | case 'Buffer':
|
449 | try {
|
450 | return Buffer.from(entry);
|
451 | } catch (error) {
|
452 | try {
|
453 | return new Uint8Array(entry);
|
454 | } catch (error) { return Array.from(entry); }
|
455 | }
|
456 | default:
|
457 |
|
458 |
|
459 |
|
460 | let _fn = (Helpers.__GLOBAL[inttype] ? (new Function(`return ${inttype}`))() : (e) => { return e });
|
461 | try { return _fn(entry); } catch (e) { try { return new _fn(entry); } catch (error) { console.trace(error); } }
|
462 | }
|
463 | }
|
464 |
|
465 |
|
466 |
|
467 |
|
468 | module.exports = MapQL;
|