sorla-db 1.1.0 → 1.3.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.
- package/README.md +152 -9
- package/dist/sorla.min.js +1 -1
- package/index.d.ts +157 -0
- package/index.mjs +5 -0
- package/package.json +27 -3
- package/src/dbs/database.js +48 -0
- package/src/documents/cursor.js +141 -25
- package/src/documents/document.js +26 -30
- package/src/events.js +69 -0
- package/src/query_language/manipulation.js +321 -50
- package/src/query_language/operators.js +66 -1
- package/src/query_language/query.js +6 -2
- package/src/query_language/update.js +167 -0
- package/src/sorla.js +27 -0
- package/src/utils.js +9 -8
- package/Brainstorming.md +0 -22
- package/debug-not.js +0 -24
- package/example/simple_data_example.html +0 -196
- package/index.js +0 -62
- package/src/helper.js +0 -41
- package/test/feature_collection.test.js +0 -75
- package/test/feature_collection_queries.test.js +0 -224
- package/test/feature_cursor.test.js +0 -84
- package/test/feature_database.test.js +0 -62
- package/test/feature_insert.test.js +0 -116
- package/test/feature_nested_queries.test.js +0 -94
- package/test/feature_query_equivalence.test.js +0 -165
- package/test/unit_database.test.js +0 -36
- package/test/unit_document.test.js +0 -39
- package/test/unit_operators.test.js +0 -78
- package/test/unit_sorla.test.js +0 -42
- package/test/unit_test.test.js +0 -25
- package/webpack.config.js +0 -11
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
const chai = require('chai')
|
|
2
|
-
const expect = chai.expect
|
|
3
|
-
const faker = require('faker')
|
|
4
|
-
const sorla = require('../src/sorla.js')
|
|
5
|
-
const helper = require('../src/helper.js')
|
|
6
|
-
|
|
7
|
-
describe('SORLA Collection Queries Feature Test', () => {
|
|
8
|
-
beforeAll(() => {
|
|
9
|
-
this.srla = new sorla();
|
|
10
|
-
this.dbname = faker.random.word();
|
|
11
|
-
this.srla.createDb(this.dbname);
|
|
12
|
-
this.srla.useDb(this.dbname);
|
|
13
|
-
this.collectionName = faker.random.word();
|
|
14
|
-
this.srla.db.createCollection(this.collectionName);
|
|
15
|
-
this.objects = helper.createFakeCollection(this.srla, this.collectionName, 30);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('Test if the first document in the collection is the same first document on the objects array', () => {
|
|
19
|
-
const firstDoc = this.srla.db[this.collectionName].findOne({}, { _id: 0 });
|
|
20
|
-
const firstObj = this.objects[0];
|
|
21
|
-
expect(firstDoc).to.deep.equal(firstObj);
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('Takes the data field name from the document inside the collection', () => {
|
|
25
|
-
const firstDoc = this.srla.db[this.collectionName].findOne({}, { _id: 0 });
|
|
26
|
-
const firstObj = this.objects[0];
|
|
27
|
-
expect(firstDoc.name).to.be.a('string').equal(firstObj.name);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('Takes the data field email from the document inside the collection', () => {
|
|
31
|
-
const firstDoc = this.srla.db[this.collectionName].findOne({}, { _id: 0 });
|
|
32
|
-
const firstObj = this.objects[0];
|
|
33
|
-
expect(firstDoc.email).to.be.a('string').equal(firstObj.email);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('Takes the data field age from the document inside the collection', () => {
|
|
37
|
-
const firstDoc = this.srla.db[this.collectionName].findOne({}, { _id: 0 });
|
|
38
|
-
const firstObj = this.objects[0];
|
|
39
|
-
expect(firstDoc.age).to.be.a('number').equal(firstObj.age);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('Creates a few documents and search for one of them', () => {
|
|
43
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
44
|
-
const doc = this.srla.db[this.collectionName].findOne({ uuid: item1.uuid }, { _id: 0 });
|
|
45
|
-
expect(doc).to.deep.equal(item1);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('Creates a few documents and search for some of them', () => {
|
|
49
|
-
const [item1, item2] = helper.getRandomComparisonData(this.objects);
|
|
50
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
51
|
-
$or: [
|
|
52
|
-
{ uuid: item1.uuid },
|
|
53
|
-
{ uuid: item2.uuid }
|
|
54
|
-
]
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
expect(docs.length).to.be.equal(2);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('Creates a few documents and search using not', () => {
|
|
61
|
-
const [item1] = helper.getRandomComparisonData(this.objects);
|
|
62
|
-
const docs = this.srla.db[this.collectionName].find(
|
|
63
|
-
{
|
|
64
|
-
uuid: {
|
|
65
|
-
$not: {
|
|
66
|
-
$eq: item1.uuid
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
expect(docs.length).to.be.equal(this.objects.length - 1);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it('Creates a few documents and search using not with more than one expression', () => {
|
|
76
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
77
|
-
const docs = this.srla.db[this.collectionName].find(
|
|
78
|
-
{
|
|
79
|
-
age: {
|
|
80
|
-
$not: {
|
|
81
|
-
$eq: item1.age,
|
|
82
|
-
$gt: 18
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
// $not negates the whole condition: excluded when age equals item1.age AND is over 18
|
|
89
|
-
const expected = this.objects.filter(obj => !(obj.age === item1.age && obj.age > 18));
|
|
90
|
-
expect(docs.length).to.be.equal(expected.length);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('Creates a few documents and search it using nor', () => {
|
|
94
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
95
|
-
const docs = this.srla.db[this.collectionName].findOne({
|
|
96
|
-
$nor: [
|
|
97
|
-
{name: item1.name},
|
|
98
|
-
{age: item1.age}
|
|
99
|
-
]
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
expect(docs.name).to.not.be.equal(item1.name);
|
|
103
|
-
expect(docs.age).to.not.be.equal(item1.age);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it('Creates a few documents and search it using nor with more than one expression', () => {
|
|
107
|
-
const [item1, item2] = helper.getRandomComparisonData(this.objects);
|
|
108
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
109
|
-
$nor: [
|
|
110
|
-
{name: item1.name},
|
|
111
|
-
{age: item1.age},
|
|
112
|
-
{name: item2.name},
|
|
113
|
-
{age: item2.age}
|
|
114
|
-
]
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
// random names and ages can repeat, so count the expected documents instead of assuming 2
|
|
118
|
-
const expected = this.objects.filter(obj => obj.name !== item1.name && obj.age !== item1.age && obj.name !== item2.name && obj.age !== item2.age);
|
|
119
|
-
expect(docs.length).to.be.equal(expected.length);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// sorting is covered by test/feature_cursor.test.js
|
|
123
|
-
|
|
124
|
-
it('Test equal comparison', () => {
|
|
125
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
126
|
-
const doc = this.srla.db[this.collectionName].findOne({ uuid: item1.uuid }, { _id: 0 });
|
|
127
|
-
expect(doc).to.deep.equal(item1);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('Test not equal comparison', () => {
|
|
131
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
132
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
133
|
-
uuid: { $ne: item1.uuid }
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
expect(docs.length).to.be.equal(this.objects.length - 1);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('Test greater than comparison', () => {
|
|
140
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
141
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
142
|
-
age: { $gt: item1.age }
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
countAges = this.objects.filter(obj => obj.age > item1.age).length;
|
|
146
|
-
expect(docs.length).to.be.equal(countAges);
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('Test greater than or equal comparison', () => {
|
|
150
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
151
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
152
|
-
age: { $gte: item1.age }
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
countAges = this.objects.filter(obj => obj.age >= item1.age).length;
|
|
156
|
-
expect(docs.length).to.be.equal(countAges);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('Test less than comparison', () => {
|
|
160
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
161
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
162
|
-
age: { $lt: item1.age }
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
countAges = this.objects.filter(obj => obj.age < item1.age).length;
|
|
166
|
-
expect(docs.length).to.be.equal(countAges);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('Test less than or equal comparison', () => {
|
|
170
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
171
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
172
|
-
age: { $lte: item1.age }
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
countAges = this.objects.filter(obj => obj.age <= item1.age).length;
|
|
176
|
-
expect(docs.length).to.be.equal(countAges);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
it('Test in comparison', () => {
|
|
180
|
-
const [item1, item2] = helper.getRandomComparisonData(this.objects);
|
|
181
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
182
|
-
age: { $in: [item1.age, item2.age] }
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
countAges = this.objects.filter(obj => obj.age === item1.age || obj.age === item2.age).length;
|
|
186
|
-
expect(docs.length).to.be.equal(countAges);
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
it('Test not in comparison', () => {
|
|
190
|
-
const [item1, item2] = helper.getRandomComparisonData(this.objects);
|
|
191
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
192
|
-
age: { $nin: [item1.age, item2.age] }
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
countAges = this.objects.filter(obj => obj.age !== item1.age && obj.age !== item2.age).length;
|
|
196
|
-
expect(docs.length).to.be.equal(countAges);
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
it('Test mixed comparison with and', () => {
|
|
200
|
-
const [item1,] = helper.getRandomComparisonData(this.objects);
|
|
201
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
202
|
-
$and: [
|
|
203
|
-
{ name: item1.name },
|
|
204
|
-
{ age: item1.age }
|
|
205
|
-
]
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
const filtered = this.objects.filter(obj => obj.name === item1.name && obj.age === item1.age);
|
|
209
|
-
expect(docs.length).to.be.equal(filtered.length);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
it('Test mixed comparison with or', () => {
|
|
213
|
-
const [item1, item2] = helper.getRandomComparisonData(this.objects);
|
|
214
|
-
const docs = this.srla.db[this.collectionName].find({
|
|
215
|
-
$or: [
|
|
216
|
-
{ name: item1.name },
|
|
217
|
-
{ age: item2.age }
|
|
218
|
-
]
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
const filtered = this.objects.filter(obj => obj.name === item1.name || obj.age === item2.age);
|
|
222
|
-
expect(docs.length).to.be.equal(filtered.length);
|
|
223
|
-
});
|
|
224
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
const chai = require('chai')
|
|
2
|
-
const expect = chai.expect
|
|
3
|
-
const sorla = require('../src/sorla.js')
|
|
4
|
-
|
|
5
|
-
describe('SORLA cursor', () => {
|
|
6
|
-
let people;
|
|
7
|
-
|
|
8
|
-
beforeAll(() => {
|
|
9
|
-
const srla = new sorla();
|
|
10
|
-
srla.useDb('cursor');
|
|
11
|
-
srla.db.createCollection('people');
|
|
12
|
-
people = srla.db.people;
|
|
13
|
-
for(const [name, age] of [['Carla', 42], ['Ana', 25], ['Bruno', 31]])
|
|
14
|
-
people.insertOne({ name, age, email: `${name.toLowerCase()}@mail.com` });
|
|
15
|
-
// projection logs every removed property
|
|
16
|
-
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterAll(() => jest.restoreAllMocks());
|
|
20
|
-
|
|
21
|
-
it('iterates with hasNext/next and can be reset', () => {
|
|
22
|
-
const cursor = people.find({});
|
|
23
|
-
expect(cursor.length).to.equal(3);
|
|
24
|
-
const names = [];
|
|
25
|
-
while(cursor.hasNext()) names.push(cursor.next()._data.name);
|
|
26
|
-
expect(names).to.deep.equal(['Carla', 'Ana', 'Bruno']);
|
|
27
|
-
expect(() => cursor.next()).to.throw('No more item to the cursor');
|
|
28
|
-
cursor.reset();
|
|
29
|
-
expect(cursor.next()._data.name).to.equal('Carla');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('first() returns the data of the first document', () => {
|
|
33
|
-
expect(people.find({ age: { $gt: 30 } }).first().name).to.equal('Carla');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('returns copies: changing a result does not change the collection', () => {
|
|
37
|
-
const cursor = people.find({ name: 'Ana' });
|
|
38
|
-
cursor.next()._data.age = 99;
|
|
39
|
-
expect(people.findOne({ name: 'Ana' }).age).to.equal(25);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('projection with 0 removes fields from the results only', () => {
|
|
43
|
-
const cursor = people.find({ name: 'Ana' }, { email: 0 });
|
|
44
|
-
expect(cursor.next()._data).to.not.have.property('email');
|
|
45
|
-
expect(people.findOne({ name: 'Ana' }).email).to.equal('ana@mail.com');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('last() gives access to the fields of the last document', () => {
|
|
49
|
-
expect(people.find({}).last().name).to.equal('Bruno');
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it('push only accepts objects', () => {
|
|
53
|
-
const cursor = people.find({});
|
|
54
|
-
expect(() => cursor.push('x')).to.throw('The element must be an object');
|
|
55
|
-
cursor.push({ name: 'Extra' });
|
|
56
|
-
expect(cursor.length).to.equal(4);
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
// ---- known bugs: these document current behavior and will start failing once fixed
|
|
60
|
-
|
|
61
|
-
it.failing('sorts by a number ascending (cursor.js/document.js circular require)', () => {
|
|
62
|
-
const cursor = people.find({});
|
|
63
|
-
cursor.sort({ age: 1 });
|
|
64
|
-
expect(cursor.first().name).to.equal('Ana');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it.failing('sorts by several fields descending', () => {
|
|
68
|
-
const cursor = people.find({});
|
|
69
|
-
cursor.sort({ age: -1, name: 1 });
|
|
70
|
-
expect(cursor.first().name).to.equal('Carla');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it.failing('toArray returns plain objects (structuredClone of a Proxy)', () => {
|
|
74
|
-
expect(people.find({}).toArray().map(doc => doc.name)).to.deep.equal(['Carla', 'Ana', 'Bruno']);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it.failing('findOne returns null when nothing matches', () => {
|
|
78
|
-
expect(people.findOne({ name: 'Nobody' })).to.equal(null);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
it.todo('projection with 1 keeps only the listed fields');
|
|
83
|
-
it.todo('skip() and limit()');
|
|
84
|
-
});
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
const chai = require('chai')
|
|
2
|
-
const expect = chai.expect
|
|
3
|
-
const sorla = require('../src/sorla.js')
|
|
4
|
-
|
|
5
|
-
describe('SORLA databases and collections', () => {
|
|
6
|
-
it('useDb creates the database when needed and selects it', () => {
|
|
7
|
-
const srla = new sorla();
|
|
8
|
-
expect(srla.dbExists('shop')).to.equal(false);
|
|
9
|
-
srla.useDb('shop');
|
|
10
|
-
expect(srla.dbExists('shop')).to.equal(true);
|
|
11
|
-
expect(srla.db.name).to.equal('shop');
|
|
12
|
-
expect(srla.countDb()).to.equal(1);
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('createDb returns the position, or false when the name exists', () => {
|
|
16
|
-
const srla = new sorla();
|
|
17
|
-
expect(srla.createDb('a')).to.equal(0);
|
|
18
|
-
expect(srla.createDb('b')).to.equal(1);
|
|
19
|
-
expect(srla.createDb('a')).to.equal(false);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('keeps databases and their collections isolated', () => {
|
|
23
|
-
const srla = new sorla();
|
|
24
|
-
srla.useDb('one');
|
|
25
|
-
srla.db.createCollection('items');
|
|
26
|
-
srla.db.items.insertOne({ n: 1 });
|
|
27
|
-
srla.useDb('two');
|
|
28
|
-
srla.db.createCollection('items');
|
|
29
|
-
expect(srla.db.items.count()).to.equal(0);
|
|
30
|
-
srla.useDb('one');
|
|
31
|
-
expect(srla.db.items.count()).to.equal(1);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('instances do not share data', () => {
|
|
35
|
-
const a = new sorla();
|
|
36
|
-
const b = new sorla();
|
|
37
|
-
a.useDb('x');
|
|
38
|
-
expect(b.dbExists('x')).to.equal(false);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('rejects duplicated collection names', () => {
|
|
42
|
-
const srla = new sorla();
|
|
43
|
-
srla.useDb('dupes');
|
|
44
|
-
srla.db.createCollection('items');
|
|
45
|
-
expect(() => srla.db.createCollection('items')).to.throw('Collection already exists');
|
|
46
|
-
expect(srla.db.countCollections()).to.equal(1);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('collection count, first, last and all', () => {
|
|
50
|
-
const srla = new sorla();
|
|
51
|
-
srla.useDb('basics');
|
|
52
|
-
srla.db.createCollection('items');
|
|
53
|
-
const items = srla.db.items;
|
|
54
|
-
expect(items.count()).to.equal(0);
|
|
55
|
-
items.insertOne({ n: 1 });
|
|
56
|
-
items.insertOne({ n: 2 });
|
|
57
|
-
expect(items.count()).to.equal(2);
|
|
58
|
-
expect(items.first.n).to.equal(1);
|
|
59
|
-
expect(items.last.n).to.equal(2);
|
|
60
|
-
expect(items.all.length).to.equal(2);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
const chai = require('chai')
|
|
2
|
-
const expect = chai.expect
|
|
3
|
-
const sorla = require('../src/sorla.js')
|
|
4
|
-
|
|
5
|
-
const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
6
|
-
|
|
7
|
-
describe('SORLA inserting documents', () => {
|
|
8
|
-
let srla;
|
|
9
|
-
let counter = 0;
|
|
10
|
-
const fresh = () => {
|
|
11
|
-
const name = `insert${++counter}`;
|
|
12
|
-
srla.db.createCollection(name);
|
|
13
|
-
return srla.db[name];
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
beforeAll(() => {
|
|
17
|
-
srla = new sorla();
|
|
18
|
-
srla.useDb('inserts');
|
|
19
|
-
// insertMany logs the count of invalid documents on every call
|
|
20
|
-
jest.spyOn(console, 'info').mockImplementation(() => {});
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
afterAll(() => jest.restoreAllMocks());
|
|
24
|
-
|
|
25
|
-
it('generates a uuid v4 _id when the document has none', () => {
|
|
26
|
-
const collection = fresh();
|
|
27
|
-
collection.insertOne({ name: 'Ana' });
|
|
28
|
-
expect(collection.first._id).to.match(UUID_V4);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('generates unique ids', () => {
|
|
32
|
-
const collection = fresh();
|
|
33
|
-
for(let i = 0; i < 500; i++) collection.insertOne({ i });
|
|
34
|
-
const ids = new Set(collection.all.map(doc => doc._id));
|
|
35
|
-
expect(ids.size).to.equal(500);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('keeps a provided _id', () => {
|
|
39
|
-
const collection = fresh();
|
|
40
|
-
collection.insertOne({ _id: 'custom-1', name: 'Ana' });
|
|
41
|
-
expect(collection.findOne({ _id: 'custom-1' }).name).to.equal('Ana');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it('keeps values of every type', () => {
|
|
45
|
-
const collection = fresh();
|
|
46
|
-
collection.insertOne({ text: 'a', number: 1.5, flag: false, list: [1, 'b'], nested: { deep: { value: 3 } }, empty: '' });
|
|
47
|
-
const doc = collection.first;
|
|
48
|
-
expect(doc).to.include({ text: 'a', number: 1.5, flag: false, empty: '' });
|
|
49
|
-
expect(doc.list).to.deep.equal([1, 'b']);
|
|
50
|
-
expect(doc.nested.deep.value).to.equal(3);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('insertMany inserts in order and counts', () => {
|
|
54
|
-
const collection = fresh();
|
|
55
|
-
collection.insertMany([{ n: 1 }, { n: 2 }, { n: 3 }]);
|
|
56
|
-
expect(collection.count()).to.equal(3);
|
|
57
|
-
expect(collection.all.map(doc => doc.n)).to.deep.equal([1, 2, 3]);
|
|
58
|
-
expect(collection.last.n).to.equal(3);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('calls the callbacks', () => {
|
|
62
|
-
const collection = fresh();
|
|
63
|
-
let calls = 0;
|
|
64
|
-
collection.insertOne({ n: 1 }, () => calls++);
|
|
65
|
-
collection.insertMany([{ n: 2 }], () => calls++);
|
|
66
|
-
expect(calls).to.equal(2);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('rejects documents that are not objects', () => {
|
|
70
|
-
const collection = fresh();
|
|
71
|
-
expect(() => collection.insertOne('text')).to.throw('Invalid document type');
|
|
72
|
-
expect(() => collection.insertOne(['a'])).to.throw('Invalid document type');
|
|
73
|
-
expect(() => collection.insertMany('text')).to.throw();
|
|
74
|
-
expect(() => collection.insertMany([{ a: 1 }, 'b'])).to.throw('Documents must be an array of objects');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('rejects keys with $ or .', () => {
|
|
78
|
-
const collection = fresh();
|
|
79
|
-
expect(() => collection.insertOne({ $name: 'a' })).to.throw('Invalid document keys');
|
|
80
|
-
expect(() => collection.insertOne({ 'a.b': 1 })).to.throw('Invalid document keys');
|
|
81
|
-
expect(() => collection.insertOne({ nested: { 'a.b': 1 } })).to.throw('Invalid document keys');
|
|
82
|
-
expect(() => collection.insertOne({ list: [{ 'a.b': 1 }] })).to.throw('Invalid document keys');
|
|
83
|
-
expect(collection.count()).to.equal(0);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ---- known bugs: these document current behavior and will start failing once fixed
|
|
87
|
-
|
|
88
|
-
it.failing('accepts null values (Object.keys(null) in _validateKeys)', () => {
|
|
89
|
-
const collection = fresh();
|
|
90
|
-
collection.insertOne({ name: 'Ana', nickname: null });
|
|
91
|
-
expect(collection.first.nickname).to.equal(null);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
it.failing('validates every nested object, not only the first one', () => {
|
|
95
|
-
const collection = fresh();
|
|
96
|
-
expect(() => collection.insertOne({ first: { ok: 1 }, second: { 'a.b': 1 } })).to.throw('Invalid document keys');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it.failing('rejects a duplicated _id', () => {
|
|
100
|
-
const collection = fresh();
|
|
101
|
-
collection.insertOne({ _id: 'same' });
|
|
102
|
-
expect(() => collection.insertOne({ _id: 'same' })).to.throw();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it.failing('insertMany explains why a non array is rejected (it calls reduce before validating)', () => {
|
|
106
|
-
const collection = fresh();
|
|
107
|
-
expect(() => collection.insertMany('text')).to.throw('Documents must be an array of objects');
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
it.failing('does not mutate the inserted object', () => {
|
|
111
|
-
const collection = fresh();
|
|
112
|
-
const input = { name: 'Ana' };
|
|
113
|
-
collection.insertOne(input);
|
|
114
|
-
expect(input).to.not.have.property('_id');
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
const chai = require('chai')
|
|
2
|
-
const expect = chai.expect
|
|
3
|
-
const sorla = require('../src/sorla.js')
|
|
4
|
-
|
|
5
|
-
describe('SORLA nested logical queries', () => {
|
|
6
|
-
let people;
|
|
7
|
-
|
|
8
|
-
beforeAll(() => {
|
|
9
|
-
const srla = new sorla();
|
|
10
|
-
srla.useDb('nestedQueries');
|
|
11
|
-
srla.db.createCollection('people');
|
|
12
|
-
srla.db.people.insertOne({ name: 'Ana', age: 25, city: 'A', tags: ['admin'] });
|
|
13
|
-
srla.db.people.insertOne({ name: 'Bruno', age: 31, city: 'B', tags: [] });
|
|
14
|
-
srla.db.people.insertOne({ name: 'Carla', age: 42, city: 'C', tags: ['admin', 'dev'] });
|
|
15
|
-
srla.db.people.insertOne({ name: 'Davi', age: 35, city: 'A' });
|
|
16
|
-
srla.db.people.insertOne({ name: 'Eva', age: 19, city: 'D', tags: ['dev'] });
|
|
17
|
-
people = srla.db.people;
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const names = cursor => {
|
|
21
|
-
const result = [];
|
|
22
|
-
while(cursor.hasNext()) result.push(cursor.next()._data.name);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
it('combines $and with a nested $or', () => {
|
|
27
|
-
const docs = people.find({
|
|
28
|
-
$and: [
|
|
29
|
-
{ age: { $gte: 30 } },
|
|
30
|
-
{ $or: [{ name: 'Carla' }, { city: { $in: ['A', 'B'] } }] }
|
|
31
|
-
]
|
|
32
|
-
});
|
|
33
|
-
expect(names(docs)).to.deep.equal(['Bruno', 'Carla', 'Davi']);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('nests groups at any depth', () => {
|
|
37
|
-
const docs = people.find({
|
|
38
|
-
$or: [
|
|
39
|
-
{ $and: [{ city: 'A' }, { $or: [{ age: { $lt: 30 } }, { name: 'Davi' }] }] },
|
|
40
|
-
{ $and: [{ city: 'D' }, { age: { $lt: 20 } }] }
|
|
41
|
-
]
|
|
42
|
-
});
|
|
43
|
-
expect(names(docs)).to.deep.equal(['Ana', 'Davi', 'Eva']);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('evaluates every operator of a field inside $or', () => {
|
|
47
|
-
const docs = people.find({ $or: [{ age: { $gte: 30, $lte: 35 } }, { name: 'Eva' }] });
|
|
48
|
-
expect(names(docs)).to.deep.equal(['Bruno', 'Davi', 'Eva']);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('evaluates every operator of a field at the top level', () => {
|
|
52
|
-
expect(names(people.find({ age: { $gt: 20, $lt: 40, $ne: 31 } }))).to.deep.equal(['Ana', 'Davi']);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('supports $nor with nested groups', () => {
|
|
56
|
-
const docs = people.find({ $nor: [{ city: 'A' }, { $and: [{ age: { $gt: 40 } }, { city: 'C' }] }] });
|
|
57
|
-
expect(names(docs)).to.deep.equal(['Bruno', 'Eva']);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('$not inverts the whole condition, with one or more operators', () => {
|
|
61
|
-
expect(names(people.find({ age: { $not: { $gte: 30 } } }))).to.deep.equal(['Ana', 'Eva']);
|
|
62
|
-
expect(names(people.find({ age: { $not: { $gte: 30, $lte: 35 } } }))).to.deep.equal(['Ana', 'Carla', 'Eva']);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('$not can be combined with other operators and logical groups', () => {
|
|
66
|
-
const docs = people.find({ $and: [{ city: { $not: { $eq: 'A' } } }, { $or: [{ age: { $lt: 20 } }, { name: 'Bruno' }] }] });
|
|
67
|
-
expect(names(docs)).to.deep.equal(['Bruno', 'Eva']);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('matches array fields with $in', () => {
|
|
71
|
-
expect(names(people.find({ $or: [{ tags: { $in: ['dev'] } }, { age: 31 }] }))).to.deep.equal(['Bruno', 'Carla', 'Eva']);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('a field missing in the document matches $ne and $nin', () => {
|
|
75
|
-
expect(names(people.find({ tags: { $ne: 'x' }, city: 'A' }))).to.deep.equal(['Ana', 'Davi']);
|
|
76
|
-
expect(names(people.find({ name: { $nin: ['Ana', 'Bruno'] }, age: { $lt: 40 } }))).to.deep.equal(['Davi', 'Eva']);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('accepts null values in conditions', () => {
|
|
80
|
-
expect(people.find({ name: null }).length).to.equal(0);
|
|
81
|
-
expect(people.find({ $or: [{ name: null }, { name: 'Ana' }] }).length).to.equal(1);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('findOne works with nested queries', () => {
|
|
85
|
-
const doc = people.findOne({ $and: [{ $or: [{ city: 'C' }, { city: 'D' }] }, { age: { $gt: 40 } }] });
|
|
86
|
-
expect(doc.name).to.equal('Carla');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('rejects invalid queries with a clear error', () => {
|
|
90
|
-
expect(() => people.find({ $or: { name: 'Ana' } })).to.throw('The $or operator must be an array');
|
|
91
|
-
expect(() => people.find({ age: { $between: [1, 2] } })).to.throw('Unknown operator $between');
|
|
92
|
-
expect(() => people.find({ $not: { age: 1 } })).to.throw('$not operator must be used inside a field condition');
|
|
93
|
-
});
|
|
94
|
-
});
|