@intellegens/cornerstone-client 0.0.9999-alpha-34 → 0.0.9999-alpha-35
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/dist/data/api/dto/read/ReadSelectedSearchDefinitionBuilder.d.ts +167 -0
- package/dist/data/api/dto/read/ReadSelectedSearchDefinitionBuilder.js +267 -0
- package/dist/data/api/dto/read/index.d.ts +1 -0
- package/dist/data/api/dto/read/index.js +1 -0
- package/package.json +1 -1
- package/src/data/api/dto/read/ReadSelectedSearchDefinitionBuilder.ts +348 -0
- package/src/data/api/dto/read/index.ts +1 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { ReadSelectedComparisonOperator, ReadSelectedPropertyType } from '../../..';
|
|
2
|
+
import { ReadSelectedSearchDefinitionDto } from './ReadSelectedSearchDefinitionDto';
|
|
3
|
+
/**
|
|
4
|
+
* Builder class for constructing ReadSelectedSearchDefinitionDto with a fluent API.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Simple AND search with property criteria
|
|
8
|
+
* const search = SearchBuilder.and<User>()
|
|
9
|
+
* .whereString('name', ReadSelectedComparisonOperator.IContains, 'John')
|
|
10
|
+
* .whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
11
|
+
* .build();
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // OR search with nested criteria (specify nested type for intellisense)
|
|
15
|
+
* const search = SearchBuilder.or<User>()
|
|
16
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'active')
|
|
17
|
+
* .nested<Address>('address', nested =>
|
|
18
|
+
* nested.whereString('city', ReadSelectedComparisonOperator.Equal, 'NYC')
|
|
19
|
+
* )
|
|
20
|
+
* .build();
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Complex nested search with collection criteria
|
|
24
|
+
* const search = SearchBuilder.and<Order>()
|
|
25
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'pending')
|
|
26
|
+
* .any<OrderItem>('items', itemSearch =>
|
|
27
|
+
* itemSearch.whereNumber('quantity', ReadSelectedComparisonOperator.GreaterThan, 5)
|
|
28
|
+
* )
|
|
29
|
+
* .group(sub => sub.or()
|
|
30
|
+
* .whereString('priority', ReadSelectedComparisonOperator.Equal, 'high')
|
|
31
|
+
* .whereString('priority', ReadSelectedComparisonOperator.Equal, 'urgent')
|
|
32
|
+
* )
|
|
33
|
+
* .build();
|
|
34
|
+
*/
|
|
35
|
+
export declare class SearchBuilder<T> {
|
|
36
|
+
private _logicalOperator;
|
|
37
|
+
private _searches;
|
|
38
|
+
private _propertyCriteria;
|
|
39
|
+
private _nestedCriteria;
|
|
40
|
+
private _nestedCollectionCriteria;
|
|
41
|
+
private constructor();
|
|
42
|
+
/**
|
|
43
|
+
* Creates a new builder with AND logical operator.
|
|
44
|
+
* All criteria added to this builder will be combined with AND.
|
|
45
|
+
*/
|
|
46
|
+
static and<T>(): SearchBuilder<T>;
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new builder with OR logical operator.
|
|
49
|
+
* All criteria added to this builder will be combined with OR.
|
|
50
|
+
*/
|
|
51
|
+
static or<T>(): SearchBuilder<T>;
|
|
52
|
+
/**
|
|
53
|
+
* Sets the logical operator to AND.
|
|
54
|
+
*/
|
|
55
|
+
and(): this;
|
|
56
|
+
/**
|
|
57
|
+
* Sets the logical operator to OR.
|
|
58
|
+
*/
|
|
59
|
+
or(): this;
|
|
60
|
+
/**
|
|
61
|
+
* Adds a property comparison criterion.
|
|
62
|
+
*
|
|
63
|
+
* @param propertyName - The property to compare
|
|
64
|
+
* @param comparisonOperator - The comparison operator to use
|
|
65
|
+
* @param value - The value to compare against
|
|
66
|
+
* @param valueType - The type of the value
|
|
67
|
+
*/
|
|
68
|
+
where<K extends keyof T>(propertyName: K, comparisonOperator: ReadSelectedComparisonOperator, value: unknown, valueType: ReadSelectedPropertyType): this;
|
|
69
|
+
/**
|
|
70
|
+
* Adds a string property comparison with common string operations.
|
|
71
|
+
*/
|
|
72
|
+
whereString<K extends keyof T>(propertyName: K, comparisonOperator: ReadSelectedComparisonOperator.Equal | ReadSelectedComparisonOperator.NotEqual | ReadSelectedComparisonOperator.Contains | ReadSelectedComparisonOperator.StartsWith | ReadSelectedComparisonOperator.EndsWith | ReadSelectedComparisonOperator.IContains | ReadSelectedComparisonOperator.IStartsWith | ReadSelectedComparisonOperator.IEndsWith | ReadSelectedComparisonOperator.IEqual | ReadSelectedComparisonOperator.INotEqual, value: string): this;
|
|
73
|
+
/**
|
|
74
|
+
* Adds a number property comparison.
|
|
75
|
+
*/
|
|
76
|
+
whereNumber<K extends keyof T>(propertyName: K, comparisonOperator: ReadSelectedComparisonOperator.Equal | ReadSelectedComparisonOperator.NotEqual | ReadSelectedComparisonOperator.LessThan | ReadSelectedComparisonOperator.LessOrEqual | ReadSelectedComparisonOperator.GreaterThan | ReadSelectedComparisonOperator.GreaterOrEqual, value: number, valueType?: ReadSelectedPropertyType.Int | ReadSelectedPropertyType.Short | ReadSelectedPropertyType.Long | ReadSelectedPropertyType.Decimal | ReadSelectedPropertyType.Double | ReadSelectedPropertyType.Float): this;
|
|
77
|
+
/**
|
|
78
|
+
* Adds a boolean property comparison.
|
|
79
|
+
*/
|
|
80
|
+
whereBool<K extends keyof T>(propertyName: K, comparisonOperator: ReadSelectedComparisonOperator.Equal | ReadSelectedComparisonOperator.NotEqual, value: boolean): this;
|
|
81
|
+
/**
|
|
82
|
+
* Adds a date/time property comparison.
|
|
83
|
+
*/
|
|
84
|
+
whereDate<K extends keyof T>(propertyName: K, comparisonOperator: ReadSelectedComparisonOperator.Equal | ReadSelectedComparisonOperator.NotEqual | ReadSelectedComparisonOperator.LessThan | ReadSelectedComparisonOperator.LessOrEqual | ReadSelectedComparisonOperator.GreaterThan | ReadSelectedComparisonOperator.GreaterOrEqual, value: string | Date, valueType?: ReadSelectedPropertyType.DateTime | ReadSelectedPropertyType.DateTimeOffset | ReadSelectedPropertyType.DateOnly | ReadSelectedPropertyType.TimeOnly | ReadSelectedPropertyType.TimeSpan): this;
|
|
85
|
+
/**
|
|
86
|
+
* Adds an isNull check for a property.
|
|
87
|
+
*/
|
|
88
|
+
whereNull<K extends keyof T>(propertyName: K): this;
|
|
89
|
+
/**
|
|
90
|
+
* Adds an isNotNull check for a property.
|
|
91
|
+
*/
|
|
92
|
+
whereNotNull<K extends keyof T>(propertyName: K): this;
|
|
93
|
+
/**
|
|
94
|
+
* Adds criteria for a nested object property (navigation property).
|
|
95
|
+
*
|
|
96
|
+
* @param propertyName - The navigation property name
|
|
97
|
+
* @param builderFn - A function that receives a new builder for the nested type
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* // With explicit type for proper intellisense
|
|
101
|
+
* SearchBuilder.and<VehicleDto>()
|
|
102
|
+
* .nested<PersonDto>('driver', nested =>
|
|
103
|
+
* nested.whereString('name', ReadSelectedComparisonOperator.Contains, 'Alice')
|
|
104
|
+
* )
|
|
105
|
+
* .build();
|
|
106
|
+
*/
|
|
107
|
+
nested<TNested, K extends keyof T = keyof T>(propertyName: K, builderFn: (builder: SearchBuilder<TNested>) => SearchBuilder<TNested>): this;
|
|
108
|
+
/**
|
|
109
|
+
* Adds criteria for a collection property with Any quantifier.
|
|
110
|
+
* Matches if at least one element in the collection satisfies the criteria.
|
|
111
|
+
*
|
|
112
|
+
* @param propertyName - The collection property name
|
|
113
|
+
* @param builderFn - A function that receives a new builder for the collection element type
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* // With explicit type for proper intellisense
|
|
117
|
+
* SearchBuilder.and<VehicleDto>()
|
|
118
|
+
* .any<PersonDto>('passengers', passengers =>
|
|
119
|
+
* passengers.whereString('name', ReadSelectedComparisonOperator.Contains, 'Bob')
|
|
120
|
+
* )
|
|
121
|
+
* .build();
|
|
122
|
+
*/
|
|
123
|
+
any<TElement, K extends keyof T = keyof T>(propertyName: K, builderFn: (builder: SearchBuilder<TElement>) => SearchBuilder<TElement>): this;
|
|
124
|
+
/**
|
|
125
|
+
* Adds criteria for a collection property with All quantifier.
|
|
126
|
+
* Matches if all elements in the collection satisfy the criteria.
|
|
127
|
+
*
|
|
128
|
+
* @param propertyName - The collection property name
|
|
129
|
+
* @param builderFn - A function that receives a new builder for the collection element type
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* // With explicit type for proper intellisense
|
|
133
|
+
* SearchBuilder.and<VehicleDto>()
|
|
134
|
+
* .all<PersonDto>('passengers', passengers =>
|
|
135
|
+
* passengers.whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
136
|
+
* )
|
|
137
|
+
* .build();
|
|
138
|
+
*/
|
|
139
|
+
all<TElement, K extends keyof T = keyof T>(propertyName: K, builderFn: (builder: SearchBuilder<TElement>) => SearchBuilder<TElement>): this;
|
|
140
|
+
/**
|
|
141
|
+
* Adds a nested search group. Use this for complex logical groupings.
|
|
142
|
+
*
|
|
143
|
+
* @param builderFn - A function that receives a new builder for creating the nested search
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* // (name contains 'John') AND ((age > 18) OR (status = 'verified'))
|
|
147
|
+
* SearchBuilder.and<User>()
|
|
148
|
+
* .whereString('name', ReadSelectedComparisonOperator.IContains, 'John')
|
|
149
|
+
* .group(sub => sub.or()
|
|
150
|
+
* .whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
151
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'verified')
|
|
152
|
+
* )
|
|
153
|
+
* .build();
|
|
154
|
+
*/
|
|
155
|
+
group(builderFn: (builder: SearchBuilder<T>) => SearchBuilder<T>): this;
|
|
156
|
+
/**
|
|
157
|
+
* Adds an existing search definition as a nested search.
|
|
158
|
+
*
|
|
159
|
+
* @param search - An existing ReadSelectedSearchDefinitionDto
|
|
160
|
+
*/
|
|
161
|
+
addSearch(search: ReadSelectedSearchDefinitionDto<T>): this;
|
|
162
|
+
/**
|
|
163
|
+
* Builds and returns the ReadSelectedSearchDefinitionDto.
|
|
164
|
+
*/
|
|
165
|
+
build(): ReadSelectedSearchDefinitionDto<T>;
|
|
166
|
+
private toPascalCase;
|
|
167
|
+
}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { ReadSelectedCollectionOperator, ReadSelectedComparisonOperator, ReadSelectedLogicalOperator, ReadSelectedPropertyType } from '../../..';
|
|
2
|
+
/**
|
|
3
|
+
* Builder class for constructing ReadSelectedSearchDefinitionDto with a fluent API.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* // Simple AND search with property criteria
|
|
7
|
+
* const search = SearchBuilder.and<User>()
|
|
8
|
+
* .whereString('name', ReadSelectedComparisonOperator.IContains, 'John')
|
|
9
|
+
* .whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
10
|
+
* .build();
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // OR search with nested criteria (specify nested type for intellisense)
|
|
14
|
+
* const search = SearchBuilder.or<User>()
|
|
15
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'active')
|
|
16
|
+
* .nested<Address>('address', nested =>
|
|
17
|
+
* nested.whereString('city', ReadSelectedComparisonOperator.Equal, 'NYC')
|
|
18
|
+
* )
|
|
19
|
+
* .build();
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* // Complex nested search with collection criteria
|
|
23
|
+
* const search = SearchBuilder.and<Order>()
|
|
24
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'pending')
|
|
25
|
+
* .any<OrderItem>('items', itemSearch =>
|
|
26
|
+
* itemSearch.whereNumber('quantity', ReadSelectedComparisonOperator.GreaterThan, 5)
|
|
27
|
+
* )
|
|
28
|
+
* .group(sub => sub.or()
|
|
29
|
+
* .whereString('priority', ReadSelectedComparisonOperator.Equal, 'high')
|
|
30
|
+
* .whereString('priority', ReadSelectedComparisonOperator.Equal, 'urgent')
|
|
31
|
+
* )
|
|
32
|
+
* .build();
|
|
33
|
+
*/
|
|
34
|
+
export class SearchBuilder {
|
|
35
|
+
_logicalOperator;
|
|
36
|
+
_searches = [];
|
|
37
|
+
_propertyCriteria = [];
|
|
38
|
+
_nestedCriteria = [];
|
|
39
|
+
_nestedCollectionCriteria = [];
|
|
40
|
+
constructor(logicalOperator) {
|
|
41
|
+
this._logicalOperator = logicalOperator;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Creates a new builder with AND logical operator.
|
|
45
|
+
* All criteria added to this builder will be combined with AND.
|
|
46
|
+
*/
|
|
47
|
+
static and() {
|
|
48
|
+
return new SearchBuilder(ReadSelectedLogicalOperator.And);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new builder with OR logical operator.
|
|
52
|
+
* All criteria added to this builder will be combined with OR.
|
|
53
|
+
*/
|
|
54
|
+
static or() {
|
|
55
|
+
return new SearchBuilder(ReadSelectedLogicalOperator.Or);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Sets the logical operator to AND.
|
|
59
|
+
*/
|
|
60
|
+
and() {
|
|
61
|
+
this._logicalOperator = ReadSelectedLogicalOperator.And;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Sets the logical operator to OR.
|
|
66
|
+
*/
|
|
67
|
+
or() {
|
|
68
|
+
this._logicalOperator = ReadSelectedLogicalOperator.Or;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Adds a property comparison criterion.
|
|
73
|
+
*
|
|
74
|
+
* @param propertyName - The property to compare
|
|
75
|
+
* @param comparisonOperator - The comparison operator to use
|
|
76
|
+
* @param value - The value to compare against
|
|
77
|
+
* @param valueType - The type of the value
|
|
78
|
+
*/
|
|
79
|
+
where(propertyName, comparisonOperator, value, valueType) {
|
|
80
|
+
this._propertyCriteria.push({
|
|
81
|
+
propertyName: this.toPascalCase(propertyName),
|
|
82
|
+
comparisonOperator,
|
|
83
|
+
value,
|
|
84
|
+
valueType,
|
|
85
|
+
});
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Adds a string property comparison with common string operations.
|
|
90
|
+
*/
|
|
91
|
+
whereString(propertyName, comparisonOperator, value) {
|
|
92
|
+
return this.where(propertyName, comparisonOperator, value, ReadSelectedPropertyType.String);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Adds a number property comparison.
|
|
96
|
+
*/
|
|
97
|
+
whereNumber(propertyName, comparisonOperator, value, valueType = ReadSelectedPropertyType.Int) {
|
|
98
|
+
return this.where(propertyName, comparisonOperator, value, valueType);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Adds a boolean property comparison.
|
|
102
|
+
*/
|
|
103
|
+
whereBool(propertyName, comparisonOperator, value) {
|
|
104
|
+
return this.where(propertyName, comparisonOperator, value, ReadSelectedPropertyType.Bool);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Adds a date/time property comparison.
|
|
108
|
+
*/
|
|
109
|
+
whereDate(propertyName, comparisonOperator, value, valueType = ReadSelectedPropertyType.DateTime) {
|
|
110
|
+
const stringValue = value instanceof Date ? value.toISOString() : value;
|
|
111
|
+
return this.where(propertyName, comparisonOperator, stringValue, valueType);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Adds an isNull check for a property.
|
|
115
|
+
*/
|
|
116
|
+
whereNull(propertyName) {
|
|
117
|
+
this._propertyCriteria.push({
|
|
118
|
+
propertyName: this.toPascalCase(propertyName),
|
|
119
|
+
comparisonOperator: ReadSelectedComparisonOperator.IsNull,
|
|
120
|
+
value: null,
|
|
121
|
+
valueType: ReadSelectedPropertyType.String,
|
|
122
|
+
});
|
|
123
|
+
return this;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Adds an isNotNull check for a property.
|
|
127
|
+
*/
|
|
128
|
+
whereNotNull(propertyName) {
|
|
129
|
+
this._propertyCriteria.push({
|
|
130
|
+
propertyName: this.toPascalCase(propertyName),
|
|
131
|
+
comparisonOperator: ReadSelectedComparisonOperator.IsNotNull,
|
|
132
|
+
value: null,
|
|
133
|
+
valueType: ReadSelectedPropertyType.String,
|
|
134
|
+
});
|
|
135
|
+
return this;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Adds criteria for a nested object property (navigation property).
|
|
139
|
+
*
|
|
140
|
+
* @param propertyName - The navigation property name
|
|
141
|
+
* @param builderFn - A function that receives a new builder for the nested type
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* // With explicit type for proper intellisense
|
|
145
|
+
* SearchBuilder.and<VehicleDto>()
|
|
146
|
+
* .nested<PersonDto>('driver', nested =>
|
|
147
|
+
* nested.whereString('name', ReadSelectedComparisonOperator.Contains, 'Alice')
|
|
148
|
+
* )
|
|
149
|
+
* .build();
|
|
150
|
+
*/
|
|
151
|
+
nested(propertyName, builderFn) {
|
|
152
|
+
const nestedBuilder = SearchBuilder.and();
|
|
153
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
154
|
+
this._nestedCriteria.push({
|
|
155
|
+
propertyName: this.toPascalCase(propertyName),
|
|
156
|
+
criteria: configuredBuilder.build(),
|
|
157
|
+
});
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Adds criteria for a collection property with Any quantifier.
|
|
162
|
+
* Matches if at least one element in the collection satisfies the criteria.
|
|
163
|
+
*
|
|
164
|
+
* @param propertyName - The collection property name
|
|
165
|
+
* @param builderFn - A function that receives a new builder for the collection element type
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* // With explicit type for proper intellisense
|
|
169
|
+
* SearchBuilder.and<VehicleDto>()
|
|
170
|
+
* .any<PersonDto>('passengers', passengers =>
|
|
171
|
+
* passengers.whereString('name', ReadSelectedComparisonOperator.Contains, 'Bob')
|
|
172
|
+
* )
|
|
173
|
+
* .build();
|
|
174
|
+
*/
|
|
175
|
+
any(propertyName, builderFn) {
|
|
176
|
+
const nestedBuilder = SearchBuilder.and();
|
|
177
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
178
|
+
this._nestedCollectionCriteria.push({
|
|
179
|
+
propertyName: this.toPascalCase(propertyName),
|
|
180
|
+
collectionOperator: ReadSelectedCollectionOperator.Any,
|
|
181
|
+
criteria: configuredBuilder.build(),
|
|
182
|
+
});
|
|
183
|
+
return this;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Adds criteria for a collection property with All quantifier.
|
|
187
|
+
* Matches if all elements in the collection satisfy the criteria.
|
|
188
|
+
*
|
|
189
|
+
* @param propertyName - The collection property name
|
|
190
|
+
* @param builderFn - A function that receives a new builder for the collection element type
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* // With explicit type for proper intellisense
|
|
194
|
+
* SearchBuilder.and<VehicleDto>()
|
|
195
|
+
* .all<PersonDto>('passengers', passengers =>
|
|
196
|
+
* passengers.whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
197
|
+
* )
|
|
198
|
+
* .build();
|
|
199
|
+
*/
|
|
200
|
+
all(propertyName, builderFn) {
|
|
201
|
+
const nestedBuilder = SearchBuilder.and();
|
|
202
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
203
|
+
this._nestedCollectionCriteria.push({
|
|
204
|
+
propertyName: this.toPascalCase(propertyName),
|
|
205
|
+
collectionOperator: ReadSelectedCollectionOperator.All,
|
|
206
|
+
criteria: configuredBuilder.build(),
|
|
207
|
+
});
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Adds a nested search group. Use this for complex logical groupings.
|
|
212
|
+
*
|
|
213
|
+
* @param builderFn - A function that receives a new builder for creating the nested search
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* // (name contains 'John') AND ((age > 18) OR (status = 'verified'))
|
|
217
|
+
* SearchBuilder.and<User>()
|
|
218
|
+
* .whereString('name', ReadSelectedComparisonOperator.IContains, 'John')
|
|
219
|
+
* .group(sub => sub.or()
|
|
220
|
+
* .whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
221
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'verified')
|
|
222
|
+
* )
|
|
223
|
+
* .build();
|
|
224
|
+
*/
|
|
225
|
+
group(builderFn) {
|
|
226
|
+
const nestedBuilder = SearchBuilder.and();
|
|
227
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
228
|
+
this._searches.push(configuredBuilder.build());
|
|
229
|
+
return this;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Adds an existing search definition as a nested search.
|
|
233
|
+
*
|
|
234
|
+
* @param search - An existing ReadSelectedSearchDefinitionDto
|
|
235
|
+
*/
|
|
236
|
+
addSearch(search) {
|
|
237
|
+
this._searches.push(search);
|
|
238
|
+
return this;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Builds and returns the ReadSelectedSearchDefinitionDto.
|
|
242
|
+
*/
|
|
243
|
+
build() {
|
|
244
|
+
const result = {
|
|
245
|
+
logicalOperator: this._logicalOperator,
|
|
246
|
+
};
|
|
247
|
+
if (this._searches.length > 0) {
|
|
248
|
+
result.searches = this._searches;
|
|
249
|
+
}
|
|
250
|
+
if (this._propertyCriteria.length > 0) {
|
|
251
|
+
result.propertyCriteria = this._propertyCriteria;
|
|
252
|
+
}
|
|
253
|
+
if (this._nestedCriteria.length > 0) {
|
|
254
|
+
result.nestedCriteria = this._nestedCriteria;
|
|
255
|
+
}
|
|
256
|
+
if (this._nestedCollectionCriteria.length > 0) {
|
|
257
|
+
result.nestedCollectionCriteria = this._nestedCollectionCriteria;
|
|
258
|
+
}
|
|
259
|
+
return result;
|
|
260
|
+
}
|
|
261
|
+
toPascalCase(str) {
|
|
262
|
+
if (typeof str !== 'string') {
|
|
263
|
+
return str.toString();
|
|
264
|
+
}
|
|
265
|
+
return str.replace(/([A-Z])/g, ' $1').replace(/^./, firstChar => firstChar.toUpperCase());
|
|
266
|
+
}
|
|
267
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { ReadSelectedCollectionOperator, ReadSelectedComparisonOperator, ReadSelectedLogicalOperator, ReadSelectedPropertyType } from '@data';
|
|
2
|
+
import { ReadSelectedSearchDefinitionDto } from './ReadSelectedSearchDefinitionDto';
|
|
3
|
+
import { ReadSelectedSearchPropertyDefinitionDto } from './ReadSelectedSearchPropertyDefinitionDto';
|
|
4
|
+
import { ReadSelectedNestedCriteriaDto } from './ReadSelectedNestedCriteriaDto';
|
|
5
|
+
import { ReadSelectedNestedCollectionCriteriaDto } from './ReadSelectedNestedCollectionCriteriaDto';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Builder class for constructing ReadSelectedSearchDefinitionDto with a fluent API.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* // Simple AND search with property criteria
|
|
12
|
+
* const search = SearchBuilder.and<User>()
|
|
13
|
+
* .whereString('name', ReadSelectedComparisonOperator.IContains, 'John')
|
|
14
|
+
* .whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
15
|
+
* .build();
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* // OR search with nested criteria (specify nested type for intellisense)
|
|
19
|
+
* const search = SearchBuilder.or<User>()
|
|
20
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'active')
|
|
21
|
+
* .nested<Address>('address', nested =>
|
|
22
|
+
* nested.whereString('city', ReadSelectedComparisonOperator.Equal, 'NYC')
|
|
23
|
+
* )
|
|
24
|
+
* .build();
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Complex nested search with collection criteria
|
|
28
|
+
* const search = SearchBuilder.and<Order>()
|
|
29
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'pending')
|
|
30
|
+
* .any<OrderItem>('items', itemSearch =>
|
|
31
|
+
* itemSearch.whereNumber('quantity', ReadSelectedComparisonOperator.GreaterThan, 5)
|
|
32
|
+
* )
|
|
33
|
+
* .group(sub => sub.or()
|
|
34
|
+
* .whereString('priority', ReadSelectedComparisonOperator.Equal, 'high')
|
|
35
|
+
* .whereString('priority', ReadSelectedComparisonOperator.Equal, 'urgent')
|
|
36
|
+
* )
|
|
37
|
+
* .build();
|
|
38
|
+
*/
|
|
39
|
+
export class SearchBuilder<T> {
|
|
40
|
+
private _logicalOperator: ReadSelectedLogicalOperator;
|
|
41
|
+
private _searches: ReadSelectedSearchDefinitionDto<T>[] = [];
|
|
42
|
+
private _propertyCriteria: ReadSelectedSearchPropertyDefinitionDto<T, keyof T>[] = [];
|
|
43
|
+
private _nestedCriteria: ReadSelectedNestedCriteriaDto<T, keyof T>[] = [];
|
|
44
|
+
private _nestedCollectionCriteria: ReadSelectedNestedCollectionCriteriaDto<T, keyof T>[] = [];
|
|
45
|
+
|
|
46
|
+
private constructor(logicalOperator: ReadSelectedLogicalOperator) {
|
|
47
|
+
this._logicalOperator = logicalOperator;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new builder with AND logical operator.
|
|
52
|
+
* All criteria added to this builder will be combined with AND.
|
|
53
|
+
*/
|
|
54
|
+
static and<T>(): SearchBuilder<T> {
|
|
55
|
+
return new SearchBuilder<T>(ReadSelectedLogicalOperator.And);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a new builder with OR logical operator.
|
|
60
|
+
* All criteria added to this builder will be combined with OR.
|
|
61
|
+
*/
|
|
62
|
+
static or<T>(): SearchBuilder<T> {
|
|
63
|
+
return new SearchBuilder<T>(ReadSelectedLogicalOperator.Or);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Sets the logical operator to AND.
|
|
68
|
+
*/
|
|
69
|
+
and(): this {
|
|
70
|
+
this._logicalOperator = ReadSelectedLogicalOperator.And;
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sets the logical operator to OR.
|
|
76
|
+
*/
|
|
77
|
+
or(): this {
|
|
78
|
+
this._logicalOperator = ReadSelectedLogicalOperator.Or;
|
|
79
|
+
return this;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Adds a property comparison criterion.
|
|
84
|
+
*
|
|
85
|
+
* @param propertyName - The property to compare
|
|
86
|
+
* @param comparisonOperator - The comparison operator to use
|
|
87
|
+
* @param value - The value to compare against
|
|
88
|
+
* @param valueType - The type of the value
|
|
89
|
+
*/
|
|
90
|
+
where<K extends keyof T>(propertyName: K, comparisonOperator: ReadSelectedComparisonOperator, value: unknown, valueType: ReadSelectedPropertyType): this {
|
|
91
|
+
this._propertyCriteria.push({
|
|
92
|
+
propertyName: this.toPascalCase(propertyName),
|
|
93
|
+
comparisonOperator,
|
|
94
|
+
value,
|
|
95
|
+
valueType,
|
|
96
|
+
} as ReadSelectedSearchPropertyDefinitionDto<T, keyof T>);
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Adds a string property comparison with common string operations.
|
|
102
|
+
*/
|
|
103
|
+
whereString<K extends keyof T>(
|
|
104
|
+
propertyName: K,
|
|
105
|
+
comparisonOperator:
|
|
106
|
+
| ReadSelectedComparisonOperator.Equal
|
|
107
|
+
| ReadSelectedComparisonOperator.NotEqual
|
|
108
|
+
| ReadSelectedComparisonOperator.Contains
|
|
109
|
+
| ReadSelectedComparisonOperator.StartsWith
|
|
110
|
+
| ReadSelectedComparisonOperator.EndsWith
|
|
111
|
+
| ReadSelectedComparisonOperator.IContains
|
|
112
|
+
| ReadSelectedComparisonOperator.IStartsWith
|
|
113
|
+
| ReadSelectedComparisonOperator.IEndsWith
|
|
114
|
+
| ReadSelectedComparisonOperator.IEqual
|
|
115
|
+
| ReadSelectedComparisonOperator.INotEqual,
|
|
116
|
+
value: string,
|
|
117
|
+
): this {
|
|
118
|
+
return this.where(propertyName, comparisonOperator, value, ReadSelectedPropertyType.String);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Adds a number property comparison.
|
|
123
|
+
*/
|
|
124
|
+
whereNumber<K extends keyof T>(
|
|
125
|
+
propertyName: K,
|
|
126
|
+
comparisonOperator:
|
|
127
|
+
| ReadSelectedComparisonOperator.Equal
|
|
128
|
+
| ReadSelectedComparisonOperator.NotEqual
|
|
129
|
+
| ReadSelectedComparisonOperator.LessThan
|
|
130
|
+
| ReadSelectedComparisonOperator.LessOrEqual
|
|
131
|
+
| ReadSelectedComparisonOperator.GreaterThan
|
|
132
|
+
| ReadSelectedComparisonOperator.GreaterOrEqual,
|
|
133
|
+
value: number,
|
|
134
|
+
valueType:
|
|
135
|
+
| ReadSelectedPropertyType.Int
|
|
136
|
+
| ReadSelectedPropertyType.Short
|
|
137
|
+
| ReadSelectedPropertyType.Long
|
|
138
|
+
| ReadSelectedPropertyType.Decimal
|
|
139
|
+
| ReadSelectedPropertyType.Double
|
|
140
|
+
| ReadSelectedPropertyType.Float = ReadSelectedPropertyType.Int,
|
|
141
|
+
): this {
|
|
142
|
+
return this.where(propertyName, comparisonOperator, value, valueType);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Adds a boolean property comparison.
|
|
147
|
+
*/
|
|
148
|
+
whereBool<K extends keyof T>(
|
|
149
|
+
propertyName: K,
|
|
150
|
+
comparisonOperator: ReadSelectedComparisonOperator.Equal | ReadSelectedComparisonOperator.NotEqual,
|
|
151
|
+
value: boolean,
|
|
152
|
+
): this {
|
|
153
|
+
return this.where(propertyName, comparisonOperator, value, ReadSelectedPropertyType.Bool);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Adds a date/time property comparison.
|
|
158
|
+
*/
|
|
159
|
+
whereDate<K extends keyof T>(
|
|
160
|
+
propertyName: K,
|
|
161
|
+
comparisonOperator:
|
|
162
|
+
| ReadSelectedComparisonOperator.Equal
|
|
163
|
+
| ReadSelectedComparisonOperator.NotEqual
|
|
164
|
+
| ReadSelectedComparisonOperator.LessThan
|
|
165
|
+
| ReadSelectedComparisonOperator.LessOrEqual
|
|
166
|
+
| ReadSelectedComparisonOperator.GreaterThan
|
|
167
|
+
| ReadSelectedComparisonOperator.GreaterOrEqual,
|
|
168
|
+
value: string | Date,
|
|
169
|
+
valueType:
|
|
170
|
+
| ReadSelectedPropertyType.DateTime
|
|
171
|
+
| ReadSelectedPropertyType.DateTimeOffset
|
|
172
|
+
| ReadSelectedPropertyType.DateOnly
|
|
173
|
+
| ReadSelectedPropertyType.TimeOnly
|
|
174
|
+
| ReadSelectedPropertyType.TimeSpan = ReadSelectedPropertyType.DateTime,
|
|
175
|
+
): this {
|
|
176
|
+
const stringValue = value instanceof Date ? value.toISOString() : value;
|
|
177
|
+
return this.where(propertyName, comparisonOperator, stringValue, valueType);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Adds an isNull check for a property.
|
|
182
|
+
*/
|
|
183
|
+
whereNull<K extends keyof T>(propertyName: K): this {
|
|
184
|
+
this._propertyCriteria.push({
|
|
185
|
+
propertyName: this.toPascalCase(propertyName),
|
|
186
|
+
comparisonOperator: ReadSelectedComparisonOperator.IsNull,
|
|
187
|
+
value: null,
|
|
188
|
+
valueType: ReadSelectedPropertyType.String,
|
|
189
|
+
} as unknown as ReadSelectedSearchPropertyDefinitionDto<T, keyof T>);
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Adds an isNotNull check for a property.
|
|
195
|
+
*/
|
|
196
|
+
whereNotNull<K extends keyof T>(propertyName: K): this {
|
|
197
|
+
this._propertyCriteria.push({
|
|
198
|
+
propertyName: this.toPascalCase(propertyName),
|
|
199
|
+
comparisonOperator: ReadSelectedComparisonOperator.IsNotNull,
|
|
200
|
+
value: null,
|
|
201
|
+
valueType: ReadSelectedPropertyType.String,
|
|
202
|
+
} as unknown as ReadSelectedSearchPropertyDefinitionDto<T, keyof T>);
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Adds criteria for a nested object property (navigation property).
|
|
208
|
+
*
|
|
209
|
+
* @param propertyName - The navigation property name
|
|
210
|
+
* @param builderFn - A function that receives a new builder for the nested type
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* // With explicit type for proper intellisense
|
|
214
|
+
* SearchBuilder.and<VehicleDto>()
|
|
215
|
+
* .nested<PersonDto>('driver', nested =>
|
|
216
|
+
* nested.whereString('name', ReadSelectedComparisonOperator.Contains, 'Alice')
|
|
217
|
+
* )
|
|
218
|
+
* .build();
|
|
219
|
+
*/
|
|
220
|
+
nested<TNested, K extends keyof T = keyof T>(propertyName: K, builderFn: (builder: SearchBuilder<TNested>) => SearchBuilder<TNested>): this {
|
|
221
|
+
const nestedBuilder = SearchBuilder.and<TNested>();
|
|
222
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
223
|
+
this._nestedCriteria.push({
|
|
224
|
+
propertyName: this.toPascalCase(propertyName),
|
|
225
|
+
criteria: configuredBuilder.build(),
|
|
226
|
+
} as ReadSelectedNestedCriteriaDto<T, keyof T>);
|
|
227
|
+
return this;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Adds criteria for a collection property with Any quantifier.
|
|
232
|
+
* Matches if at least one element in the collection satisfies the criteria.
|
|
233
|
+
*
|
|
234
|
+
* @param propertyName - The collection property name
|
|
235
|
+
* @param builderFn - A function that receives a new builder for the collection element type
|
|
236
|
+
*
|
|
237
|
+
* @example
|
|
238
|
+
* // With explicit type for proper intellisense
|
|
239
|
+
* SearchBuilder.and<VehicleDto>()
|
|
240
|
+
* .any<PersonDto>('passengers', passengers =>
|
|
241
|
+
* passengers.whereString('name', ReadSelectedComparisonOperator.Contains, 'Bob')
|
|
242
|
+
* )
|
|
243
|
+
* .build();
|
|
244
|
+
*/
|
|
245
|
+
any<TElement, K extends keyof T = keyof T>(propertyName: K, builderFn: (builder: SearchBuilder<TElement>) => SearchBuilder<TElement>): this {
|
|
246
|
+
const nestedBuilder = SearchBuilder.and<TElement>();
|
|
247
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
248
|
+
this._nestedCollectionCriteria.push({
|
|
249
|
+
propertyName: this.toPascalCase(propertyName),
|
|
250
|
+
collectionOperator: ReadSelectedCollectionOperator.Any,
|
|
251
|
+
criteria: configuredBuilder.build(),
|
|
252
|
+
} as ReadSelectedNestedCollectionCriteriaDto<T, keyof T>);
|
|
253
|
+
return this;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Adds criteria for a collection property with All quantifier.
|
|
258
|
+
* Matches if all elements in the collection satisfy the criteria.
|
|
259
|
+
*
|
|
260
|
+
* @param propertyName - The collection property name
|
|
261
|
+
* @param builderFn - A function that receives a new builder for the collection element type
|
|
262
|
+
*
|
|
263
|
+
* @example
|
|
264
|
+
* // With explicit type for proper intellisense
|
|
265
|
+
* SearchBuilder.and<VehicleDto>()
|
|
266
|
+
* .all<PersonDto>('passengers', passengers =>
|
|
267
|
+
* passengers.whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
268
|
+
* )
|
|
269
|
+
* .build();
|
|
270
|
+
*/
|
|
271
|
+
all<TElement, K extends keyof T = keyof T>(propertyName: K, builderFn: (builder: SearchBuilder<TElement>) => SearchBuilder<TElement>): this {
|
|
272
|
+
const nestedBuilder = SearchBuilder.and<TElement>();
|
|
273
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
274
|
+
this._nestedCollectionCriteria.push({
|
|
275
|
+
propertyName: this.toPascalCase(propertyName),
|
|
276
|
+
collectionOperator: ReadSelectedCollectionOperator.All,
|
|
277
|
+
criteria: configuredBuilder.build(),
|
|
278
|
+
} as ReadSelectedNestedCollectionCriteriaDto<T, keyof T>);
|
|
279
|
+
return this;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Adds a nested search group. Use this for complex logical groupings.
|
|
284
|
+
*
|
|
285
|
+
* @param builderFn - A function that receives a new builder for creating the nested search
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* // (name contains 'John') AND ((age > 18) OR (status = 'verified'))
|
|
289
|
+
* SearchBuilder.and<User>()
|
|
290
|
+
* .whereString('name', ReadSelectedComparisonOperator.IContains, 'John')
|
|
291
|
+
* .group(sub => sub.or()
|
|
292
|
+
* .whereNumber('age', ReadSelectedComparisonOperator.GreaterThan, 18)
|
|
293
|
+
* .whereString('status', ReadSelectedComparisonOperator.Equal, 'verified')
|
|
294
|
+
* )
|
|
295
|
+
* .build();
|
|
296
|
+
*/
|
|
297
|
+
group(builderFn: (builder: SearchBuilder<T>) => SearchBuilder<T>): this {
|
|
298
|
+
const nestedBuilder = SearchBuilder.and<T>();
|
|
299
|
+
const configuredBuilder = builderFn(nestedBuilder);
|
|
300
|
+
this._searches.push(configuredBuilder.build());
|
|
301
|
+
return this;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Adds an existing search definition as a nested search.
|
|
306
|
+
*
|
|
307
|
+
* @param search - An existing ReadSelectedSearchDefinitionDto
|
|
308
|
+
*/
|
|
309
|
+
addSearch(search: ReadSelectedSearchDefinitionDto<T>): this {
|
|
310
|
+
this._searches.push(search);
|
|
311
|
+
return this;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Builds and returns the ReadSelectedSearchDefinitionDto.
|
|
316
|
+
*/
|
|
317
|
+
build(): ReadSelectedSearchDefinitionDto<T> {
|
|
318
|
+
const result: ReadSelectedSearchDefinitionDto<T> = {
|
|
319
|
+
logicalOperator: this._logicalOperator,
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
if (this._searches.length > 0) {
|
|
323
|
+
result.searches = this._searches;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (this._propertyCriteria.length > 0) {
|
|
327
|
+
result.propertyCriteria = this._propertyCriteria;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (this._nestedCriteria.length > 0) {
|
|
331
|
+
result.nestedCriteria = this._nestedCriteria;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (this._nestedCollectionCriteria.length > 0) {
|
|
335
|
+
result.nestedCollectionCriteria = this._nestedCollectionCriteria;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return result;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
private toPascalCase(str: string | number | symbol): string {
|
|
342
|
+
if (typeof str !== 'string') {
|
|
343
|
+
return str.toString();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return str.replace(/([A-Z])/g, ' $1').replace(/^./, firstChar => firstChar.toUpperCase());
|
|
347
|
+
}
|
|
348
|
+
}
|