@ackplus/nest-crud-request 0.0.1 → 0.0.6
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/LICENSE +21 -0
- package/README.md +130 -6
- package/eslint.config.cjs +22 -0
- package/jest.config.ts +10 -0
- package/package.json +14 -8
- package/project.json +9 -0
- package/src/index.js +7 -0
- package/src/index.js.map +1 -0
- package/src/index.ts +3 -0
- package/{lib → src/lib}/query-builder.d.ts +1 -1
- package/src/lib/query-builder.js +123 -0
- package/src/lib/query-builder.js.map +1 -0
- package/src/lib/query-builder.ts +139 -0
- package/src/lib/relation-builder.js +59 -0
- package/src/lib/relation-builder.js.map +1 -0
- package/src/lib/relation-builder.ts +64 -0
- package/src/lib/types.js +37 -0
- package/src/lib/types.js.map +1 -0
- package/src/lib/types.ts +48 -0
- package/src/lib/utils.js +15 -0
- package/src/lib/utils.js.map +1 -0
- package/src/lib/utils.ts +11 -0
- package/src/lib/where-builder.js +106 -0
- package/src/lib/where-builder.js.map +1 -0
- package/src/lib/where-builder.ts +124 -0
- package/src/test/query-builder-where.spec.ts +173 -0
- package/src/test/query-builder.spec.ts +149 -0
- package/src/test/relation-builder.spec.ts +32 -0
- package/tsconfig.json +21 -0
- package/tsconfig.lib.json +10 -0
- package/tsconfig.spec.json +15 -0
- package/vite.config.ts +46 -0
- package/index.js +0 -137
- /package/{index.d.ts → src/index.d.ts} +0 -0
- /package/{lib → src/lib}/relation-builder.d.ts +0 -0
- /package/{lib → src/lib}/types.d.ts +0 -0
- /package/{lib → src/lib}/utils.d.ts +0 -0
- /package/{lib → src/lib}/where-builder.d.ts +0 -0
package/src/lib/types.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OrderDirectionEnum = exports.WhereOperatorEnum = exports.WhereLogicalOperatorEnum = void 0;
|
|
4
|
+
var WhereLogicalOperatorEnum;
|
|
5
|
+
(function (WhereLogicalOperatorEnum) {
|
|
6
|
+
WhereLogicalOperatorEnum["AND"] = "$and";
|
|
7
|
+
WhereLogicalOperatorEnum["OR"] = "$or";
|
|
8
|
+
})(WhereLogicalOperatorEnum || (exports.WhereLogicalOperatorEnum = WhereLogicalOperatorEnum = {}));
|
|
9
|
+
var WhereOperatorEnum;
|
|
10
|
+
(function (WhereOperatorEnum) {
|
|
11
|
+
WhereOperatorEnum["EQ"] = "$eq";
|
|
12
|
+
WhereOperatorEnum["NOT_EQ"] = "$ne";
|
|
13
|
+
WhereOperatorEnum["GT"] = "$gt";
|
|
14
|
+
WhereOperatorEnum["GT_OR_EQ"] = "$gte";
|
|
15
|
+
WhereOperatorEnum["LT"] = "$lt";
|
|
16
|
+
WhereOperatorEnum["LT_OR_EQ"] = "$lte";
|
|
17
|
+
WhereOperatorEnum["IN"] = "$in";
|
|
18
|
+
WhereOperatorEnum["NOT_IN"] = "$notIn";
|
|
19
|
+
WhereOperatorEnum["LIKE"] = "$like";
|
|
20
|
+
WhereOperatorEnum["NOT_LIKE"] = "$notLike";
|
|
21
|
+
WhereOperatorEnum["ILIKE"] = "$iLike";
|
|
22
|
+
WhereOperatorEnum["NOT_ILIKE"] = "$notIlike";
|
|
23
|
+
WhereOperatorEnum["IS_NULL"] = "$isNull";
|
|
24
|
+
WhereOperatorEnum["IS_NOT_NULL"] = "$isNotNull";
|
|
25
|
+
WhereOperatorEnum["BETWEEN"] = "$between";
|
|
26
|
+
WhereOperatorEnum["NOT_BETWEEN"] = "$notBetween";
|
|
27
|
+
WhereOperatorEnum["NULL"] = "$null";
|
|
28
|
+
WhereOperatorEnum["NOT_NULL"] = "$notNull";
|
|
29
|
+
WhereOperatorEnum["IS_TRUE"] = "$isTrue";
|
|
30
|
+
WhereOperatorEnum["IS_FALSE"] = "$isFalse";
|
|
31
|
+
})(WhereOperatorEnum || (exports.WhereOperatorEnum = WhereOperatorEnum = {}));
|
|
32
|
+
var OrderDirectionEnum;
|
|
33
|
+
(function (OrderDirectionEnum) {
|
|
34
|
+
OrderDirectionEnum["ASC"] = "ASC";
|
|
35
|
+
OrderDirectionEnum["DESC"] = "DESC";
|
|
36
|
+
})(OrderDirectionEnum || (exports.OrderDirectionEnum = OrderDirectionEnum = {}));
|
|
37
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["types.ts"],"names":[],"mappings":";;;AASA,IAAY,wBAGX;AAHD,WAAY,wBAAwB;IAChC,wCAAY,CAAA;IACZ,sCAAU,CAAA;AACd,CAAC,EAHW,wBAAwB,wCAAxB,wBAAwB,QAGnC;AAED,IAAY,iBAqBX;AArBD,WAAY,iBAAiB;IACzB,+BAAU,CAAA;IACV,mCAAc,CAAA;IACd,+BAAU,CAAA;IACV,sCAAiB,CAAA;IACjB,+BAAU,CAAA;IACV,sCAAiB,CAAA;IACjB,+BAAU,CAAA;IACV,sCAAiB,CAAA;IACjB,mCAAc,CAAA;IACd,0CAAqB,CAAA;IACrB,qCAAgB,CAAA;IAChB,4CAAuB,CAAA;IACvB,wCAAmB,CAAA;IACnB,+CAA0B,CAAA;IAC1B,yCAAoB,CAAA;IACpB,gDAA2B,CAAA;IAC3B,mCAAc,CAAA;IACd,0CAAqB,CAAA;IACrB,wCAAmB,CAAA;IACnB,0CAAqB,CAAA;AACzB,CAAC,EArBW,iBAAiB,iCAAjB,iBAAiB,QAqB5B;AAED,IAAY,kBAGX;AAHD,WAAY,kBAAkB;IAC1B,iCAAW,CAAA;IACX,mCAAa,CAAA;AACjB,CAAC,EAHW,kBAAkB,kCAAlB,kBAAkB,QAG7B"}
|
package/src/lib/types.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface QueryBuilderOptions {
|
|
2
|
+
select?: string[];
|
|
3
|
+
relations?: string[] | Relation[];
|
|
4
|
+
where?: Record<string, any>;
|
|
5
|
+
order?: Record<string, OrderDirectionEnum>;
|
|
6
|
+
skip?: number;
|
|
7
|
+
take?: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export enum WhereLogicalOperatorEnum {
|
|
11
|
+
AND = '$and',
|
|
12
|
+
OR = '$or',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export enum WhereOperatorEnum {
|
|
16
|
+
EQ = '$eq',
|
|
17
|
+
NOT_EQ = '$ne',
|
|
18
|
+
GT = '$gt',
|
|
19
|
+
GT_OR_EQ = '$gte',
|
|
20
|
+
LT = '$lt',
|
|
21
|
+
LT_OR_EQ = '$lte',
|
|
22
|
+
IN = '$in',
|
|
23
|
+
NOT_IN = '$notIn',
|
|
24
|
+
LIKE = '$like',
|
|
25
|
+
NOT_LIKE = '$notLike',
|
|
26
|
+
ILIKE = '$iLike',
|
|
27
|
+
NOT_ILIKE = '$notIlike',
|
|
28
|
+
IS_NULL = '$isNull',
|
|
29
|
+
IS_NOT_NULL = '$isNotNull',
|
|
30
|
+
BETWEEN = '$between',
|
|
31
|
+
NOT_BETWEEN = '$notBetween',
|
|
32
|
+
NULL = '$null',
|
|
33
|
+
NOT_NULL = '$notNull',
|
|
34
|
+
IS_TRUE = '$isTrue',
|
|
35
|
+
IS_FALSE = '$isFalse',
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export enum OrderDirectionEnum {
|
|
39
|
+
ASC = 'ASC',
|
|
40
|
+
DESC = 'DESC',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
export interface Relation {
|
|
45
|
+
relation: string;
|
|
46
|
+
select?: string[];
|
|
47
|
+
where?: Record<string, any>;
|
|
48
|
+
}
|
package/src/lib/utils.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deepMerge = deepMerge;
|
|
4
|
+
function deepMerge(target, source) {
|
|
5
|
+
for (const key in source) {
|
|
6
|
+
if (source[key] instanceof Object && key in target) {
|
|
7
|
+
Object.assign(source[key], deepMerge(target[key], source[key]));
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
...target,
|
|
12
|
+
...source,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":";;AAAA,8BAUC;AAVD,SAAgB,SAAS,CAAC,MAAW,EAAE,MAAW;IAC9C,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,MAAM,CAAC,GAAG,CAAC,YAAY,MAAM,IAAI,GAAG,IAAI,MAAM,EAAE,CAAC;YACjD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpE,CAAC;IACL,CAAC;IACD,OAAO;QACH,GAAG,MAAM;QACT,GAAG,MAAM;KACZ,CAAC;AACN,CAAC"}
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function deepMerge(target: any, source: any): any {
|
|
2
|
+
for (const key in source) {
|
|
3
|
+
if (source[key] instanceof Object && key in target) {
|
|
4
|
+
Object.assign(source[key], deepMerge(target[key], source[key]));
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
return {
|
|
8
|
+
...target,
|
|
9
|
+
...source,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WhereBuilder = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
5
|
+
class WhereBuilder {
|
|
6
|
+
constructor(where) {
|
|
7
|
+
this.whereObject = {};
|
|
8
|
+
this.whereObject = where || {};
|
|
9
|
+
}
|
|
10
|
+
clear() {
|
|
11
|
+
this.whereObject = {};
|
|
12
|
+
return this;
|
|
13
|
+
}
|
|
14
|
+
where(...args) {
|
|
15
|
+
this.parseCondition(null, ...args);
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
andWhere(...args) {
|
|
19
|
+
this.parseCondition(types_1.WhereLogicalOperatorEnum.AND, ...args);
|
|
20
|
+
return this;
|
|
21
|
+
}
|
|
22
|
+
orWhere(...args) {
|
|
23
|
+
this.parseCondition(types_1.WhereLogicalOperatorEnum.OR, ...args);
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
removeWhere(field) {
|
|
27
|
+
const keys = field.split('.');
|
|
28
|
+
let current = this.whereObject;
|
|
29
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
30
|
+
if (!current[keys[i]]) {
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
current = current[keys[i]];
|
|
34
|
+
}
|
|
35
|
+
delete current[keys[keys.length - 1]];
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
hasConditions() {
|
|
39
|
+
return Object.keys(this.whereObject).length > 0;
|
|
40
|
+
}
|
|
41
|
+
toObject() {
|
|
42
|
+
return this.whereObject;
|
|
43
|
+
}
|
|
44
|
+
toJson() {
|
|
45
|
+
return JSON.stringify(this.whereObject);
|
|
46
|
+
}
|
|
47
|
+
parseCondition(type, ...args) {
|
|
48
|
+
let field;
|
|
49
|
+
let operator;
|
|
50
|
+
let value;
|
|
51
|
+
if (args.length === 0) {
|
|
52
|
+
}
|
|
53
|
+
else if (args.length === 1) {
|
|
54
|
+
const condition = args[0];
|
|
55
|
+
if (typeof condition === 'function') {
|
|
56
|
+
const builder = new WhereBuilder();
|
|
57
|
+
condition(builder);
|
|
58
|
+
this.updateCondition(builder.toObject(), type);
|
|
59
|
+
}
|
|
60
|
+
else if (condition instanceof WhereBuilder) {
|
|
61
|
+
this.updateCondition(condition.toObject(), type);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.updateCondition(condition, type);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (args.length === 2 || args.length === 3) {
|
|
68
|
+
if (args.length === 2) {
|
|
69
|
+
field = args[0];
|
|
70
|
+
value = args[1];
|
|
71
|
+
if (typeof value === 'object') {
|
|
72
|
+
const firstKey = Object.keys(value)[0];
|
|
73
|
+
if (firstKey.startsWith('$')) {
|
|
74
|
+
this.updateCondition({ [field]: value }, type);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
this.updateCondition({ [field]: { [types_1.WhereOperatorEnum.EQ]: value } }, type);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.updateCondition({ [field]: { [types_1.WhereOperatorEnum.EQ]: value } }, type);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
field = args[0];
|
|
86
|
+
operator = args[1];
|
|
87
|
+
value = args[2];
|
|
88
|
+
this.updateCondition({ [field]: { [operator]: value } }, type);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
updateCondition(condition, type) {
|
|
94
|
+
if (type === null) {
|
|
95
|
+
this.whereObject = {
|
|
96
|
+
...this.whereObject,
|
|
97
|
+
...condition,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
this.whereObject[type] = [...(this.whereObject[type] || []), condition].filter(Boolean);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.WhereBuilder = WhereBuilder;
|
|
106
|
+
//# sourceMappingURL=where-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"where-builder.js","sourceRoot":"","sources":["where-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { WhereLogicalOperatorEnum, WhereOperatorEnum } from './types';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export type WhereBuilderCondition = [string, any] | [string, WhereOperatorEnum, any] | [Record<string, any>] | [(builder: WhereBuilder) => void];
|
|
5
|
+
|
|
6
|
+
export class WhereBuilder {
|
|
7
|
+
|
|
8
|
+
private whereObject: Record<string, any> = {};
|
|
9
|
+
|
|
10
|
+
constructor(where?: Record<string, any>) {
|
|
11
|
+
this.whereObject = where || {};
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
clear(): this {
|
|
15
|
+
this.whereObject = {};
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
where(...args: WhereBuilderCondition): this {
|
|
20
|
+
this.parseCondition(null, ...args);
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
andWhere(...args: WhereBuilderCondition): this {
|
|
25
|
+
this.parseCondition(WhereLogicalOperatorEnum.AND, ...args);
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
orWhere(...args: WhereBuilderCondition): this {
|
|
30
|
+
this.parseCondition(WhereLogicalOperatorEnum.OR, ...args);
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
removeWhere(field: string): this {
|
|
35
|
+
const keys = field.split('.');
|
|
36
|
+
let current = this.whereObject;
|
|
37
|
+
|
|
38
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
39
|
+
if (!current[keys[i]]) {
|
|
40
|
+
return this; // If the path doesn't exist, do nothing
|
|
41
|
+
}
|
|
42
|
+
current = current[keys[i]];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
delete current[keys[keys.length - 1]];
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
hasConditions(): boolean {
|
|
50
|
+
return Object.keys(this.whereObject).length > 0;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
toObject(): Record<string, any> {
|
|
54
|
+
return this.whereObject;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
toJson(): string {
|
|
58
|
+
return JSON.stringify(this.whereObject);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private parseCondition(type: WhereLogicalOperatorEnum | null, ...args: any[]): Record<string, any> {
|
|
62
|
+
let field: string;
|
|
63
|
+
let operator: WhereOperatorEnum;
|
|
64
|
+
let value: any;
|
|
65
|
+
|
|
66
|
+
if (args.length === 0) {
|
|
67
|
+
// Do nothing
|
|
68
|
+
} else if (args.length === 1) {
|
|
69
|
+
const condition = args[0];
|
|
70
|
+
if (typeof condition === 'function') {
|
|
71
|
+
// If the condition is a function, create a new WhereBuilder and call the function with it
|
|
72
|
+
const builder = new WhereBuilder();
|
|
73
|
+
condition(builder);
|
|
74
|
+
this.updateCondition(builder.toObject(), type);
|
|
75
|
+
} else if (condition instanceof WhereBuilder) {
|
|
76
|
+
// If the condition is a WhereBuilder
|
|
77
|
+
this.updateCondition(condition.toObject(), type);
|
|
78
|
+
} else {
|
|
79
|
+
// If the condition is a simple object
|
|
80
|
+
this.updateCondition(condition, type);
|
|
81
|
+
}
|
|
82
|
+
} else if (args.length === 2 || args.length === 3) {
|
|
83
|
+
if (args.length === 2) {
|
|
84
|
+
// if there are only two arguments, the operator is EQ
|
|
85
|
+
field = args[0];
|
|
86
|
+
value = args[1];
|
|
87
|
+
if (typeof value === 'object') {
|
|
88
|
+
const firstKey = Object.keys(value)[0];
|
|
89
|
+
// if the first key is a operator, update the value with the operator
|
|
90
|
+
if (firstKey.startsWith('$')) {
|
|
91
|
+
this.updateCondition({ [field]: value }, type);
|
|
92
|
+
} else {
|
|
93
|
+
this.updateCondition({ [field]: { [WhereOperatorEnum.EQ]: value } }, type);
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
this.updateCondition({ [field]: { [WhereOperatorEnum.EQ]: value } }, type);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
// if there are three arguments, the operator is the second argument
|
|
100
|
+
field = args[0];
|
|
101
|
+
operator = args[1];
|
|
102
|
+
value = args[2];
|
|
103
|
+
this.updateCondition({ [field]: { [operator]: value } }, type);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return this;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// private updateCondition(condition: Record<string, any>, type: '$and' | '$or'): void {
|
|
110
|
+
// this.whereObject[type] = [...(this.whereObject[type] || []), condition].filter(Boolean);
|
|
111
|
+
// }
|
|
112
|
+
|
|
113
|
+
private updateCondition(condition: Record<string, any>, type: WhereLogicalOperatorEnum | null): void {
|
|
114
|
+
if (type === null) {
|
|
115
|
+
this.whereObject = {
|
|
116
|
+
...this.whereObject,
|
|
117
|
+
...condition,
|
|
118
|
+
};
|
|
119
|
+
} else {
|
|
120
|
+
this.whereObject[type] = [...(this.whereObject[type] || []), condition].filter(Boolean);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { QueryBuilder } from '../lib/query-builder';
|
|
2
|
+
import { WhereOperatorEnum } from '../lib/types';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
describe('QueryBuilder - Where Conditions', () => {
|
|
6
|
+
let queryBuilder: QueryBuilder;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
queryBuilder = new QueryBuilder({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should add where conditions', () => {
|
|
13
|
+
queryBuilder.where('age', 25);
|
|
14
|
+
expect(queryBuilder.toObject().where).toEqual({ age: { $eq: 25 } });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should add or where conditions', () => {
|
|
18
|
+
queryBuilder.orWhere('age', 25);
|
|
19
|
+
expect(queryBuilder.toObject().where).toEqual({ $or: [{ age: { $eq: 25 } }] });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should add where conditions with a function', () => {
|
|
23
|
+
queryBuilder.where((builder) => {
|
|
24
|
+
builder.where('age', 25);
|
|
25
|
+
builder.orWhere('name', 'John');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
29
|
+
age: { $eq: 25 },
|
|
30
|
+
$or: [{ name: { $eq: 'John' } }],
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should add or where conditions with a function', () => {
|
|
35
|
+
queryBuilder.orWhere((builder) => {
|
|
36
|
+
builder.where('age', 25);
|
|
37
|
+
builder.orWhere('name', 'John');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
41
|
+
$or: [
|
|
42
|
+
{
|
|
43
|
+
age: { $eq: 25 },
|
|
44
|
+
$or: [{ name: { $eq: 'John' } }],
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should handle complex where conditions with multiple nested functions', () => {
|
|
51
|
+
queryBuilder.where((builder) => {
|
|
52
|
+
builder.where('age', 25);
|
|
53
|
+
builder.orWhere((innerBuilder) => {
|
|
54
|
+
innerBuilder.where('name', 'John');
|
|
55
|
+
innerBuilder.where('status', 'active');
|
|
56
|
+
});
|
|
57
|
+
builder.where('country', 'USA');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
61
|
+
age: { $eq: 25 },
|
|
62
|
+
$or: [
|
|
63
|
+
{
|
|
64
|
+
name: { $eq: 'John' },
|
|
65
|
+
status: { $eq: 'active' },
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
country: { $eq: 'USA' },
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('should handle complex or where conditions with multiple nested functions', () => {
|
|
73
|
+
queryBuilder.orWhere((builder) => {
|
|
74
|
+
builder.where('age', 25);
|
|
75
|
+
builder.orWhere((innerBuilder) => {
|
|
76
|
+
innerBuilder.where('name', 'John');
|
|
77
|
+
innerBuilder.orWhere('status', 'active');
|
|
78
|
+
});
|
|
79
|
+
builder.orWhere('country', 'USA');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
83
|
+
$or: [
|
|
84
|
+
{
|
|
85
|
+
age: { $eq: 25 },
|
|
86
|
+
$or: [
|
|
87
|
+
{
|
|
88
|
+
name: { $eq: 'John' },
|
|
89
|
+
$or: [{ status: { $eq: 'active' } }],
|
|
90
|
+
},
|
|
91
|
+
{ country: { $eq: 'USA' } },
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should handle empty where conditions', () => {
|
|
99
|
+
queryBuilder.where(() => {
|
|
100
|
+
// Do nothing
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(queryBuilder.toObject().where).toEqual(undefined);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should handle single condition without nesting', () => {
|
|
107
|
+
queryBuilder.where('age', 30);
|
|
108
|
+
|
|
109
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
110
|
+
age: { $eq: 30 },
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should handle multiple conditions with different operators', () => {
|
|
115
|
+
queryBuilder.where((builder) => {
|
|
116
|
+
builder.where('age', 30);
|
|
117
|
+
builder.where('salary', WhereOperatorEnum.GT, 50000);
|
|
118
|
+
builder.orWhere('status', 'employed');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
122
|
+
age: { $eq: 30 },
|
|
123
|
+
salary: { $gt: 50000 },
|
|
124
|
+
$or: [{ status: { $eq: 'employed' } }],
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should handle nested orWhere with multiple conditions', () => {
|
|
129
|
+
queryBuilder.orWhere((builder) => {
|
|
130
|
+
builder.where('age', 30);
|
|
131
|
+
builder.orWhere((innerBuilder) => {
|
|
132
|
+
innerBuilder.where('name', 'Alice');
|
|
133
|
+
innerBuilder.orWhere('city', 'New York');
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
138
|
+
$or: [
|
|
139
|
+
{
|
|
140
|
+
age: { $eq: 30 },
|
|
141
|
+
$or: [
|
|
142
|
+
{
|
|
143
|
+
name: { $eq: 'Alice' },
|
|
144
|
+
$or: [{ city: { $eq: 'New York' } }],
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should handle complex conditions with mixed operators', () => {
|
|
153
|
+
queryBuilder.where((builder) => {
|
|
154
|
+
builder.where('age', { $gte: 18 });
|
|
155
|
+
builder.orWhere((innerBuilder) => {
|
|
156
|
+
innerBuilder.where('name', 'Bob');
|
|
157
|
+
innerBuilder.where('status', { $ne: 'inactive' });
|
|
158
|
+
});
|
|
159
|
+
builder.where('country', { $in: ['USA', 'Canada'] });
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
163
|
+
age: { $gte: 18 },
|
|
164
|
+
$or: [
|
|
165
|
+
{
|
|
166
|
+
name: { $eq: 'Bob' },
|
|
167
|
+
status: { $ne: 'inactive' },
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
country: { $in: ['USA', 'Canada'] },
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { QueryBuilder } from '../lib/query-builder';
|
|
2
|
+
import { OrderDirectionEnum } from '../lib/types';
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
describe('QueryBuilder', () => {
|
|
6
|
+
let queryBuilder: QueryBuilder;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
queryBuilder = new QueryBuilder({});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should set options', () => {
|
|
13
|
+
const options = { select: ['name', 'age'] };
|
|
14
|
+
queryBuilder.setOptions(options);
|
|
15
|
+
expect(queryBuilder.toObject().select).toEqual(options.select);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('should merge options shallowly', () => {
|
|
19
|
+
queryBuilder.setOptions({ select: ['name'] });
|
|
20
|
+
queryBuilder.mergeOptions({ select: ['age'] });
|
|
21
|
+
expect(queryBuilder.toObject().select).toEqual(['age']);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should merge options deeply', () => {
|
|
25
|
+
queryBuilder.setOptions({ where: { age: { $gt: 18 } } });
|
|
26
|
+
queryBuilder.mergeOptions({ where: { name: { $eq: 'John' } } }, true);
|
|
27
|
+
expect(queryBuilder.toObject().where).toEqual({
|
|
28
|
+
age: { $gt: 18 },
|
|
29
|
+
name: { $eq: 'John' },
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should add select fields', () => {
|
|
34
|
+
queryBuilder.addSelect('name');
|
|
35
|
+
queryBuilder.addSelect(['age', 'email']);
|
|
36
|
+
expect(queryBuilder.toObject().select).toEqual([
|
|
37
|
+
'name',
|
|
38
|
+
'age',
|
|
39
|
+
'email',
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('should remove select fields', () => {
|
|
44
|
+
queryBuilder.addSelect([
|
|
45
|
+
'name',
|
|
46
|
+
'age',
|
|
47
|
+
'email',
|
|
48
|
+
]);
|
|
49
|
+
queryBuilder.removeSelect('age');
|
|
50
|
+
expect(queryBuilder.toObject().select).toEqual(['name', 'email']);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should add and remove relations as array of objects', () => {
|
|
54
|
+
queryBuilder.addRelation('profile', ['id', 'bio']);
|
|
55
|
+
queryBuilder.addRelation('posts', ['title', 'content']);
|
|
56
|
+
|
|
57
|
+
const relations = queryBuilder.toObject().relations;
|
|
58
|
+
expect(relations).toEqual(
|
|
59
|
+
expect.arrayContaining([
|
|
60
|
+
expect.objectContaining({
|
|
61
|
+
relation: 'profile',
|
|
62
|
+
select: ['id', 'bio'],
|
|
63
|
+
}),
|
|
64
|
+
expect.objectContaining({
|
|
65
|
+
relation: 'posts',
|
|
66
|
+
select: ['title', 'content'],
|
|
67
|
+
}),
|
|
68
|
+
]),
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
queryBuilder.removeRelation('profile');
|
|
72
|
+
const updatedRelations = queryBuilder.toObject().relations;
|
|
73
|
+
expect(updatedRelations).toEqual(
|
|
74
|
+
expect.arrayContaining([
|
|
75
|
+
expect.objectContaining({
|
|
76
|
+
relation: 'posts',
|
|
77
|
+
select: ['title', 'content'],
|
|
78
|
+
}),
|
|
79
|
+
]),
|
|
80
|
+
);
|
|
81
|
+
expect(updatedRelations).not.toEqual(
|
|
82
|
+
expect.arrayContaining([expect.objectContaining({ relation: 'profile' })]),
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should handle null or empty relations', () => {
|
|
87
|
+
queryBuilder.addRelation('profile');
|
|
88
|
+
expect(queryBuilder.toObject().relations).not.toEqual(undefined);
|
|
89
|
+
|
|
90
|
+
queryBuilder.addRelation('profile');
|
|
91
|
+
queryBuilder.removeRelation('profile');
|
|
92
|
+
expect(queryBuilder.toObject().relations).toEqual(undefined);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should add and remove relations as array of strings', () => {
|
|
96
|
+
queryBuilder.addRelation('profile');
|
|
97
|
+
queryBuilder.addRelation('posts');
|
|
98
|
+
|
|
99
|
+
const relations = queryBuilder.toObject().relations;
|
|
100
|
+
expect(relations).toEqual(
|
|
101
|
+
expect.arrayContaining([expect.objectContaining({ relation: 'profile' }), expect.objectContaining({ relation: 'posts' })]),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
queryBuilder.removeRelation('profile');
|
|
105
|
+
const updatedRelations = queryBuilder.toObject().relations;
|
|
106
|
+
expect(updatedRelations).toEqual(
|
|
107
|
+
expect.arrayContaining([expect.objectContaining({ relation: 'posts' })]),
|
|
108
|
+
);
|
|
109
|
+
expect(updatedRelations).not.toEqual(
|
|
110
|
+
expect.arrayContaining([expect.objectContaining({ relation: 'profile' })]),
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should add where conditions', () => {
|
|
115
|
+
queryBuilder.where('age', 25);
|
|
116
|
+
expect(queryBuilder.toObject().where).toEqual({ age: { $eq: 25 } });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('should handle empty where conditions', () => {
|
|
120
|
+
queryBuilder.where(() => {
|
|
121
|
+
// Do nothing
|
|
122
|
+
});
|
|
123
|
+
expect(queryBuilder.toObject().where).toEqual(undefined);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should add order', () => {
|
|
127
|
+
queryBuilder.addOrder('name', OrderDirectionEnum.ASC);
|
|
128
|
+
expect(queryBuilder.toObject().order).toEqual({ name: OrderDirectionEnum.ASC });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('should remove order', () => {
|
|
132
|
+
queryBuilder.addOrder('name', OrderDirectionEnum.ASC);
|
|
133
|
+
queryBuilder.removeOrder('name');
|
|
134
|
+
expect(queryBuilder.toObject().order).toEqual({});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should set skip and take', () => {
|
|
138
|
+
queryBuilder.setSkip(10);
|
|
139
|
+
queryBuilder.setTake(5);
|
|
140
|
+
expect(queryBuilder.toObject().skip).toBe(10);
|
|
141
|
+
expect(queryBuilder.toObject().take).toBe(5);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should convert to JSON', () => {
|
|
145
|
+
queryBuilder.setOptions({ select: ['name'] });
|
|
146
|
+
const json = queryBuilder.toJson();
|
|
147
|
+
expect(json).toBe(JSON.stringify(queryBuilder.toObject()));
|
|
148
|
+
});
|
|
149
|
+
});
|