sorla-db 1.0.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.
@@ -0,0 +1,161 @@
1
+ const documents = require('../documents/document.js');
2
+ const operatorsCheck = require('./operators.js');
3
+
4
+ class manipulation {
5
+ constructor(validationSchema = null) {
6
+ this.validationSchema = validationSchema;
7
+ this._index = {};
8
+ this._prohibitedCharacters = ['$', '.'];
9
+ }
10
+
11
+ /**
12
+ * Generates a unique key as a uuid
13
+ * @return string
14
+ */
15
+ _generateKey() {
16
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
17
+ var r = Math.random() * 16 | 0,
18
+ v = c === 'x' ? r : (r & 0x3 | 0x8);
19
+ return v.toString(16)
20
+ });
21
+ }
22
+
23
+ /**
24
+ * Valid and set validation schema for collection
25
+ * @author Anderson Arruda < anderson@sysborg.com.br >
26
+ * @version 1.0.0
27
+ * @param validationSchema
28
+ * @return void
29
+ **/
30
+ set validationSchema(validationSchema)
31
+ {
32
+ if(typeof schemaValidation === 'undefined' || schemaValidation === null)
33
+ return;
34
+
35
+ if(['object', 'function'].indexOf(typeof schemaValidation) == -1)
36
+ {
37
+ throw new Error('Invalid schema validation');
38
+ }
39
+
40
+ this._validationSchema = validationSchema;
41
+ }
42
+
43
+ /**
44
+ * Validate data
45
+ * @param object data
46
+ * @return boolean
47
+ */
48
+ _validate(data)
49
+ {
50
+ //under construction
51
+ return true;
52
+ }
53
+
54
+ /**
55
+ * Verify if exists some document that is not object
56
+ * @param array documents
57
+ * @return boolean
58
+ */
59
+ _validateDocumentsType(documents)
60
+ {
61
+ const count = documents.reduce((acc, doc) => {
62
+ return typeof doc !== 'object' ? ++acc : acc;
63
+ }, 0);
64
+
65
+ return count;
66
+ }
67
+
68
+ /**
69
+ * Verify if keys has prohibited characters
70
+ * @param object document
71
+ * @return boolean
72
+ */
73
+ _validateKeys(document)
74
+ {
75
+ const keys = Object.keys(document);
76
+ const operators = new operatorsCheck();
77
+ for(let key of keys)
78
+ {
79
+ const hasProhibitedCharacters = this._prohibitedCharacters.reduce((acc, char) => {
80
+ return key.indexOf(char) > -1 ? ++acc : acc;
81
+ }, 0);
82
+
83
+ if(hasProhibitedCharacters > 0 && !operators.isOperator(key))
84
+ return false;
85
+
86
+ if(typeof document[key] === 'object')
87
+ return this._validateKeys(document[key]);
88
+
89
+ if(Array.isArray(document[key]))
90
+ {
91
+ for(let value of document[key])
92
+ {
93
+ if(typeof value === 'object')
94
+ return this._validateKeys(value);
95
+ }
96
+ }
97
+ }
98
+
99
+ return true;
100
+ }
101
+
102
+ /**
103
+ * Inserts one document to the collection
104
+ * @author Anderson Arruda < anderson@sysborg.com.br >
105
+ * @version 1.0.0
106
+ * @param object document
107
+ * @param function callback - under construction
108
+ * @return void
109
+ **/
110
+ insertOne(document, callback)
111
+ {
112
+ if(typeof document !== 'object' || Array.isArray(document))
113
+ throw new Error('Invalid document type');
114
+
115
+ if(!this._validateKeys(document))
116
+ throw new Error('Invalid document keys');
117
+
118
+ if(typeof document._id === 'undefined')
119
+ {
120
+ let _idKey = '';
121
+ do {
122
+ _idKey = this._generateKey();
123
+ } while(this._documents.find(doc => doc._id === document._id));
124
+
125
+ document._id = _idKey;
126
+ }
127
+
128
+ if(!this._validate(document))
129
+ throw new Error('Invalid document');
130
+
131
+ this._documents.push(new documents(document));
132
+
133
+ if(typeof callback === 'function')
134
+ callback();
135
+ }
136
+
137
+ /**
138
+ * Insert many documents
139
+ * @author Anderson Arruda < anderson@sysborg.com.br >
140
+ * @version 1.0.0
141
+ * @param array documents
142
+ * @param function callback
143
+ * @return void
144
+ */
145
+ insertMany(documents, callback) {
146
+ console.info(this._validateDocumentsType(documents));
147
+ if(!Array.isArray(documents) || this._validateDocumentsType(documents) > 0)
148
+ throw new Error('Documents must be an array of objects');
149
+
150
+ for(let document of documents)
151
+ this.insertOne(document);
152
+
153
+ if(typeof callback === 'function')
154
+ callback();
155
+ }
156
+
157
+ //create index
158
+ //create indexes
159
+ }
160
+
161
+ module.exports = manipulation;
@@ -0,0 +1,289 @@
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
+
289
+ module.exports = operators;
@@ -0,0 +1,104 @@
1
+ const manipulation = require('./manipulation.js');
2
+ const operators = require('./operators.js');
3
+ const cursor = require('../documents/cursor.js');
4
+
5
+ class query extends manipulation
6
+ {
7
+ constructor(validationSchema)
8
+ {
9
+ super(validationSchema);
10
+ this._operators = new operators();
11
+ }
12
+
13
+ /**
14
+ * Count documents
15
+ * @return int
16
+ */
17
+ count()
18
+ {
19
+ return this._documents.length;
20
+ }
21
+
22
+ /**
23
+ * Deeps find in object
24
+ * @param object query
25
+ * @param object projection
26
+ * @param bool firstOne
27
+ * @return array
28
+ */
29
+ _deepFind(query, projection, single=false)
30
+ {
31
+ const c = new cursor();
32
+ const find = (documents, query, single, debug=false) => {
33
+ let finded = [];
34
+ const queryKeys = Object.keys(query);
35
+ docsLoop: for(let doc of documents)
36
+ {
37
+ for(let k of queryKeys)
38
+ {
39
+ if(!this._operators.isAnyOperator(k))
40
+ {
41
+ this._operators.currentField = k;
42
+
43
+ if(typeof query[k] === 'object')
44
+ {
45
+ if(find([doc], query[k], single).length === 0)
46
+ {
47
+ continue docsLoop;
48
+ }
49
+
50
+ continue;
51
+ }
52
+ }
53
+
54
+ const isLogicalOperator = this._operators.isLogicalOperator(k);
55
+ if((isLogicalOperator && !this._operators[k](query[k], doc)) || (!isLogicalOperator && !this._operators.handleComparison(k, query[k], doc)))
56
+ {
57
+ continue docsLoop;
58
+ }
59
+ }
60
+
61
+ finded.push(doc.clone());
62
+ if(single) break;
63
+ }
64
+
65
+ return finded;
66
+ };
67
+
68
+ const result = find(this._documents, query, single);
69
+ const finded = new cursor(result, projection);
70
+
71
+ return finded;
72
+ }
73
+
74
+ /**
75
+ * Find one document
76
+ * @param query
77
+ * @param projection
78
+ * @return object
79
+ */
80
+ findOne(query, projection = null)
81
+ {
82
+ const finded = this._deepFind(query, projection, true);
83
+ return finded.first();
84
+ }
85
+
86
+ /**
87
+ * Find documents
88
+ * @param query
89
+ * @param projection
90
+ * @return array
91
+ */
92
+ find(query, projection = null)
93
+ {
94
+ const finded = this._deepFind(query, projection);
95
+ return finded;
96
+ }
97
+
98
+ //find
99
+ //find one and delete
100
+ //find one and update
101
+ //find one and replace
102
+ }
103
+
104
+ module.exports = query;
package/src/sorla.js ADDED
@@ -0,0 +1,123 @@
1
+ const database = require('./dbs/database.js');
2
+
3
+ class sorla{
4
+ /**
5
+ * Constructs class Sorla to manage databases
6
+ * @author Anderson Arruda < andmarruda@gmail.com >
7
+ * @version 1.0.0
8
+ * @param
9
+ * @return void
10
+ */
11
+ constructor()
12
+ {
13
+ this._dbs = [];
14
+ this._selectedDb = null;
15
+ }
16
+
17
+ /**
18
+ * Search a database`s position in the list of databases by its name and mark it as selected
19
+ * @author Anderson Arruda < andmarruda@gmail.com >
20
+ * @version
21
+ * @param
22
+ * @return
23
+ **/
24
+ _findDbIndex(name)
25
+ {
26
+ return this._dbs.findIndex(db => db.name == name);
27
+ }
28
+
29
+ /**
30
+ * Verify if a database name already exists in the list of databases
31
+ * @author Anderson Arruda < andmarruda@gmail.com >
32
+ * @version 1.0.0
33
+ * @param string name
34
+ * @return boolean
35
+ */
36
+ dbExists(name)
37
+ {
38
+ return this._findDbIndex(name) > -1;
39
+ }
40
+
41
+ /**
42
+ * Mark a database as selected
43
+ * @author Anderson Arruda < andmarruda@gmail.com >
44
+ * @version 1.0.0
45
+ * @param
46
+ * @return
47
+ **/
48
+ _selectDb(pos)
49
+ {
50
+ if(!this._dbs.hasOwnProperty(pos))
51
+ {
52
+ throw new Error('Database does not exists');
53
+ }
54
+ this._selectedDb = this._dbs[pos];
55
+ }
56
+
57
+ /**
58
+ * Creates a new database
59
+ * @author Anderson Arruda < andmarruda@gmail.com >
60
+ * @version 1.0.0
61
+ * @param string name
62
+ * @return int "The position of the new database in the list of databases"
63
+ **/
64
+ createDb(name)
65
+ {
66
+ if(this.dbExists(name))
67
+ {
68
+ return false;
69
+ }
70
+
71
+ let pos = this._dbs.length;
72
+ this._dbs.push(new database(name));
73
+ return pos;
74
+ }
75
+
76
+ /**
77
+ * Selects a database by its name or creates a new one if it does not exists and then selects it
78
+ * @author Anderson Arruda < andmarruda@gmail.com >
79
+ * @version 1.0.0
80
+ * @param string name
81
+ * @return void
82
+ **/
83
+ useDb(name)
84
+ {
85
+ let pos = null;
86
+ if(!this.dbExists(name))
87
+ {
88
+ pos = this.createDb(name);
89
+ }
90
+
91
+ this._selectDb(pos === null ? this._findDbIndex(name) : pos);
92
+ }
93
+
94
+ /**
95
+ * Get the number of created databases
96
+ * @author Anderson Arruda < andmarruda@gmail.com >
97
+ * @version 1.0.0
98
+ * @param
99
+ * @return int
100
+ **/
101
+ countDb()
102
+ {
103
+ return this._dbs.length;
104
+ }
105
+
106
+ /**
107
+ * Return selected db
108
+ * @author Anderson Arruda < anderson@sysborg.com.br >
109
+ * @version 1.0.0
110
+ * @param
111
+ * @return instanceof database
112
+ **/
113
+ get db()
114
+ {
115
+ return this._selectedDb;
116
+ }
117
+ }
118
+
119
+ module.exports = sorla;
120
+ if(typeof window !== 'undefined')
121
+ {
122
+ window.sorla = sorla;
123
+ }
package/src/utils.js ADDED
@@ -0,0 +1,41 @@
1
+ class utils
2
+ {
3
+ /**
4
+ * Get a uuid v4 string
5
+ * @author Anderson Arruda < anderson@sysborg.com.br >
6
+ * @version 1.0.0
7
+ * @param
8
+ * @return string
9
+ **/
10
+ get uuid()
11
+ {
12
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c)
13
+ {
14
+ let r = Math.random() * 16 | 0,
15
+ v = c == 'x' ? r : (r & 0x3 | 0x8);
16
+
17
+ return v.toString(16);
18
+ });
19
+ }
20
+
21
+ /**
22
+ * Get data using dots to access subfields
23
+ * @param object data
24
+ * @param string field
25
+ */
26
+ getDotData(data, field)
27
+ {
28
+ let fields = field.split('.');
29
+
30
+ fields.forEach(field => {
31
+ if(typeof data[field] !== 'undefined')
32
+ {
33
+ data = data[field];
34
+ }
35
+ });
36
+
37
+ return data;
38
+ }
39
+ }
40
+
41
+ module.exports = utils;