@e22m4u/js-repository-mongodb-adapter 0.0.19 → 0.0.21
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/package.json +2 -2
- package/src/mongodb-adapter.js +17 -8
- package/src/mongodb-adapter.spec.js +201 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@e22m4u/js-repository-mongodb-adapter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.21",
|
|
4
4
|
"description": "MongoDB адаптер для @e22m4u/js-repository",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@e22m4u/js-format": "*",
|
|
40
40
|
"@e22m4u/js-service": "*",
|
|
41
|
-
"@e22m4u/js-repository": "~0.0.
|
|
41
|
+
"@e22m4u/js-repository": "~0.0.36"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@commitlint/cli": "^17.7.1",
|
package/src/mongodb-adapter.js
CHANGED
|
@@ -364,15 +364,24 @@ export class MongodbAdapter extends Adapter {
|
|
|
364
364
|
* @private
|
|
365
365
|
*/
|
|
366
366
|
_buildQuery(modelName, clause) {
|
|
367
|
+
if (clause == null) return;
|
|
368
|
+
if (typeof clause !== 'object' || Array.isArray(clause))
|
|
369
|
+
throw new InvalidArgumentError(
|
|
370
|
+
'The provided option "where" should be an Object, but %v given.',
|
|
371
|
+
clause,
|
|
372
|
+
);
|
|
367
373
|
const query = {};
|
|
368
|
-
if (!clause || typeof clause !== 'object') return query;
|
|
369
374
|
const idPropName = this._getIdPropName(modelName);
|
|
370
375
|
Object.keys(clause).forEach(key => {
|
|
371
376
|
let cond = clause[key];
|
|
372
377
|
// and/or/nor clause
|
|
373
378
|
if (key === 'and' || key === 'or' || key === 'nor') {
|
|
374
|
-
if (
|
|
375
|
-
|
|
379
|
+
if (cond == null) return;
|
|
380
|
+
if (!Array.isArray(cond))
|
|
381
|
+
throw new InvalidOperatorValueError(key, 'an Array', cond);
|
|
382
|
+
if (cond.length === 0) return;
|
|
383
|
+
cond = cond.map(c => this._buildQuery(modelName, c));
|
|
384
|
+
cond = cond.filter(c => c != null);
|
|
376
385
|
query['$' + key] = cond;
|
|
377
386
|
return;
|
|
378
387
|
}
|
|
@@ -527,7 +536,7 @@ export class MongodbAdapter extends Adapter {
|
|
|
527
536
|
// unknown
|
|
528
537
|
query[key] = cond;
|
|
529
538
|
});
|
|
530
|
-
return query;
|
|
539
|
+
return Object.keys(query).length ? query : undefined;
|
|
531
540
|
}
|
|
532
541
|
|
|
533
542
|
/**
|
|
@@ -579,8 +588,8 @@ export class MongodbAdapter extends Adapter {
|
|
|
579
588
|
modelData[idPropName] = id;
|
|
580
589
|
const tableData = this._toDatabase(modelName, modelData);
|
|
581
590
|
const table = this._getCollection(modelName);
|
|
582
|
-
const {
|
|
583
|
-
if (
|
|
591
|
+
const {matchedCount} = await table.replaceOne({_id: id}, tableData);
|
|
592
|
+
if (matchedCount < 1)
|
|
584
593
|
throw new InvalidArgumentError('Identifier %v is not found.', String(id));
|
|
585
594
|
const projection = this._buildProjection(
|
|
586
595
|
modelName,
|
|
@@ -605,8 +614,8 @@ export class MongodbAdapter extends Adapter {
|
|
|
605
614
|
delete modelData[idPropName];
|
|
606
615
|
const tableData = this._toDatabase(modelName, modelData);
|
|
607
616
|
const table = this._getCollection(modelName);
|
|
608
|
-
const {
|
|
609
|
-
if (
|
|
617
|
+
const {matchedCount} = await table.updateOne({_id: id}, {$set: tableData});
|
|
618
|
+
if (matchedCount < 1)
|
|
610
619
|
throw new InvalidArgumentError('Identifier %v is not found.', String(id));
|
|
611
620
|
const projection = this._buildProjection(
|
|
612
621
|
modelName,
|
|
@@ -7,6 +7,7 @@ import {DataType} from '@e22m4u/js-repository';
|
|
|
7
7
|
import {createMongodbUrl} from './utils/index.js';
|
|
8
8
|
import {MongodbAdapter} from './mongodb-adapter.js';
|
|
9
9
|
import {AdapterRegistry} from '@e22m4u/js-repository';
|
|
10
|
+
import {InvalidOperatorValueError} from '@e22m4u/js-repository';
|
|
10
11
|
import {DEFAULT_PRIMARY_KEY_PROPERTY_NAME as DEF_PK} from '@e22m4u/js-repository';
|
|
11
12
|
|
|
12
13
|
const CONFIG = {
|
|
@@ -274,18 +275,34 @@ describe('MongodbAdapter', function () {
|
|
|
274
275
|
expect(throwable(null)()).to.be.undefined;
|
|
275
276
|
});
|
|
276
277
|
|
|
277
|
-
it('
|
|
278
|
+
it('uses ascending direction by default', async function () {
|
|
278
279
|
const schema = createSchema();
|
|
279
280
|
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
280
281
|
const A = await schema
|
|
281
282
|
.getService(AdapterRegistry)
|
|
282
283
|
.getAdapter('mongodb');
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
284
|
+
const res = A._buildSort('model', 'foo');
|
|
285
|
+
expect(res).to.be.eql({foo: 1});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('uses descending direction by the "DESC" flag', async function () {
|
|
289
|
+
const schema = createSchema();
|
|
290
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
291
|
+
const A = await schema
|
|
292
|
+
.getService(AdapterRegistry)
|
|
293
|
+
.getAdapter('mongodb');
|
|
294
|
+
const res = A._buildSort('model', 'foo DESC');
|
|
295
|
+
expect(res).to.be.eql({foo: -1});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('uses ascending direction by the "ASC" flag', async function () {
|
|
299
|
+
const schema = createSchema();
|
|
300
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
301
|
+
const A = await schema
|
|
302
|
+
.getService(AdapterRegistry)
|
|
303
|
+
.getAdapter('mongodb');
|
|
304
|
+
const res = A._buildSort('model', 'foo ASC');
|
|
305
|
+
expect(res).to.be.eql({foo: 1});
|
|
289
306
|
});
|
|
290
307
|
|
|
291
308
|
it('converts the given property name to the column name', async function () {
|
|
@@ -348,18 +365,44 @@ describe('MongodbAdapter', function () {
|
|
|
348
365
|
expect(throwable(['bar', 'baz'])()).to.be.eql({bar: 1, baz: 1});
|
|
349
366
|
});
|
|
350
367
|
|
|
351
|
-
it('
|
|
368
|
+
it('uses ascending direction by default', async function () {
|
|
369
|
+
const schema = createSchema();
|
|
370
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
371
|
+
const A = await schema
|
|
372
|
+
.getService(AdapterRegistry)
|
|
373
|
+
.getAdapter('mongodb');
|
|
374
|
+
const res = A._buildSort('model', ['foo', 'bar']);
|
|
375
|
+
expect(res).to.be.eql({foo: 1, bar: 1});
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it('uses descending direction by the "DESC" flag', async function () {
|
|
379
|
+
const schema = createSchema();
|
|
380
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
381
|
+
const A = await schema
|
|
382
|
+
.getService(AdapterRegistry)
|
|
383
|
+
.getAdapter('mongodb');
|
|
384
|
+
const res = A._buildSort('model', ['foo DESC', 'bar DESC']);
|
|
385
|
+
expect(res).to.be.eql({foo: -1, bar: -1});
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('uses ascending direction by the "ASC" flag', async function () {
|
|
352
389
|
const schema = createSchema();
|
|
353
390
|
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
354
391
|
const A = await schema
|
|
355
392
|
.getService(AdapterRegistry)
|
|
356
393
|
.getAdapter('mongodb');
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
394
|
+
const res = A._buildSort('model', ['foo ASC', 'bar ASC']);
|
|
395
|
+
expect(res).to.be.eql({foo: 1, bar: 1});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('uses multiple directions by the multiple fields', async function () {
|
|
399
|
+
const schema = createSchema();
|
|
400
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
401
|
+
const A = await schema
|
|
402
|
+
.getService(AdapterRegistry)
|
|
403
|
+
.getAdapter('mongodb');
|
|
404
|
+
const res = A._buildSort('model', ['foo', 'bar DESC', 'baz ASC']);
|
|
405
|
+
expect(res).to.be.eql({foo: 1, bar: -1, baz: 1});
|
|
363
406
|
});
|
|
364
407
|
|
|
365
408
|
it('converts the given property names to column names', async function () {
|
|
@@ -391,6 +434,120 @@ describe('MongodbAdapter', function () {
|
|
|
391
434
|
});
|
|
392
435
|
});
|
|
393
436
|
|
|
437
|
+
describe('_buildQuery', function () {
|
|
438
|
+
it('requires the second argument to be an object', async function () {
|
|
439
|
+
const schema = createSchema();
|
|
440
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
441
|
+
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
|
|
442
|
+
const throwable = v => () => A._buildQuery('model', v);
|
|
443
|
+
const error = v =>
|
|
444
|
+
format(
|
|
445
|
+
'The provided option "where" should be an Object, but %s given.',
|
|
446
|
+
v,
|
|
447
|
+
);
|
|
448
|
+
expect(throwable('str')).to.throw(error('"str"'));
|
|
449
|
+
expect(throwable('')).to.throw(error('""'));
|
|
450
|
+
expect(throwable(10)).to.throw(error('10'));
|
|
451
|
+
expect(throwable(0)).to.throw(error('0'));
|
|
452
|
+
expect(throwable(true)).to.throw(error('true'));
|
|
453
|
+
expect(throwable(false)).to.throw(error('false'));
|
|
454
|
+
expect(throwable({foo: 'bar'})()).to.be.eql({foo: 'bar'});
|
|
455
|
+
expect(throwable({})()).to.be.undefined;
|
|
456
|
+
expect(throwable(undefined)()).to.be.undefined;
|
|
457
|
+
expect(throwable(null)()).to.be.undefined;
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
it('converts the property names to column names', async function () {
|
|
461
|
+
const schema = createSchema();
|
|
462
|
+
schema.defineModel({
|
|
463
|
+
name: 'model',
|
|
464
|
+
datasource: 'mongodb',
|
|
465
|
+
properties: {
|
|
466
|
+
foo: {
|
|
467
|
+
type: DataType.STRING,
|
|
468
|
+
columnName: 'bar',
|
|
469
|
+
},
|
|
470
|
+
baz: {
|
|
471
|
+
type: DataType.STRING,
|
|
472
|
+
columnName: 'qux',
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
});
|
|
476
|
+
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
|
|
477
|
+
const res = A._buildQuery('model', {foo: 'a1', baz: null});
|
|
478
|
+
expect(res).to.be.eql({bar: 'a1', qux: null});
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
it('converts strings of the ObjectId to instances', async function () {
|
|
482
|
+
const schema = createSchema();
|
|
483
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
484
|
+
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
|
|
485
|
+
const oid1 = new ObjectId();
|
|
486
|
+
const oid2 = new ObjectId();
|
|
487
|
+
const id1 = String(oid1);
|
|
488
|
+
const id2 = String(oid2);
|
|
489
|
+
const res = A._buildQuery('model', {foo: id1, bar: id2});
|
|
490
|
+
expect(res.foo).to.be.instanceof(ObjectId);
|
|
491
|
+
expect(res.bar).to.be.instanceof(ObjectId);
|
|
492
|
+
expect(res.foo).to.be.eql(oid1);
|
|
493
|
+
expect(res.bar).to.be.eql(oid2);
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it('adds "$" prefix to the "and", "or" and "nor" operator keys', async function () {
|
|
497
|
+
const input = {
|
|
498
|
+
and: [{foo: 'a1'}],
|
|
499
|
+
or: [{foo: 'a2'}],
|
|
500
|
+
nor: [{foo: 'a3'}],
|
|
501
|
+
};
|
|
502
|
+
const schema = createSchema();
|
|
503
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
504
|
+
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
|
|
505
|
+
const res = A._buildQuery('model', input);
|
|
506
|
+
expect(res).to.be.eql({
|
|
507
|
+
$and: [{foo: 'a1'}],
|
|
508
|
+
$or: [{foo: 'a2'}],
|
|
509
|
+
$nor: [{foo: 'a3'}],
|
|
510
|
+
});
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
it('does not include an empty array of "and", "or" and "nor" operators', async function () {
|
|
514
|
+
const input1 = {foo: 'a1', and: [], or: [], nor: []};
|
|
515
|
+
const input2 = {foo: 'a2', and: undefined, or: undefined, nor: undefined};
|
|
516
|
+
const input3 = {foo: 'a3', and: null, or: null, nor: null};
|
|
517
|
+
const schema = createSchema();
|
|
518
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
519
|
+
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
|
|
520
|
+
const res1 = A._buildQuery('model', input1);
|
|
521
|
+
const res2 = A._buildQuery('model', input2);
|
|
522
|
+
const res3 = A._buildQuery('model', input3);
|
|
523
|
+
expect(res1).to.be.eql({foo: 'a1'});
|
|
524
|
+
expect(res2).to.be.eql({foo: 'a2'});
|
|
525
|
+
expect(res3).to.be.eql({foo: 'a3'});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('operators "and", "or" and "nor" are require an array of objects', async function () {
|
|
529
|
+
const schema = createSchema();
|
|
530
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
531
|
+
const A = await schema.getService(AdapterRegistry).getAdapter('mongodb');
|
|
532
|
+
const throwable = (k, v) => () => A._buildQuery('model', {[k]: v});
|
|
533
|
+
const error = (k, v) => {
|
|
534
|
+
const e = new InvalidOperatorValueError(k, 'an Array', v);
|
|
535
|
+
return e.message;
|
|
536
|
+
};
|
|
537
|
+
const testOf = v => {
|
|
538
|
+
expect(throwable('and', v)).to.throw(error('and', v));
|
|
539
|
+
expect(throwable('or', v)).to.throw(error('or', v));
|
|
540
|
+
expect(throwable('nor', v)).to.throw(error('nor', v));
|
|
541
|
+
};
|
|
542
|
+
testOf('str');
|
|
543
|
+
testOf('');
|
|
544
|
+
testOf(10);
|
|
545
|
+
testOf(0);
|
|
546
|
+
testOf(true);
|
|
547
|
+
testOf(false);
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
394
551
|
describe('create', function () {
|
|
395
552
|
it('generates a new identifier when a value of a primary key is not provided', async function () {
|
|
396
553
|
const schema = createSchema();
|
|
@@ -1299,6 +1456,21 @@ describe('MongodbAdapter', function () {
|
|
|
1299
1456
|
.findOne({_id: oid});
|
|
1300
1457
|
expect(rawData).to.be.eql({_id: oid, fooCol: 15, barCol: 25, bazCol: 35});
|
|
1301
1458
|
});
|
|
1459
|
+
|
|
1460
|
+
it('does not throws an error if nothing changed', async function () {
|
|
1461
|
+
const schema = createSchema();
|
|
1462
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
1463
|
+
const rep = schema.getRepository('model');
|
|
1464
|
+
const created = await rep.create({foo: 10});
|
|
1465
|
+
const id = created[DEF_PK];
|
|
1466
|
+
const replaced = await rep.replaceById(id, {foo: 10});
|
|
1467
|
+
expect(replaced).to.be.eql({[DEF_PK]: id, foo: 10});
|
|
1468
|
+
const oid = new ObjectId(id);
|
|
1469
|
+
const rawData = await MDB_CLIENT.db()
|
|
1470
|
+
.collection('model')
|
|
1471
|
+
.findOne({_id: oid});
|
|
1472
|
+
expect(rawData).to.be.eql({_id: oid, foo: 10});
|
|
1473
|
+
});
|
|
1302
1474
|
});
|
|
1303
1475
|
|
|
1304
1476
|
describe('patchById', function () {
|
|
@@ -1651,6 +1823,21 @@ describe('MongodbAdapter', function () {
|
|
|
1651
1823
|
.findOne({_id: oid});
|
|
1652
1824
|
expect(rawData).to.be.eql({_id: oid, fooCol: 15, barCol: 25, bazCol: 35});
|
|
1653
1825
|
});
|
|
1826
|
+
|
|
1827
|
+
it('does not throws an error if nothing changed', async function () {
|
|
1828
|
+
const schema = createSchema();
|
|
1829
|
+
schema.defineModel({name: 'model', datasource: 'mongodb'});
|
|
1830
|
+
const rep = schema.getRepository('model');
|
|
1831
|
+
const created = await rep.create({foo: 10});
|
|
1832
|
+
const id = created[DEF_PK];
|
|
1833
|
+
const patched = await rep.patchById(id, {foo: 10});
|
|
1834
|
+
expect(patched).to.be.eql({[DEF_PK]: id, foo: 10});
|
|
1835
|
+
const oid = new ObjectId(id);
|
|
1836
|
+
const rawData = await MDB_CLIENT.db()
|
|
1837
|
+
.collection('model')
|
|
1838
|
+
.findOne({_id: oid});
|
|
1839
|
+
expect(rawData).to.be.eql({_id: oid, foo: 10});
|
|
1840
|
+
});
|
|
1654
1841
|
});
|
|
1655
1842
|
|
|
1656
1843
|
describe('find', function () {
|