@kodaris/krubble-components 1.0.51 → 1.0.53
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/custom-elements.json +1811 -192
- package/dist/button/button.js +1 -1
- package/dist/form/select-field/select-field.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/krubble-components.bundled.js +3749 -1866
- package/dist/krubble-components.bundled.js.map +1 -1
- package/dist/krubble-components.bundled.min.js +874 -396
- package/dist/krubble-components.bundled.min.js.map +1 -1
- package/dist/krubble-components.umd.js +4564 -2678
- package/dist/krubble-components.umd.js.map +1 -1
- package/dist/krubble-components.umd.min.js +876 -398
- package/dist/krubble-components.umd.min.js.map +1 -1
- package/dist/table/query.d.ts +63 -0
- package/dist/table/query.d.ts.map +1 -0
- package/dist/table/query.js +1015 -0
- package/dist/table/query.js.map +1 -0
- package/dist/table/table.d.ts +63 -10
- package/dist/table/table.d.ts.map +1 -1
- package/dist/table/table.js +986 -113
- package/dist/table/table.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1015 @@
|
|
|
1
|
+
/** Data-driven operator metadata map */
|
|
2
|
+
export const KR_OPERATORS = {
|
|
3
|
+
equals: { key: 'equals', type: 'comparison', dataTypes: ['string', 'number', 'date', 'boolean'], label: 'Equals' },
|
|
4
|
+
n_equals: { key: 'n_equals', type: 'comparison', dataTypes: ['string', 'number', 'date', 'boolean'], label: "Doesn't equal" },
|
|
5
|
+
contains: { key: 'contains', type: 'comparison', dataTypes: ['string'], label: 'Contains' },
|
|
6
|
+
n_contains: { key: 'n_contains', type: 'comparison', dataTypes: ['string'], label: "Doesn't contain" },
|
|
7
|
+
starts_with: { key: 'starts_with', type: 'comparison', dataTypes: ['string'], label: 'Starts with' },
|
|
8
|
+
ends_with: { key: 'ends_with', type: 'comparison', dataTypes: ['string'], label: 'Ends with' },
|
|
9
|
+
less_than: { key: 'less_than', type: 'comparison', dataTypes: ['number', 'date'], label: 'Less than' },
|
|
10
|
+
less_than_equal: { key: 'less_than_equal', type: 'comparison', dataTypes: ['number', 'date'], label: 'Less than or equal' },
|
|
11
|
+
greater_than: { key: 'greater_than', type: 'comparison', dataTypes: ['number', 'date'], label: 'Greater than' },
|
|
12
|
+
greater_than_equal: { key: 'greater_than_equal', type: 'comparison', dataTypes: ['number', 'date'], label: 'Greater than or equal' },
|
|
13
|
+
between: { key: 'between', type: 'range', dataTypes: ['number', 'date'], label: 'Between' },
|
|
14
|
+
in: { key: 'in', type: 'list', dataTypes: ['string', 'number'], label: 'In' },
|
|
15
|
+
empty: { key: 'empty', type: 'nil', dataTypes: ['string', 'number', 'date', 'boolean'], label: 'Empty' },
|
|
16
|
+
n_empty: { key: 'n_empty', type: 'nil', dataTypes: ['string', 'number', 'date', 'boolean'], label: 'Not empty' },
|
|
17
|
+
};
|
|
18
|
+
/** Returns the list of operators available for a given column type */
|
|
19
|
+
export function getOperatorsForType(columnType) {
|
|
20
|
+
const results = [];
|
|
21
|
+
for (const op of Object.values(KR_OPERATORS)) {
|
|
22
|
+
if (op.dataTypes.includes(columnType)) {
|
|
23
|
+
results.push({
|
|
24
|
+
key: op.key,
|
|
25
|
+
label: op.label
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return results;
|
|
30
|
+
}
|
|
31
|
+
// === Solr Utilities ===
|
|
32
|
+
const SOLR_RESERVED_CHARS = [
|
|
33
|
+
'"', '+', '-', '&&', '||', '!', '(', ')', '{',
|
|
34
|
+
'}', '[', ']', '^', '~', '*', '?', ':'
|
|
35
|
+
];
|
|
36
|
+
const SOLR_RESERVED_CHARS_REPLACEMENT = [
|
|
37
|
+
'\\"', '\\+', '\\-', '\\&\\&', '\\|\\|', '\\!', '\\(', '\\)', '\\{',
|
|
38
|
+
'\\}', '\\[', '\\]', '\\^', '\\~', '\\*', '\\?', '\\:'
|
|
39
|
+
];
|
|
40
|
+
function escapeRegex(str) {
|
|
41
|
+
if (typeof str !== 'string') {
|
|
42
|
+
return str;
|
|
43
|
+
}
|
|
44
|
+
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
45
|
+
}
|
|
46
|
+
/** Converts a value to a Solr term */
|
|
47
|
+
export function termify(value, exact = true) {
|
|
48
|
+
if (value === true || value === false) {
|
|
49
|
+
return value.toString();
|
|
50
|
+
}
|
|
51
|
+
if (value === null) {
|
|
52
|
+
return `(*:* NOT *)`;
|
|
53
|
+
}
|
|
54
|
+
if (typeof value === 'number') {
|
|
55
|
+
return value.toString();
|
|
56
|
+
}
|
|
57
|
+
if (typeof value === 'string') {
|
|
58
|
+
SOLR_RESERVED_CHARS.forEach((char, i) => {
|
|
59
|
+
value = value.replace(new RegExp(`${escapeRegex(char)}`, 'g'), SOLR_RESERVED_CHARS_REPLACEMENT[i]);
|
|
60
|
+
});
|
|
61
|
+
if (exact) {
|
|
62
|
+
return `"${value}"`;
|
|
63
|
+
}
|
|
64
|
+
return value.replace(/ /g, `\\ `);
|
|
65
|
+
}
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
/** Converts a Date to a Solr term */
|
|
69
|
+
function termifyDate(value, endOfTheDay = false) {
|
|
70
|
+
const newTime = new Date(value);
|
|
71
|
+
newTime.setMinutes(newTime.getMinutes() - newTime.getTimezoneOffset());
|
|
72
|
+
if (endOfTheDay) {
|
|
73
|
+
newTime.setUTCHours(23);
|
|
74
|
+
newTime.setUTCMinutes(59);
|
|
75
|
+
newTime.setUTCSeconds(59);
|
|
76
|
+
newTime.setUTCMilliseconds(999);
|
|
77
|
+
}
|
|
78
|
+
return newTime.toISOString().replace(/:/g, '\\:');
|
|
79
|
+
}
|
|
80
|
+
/** Creates a Solr date range expression from a Date or range of Dates */
|
|
81
|
+
function createSolrDateRange(value, specificities) {
|
|
82
|
+
let startDate;
|
|
83
|
+
let endDate;
|
|
84
|
+
let startSpecificity;
|
|
85
|
+
let endSpecificity;
|
|
86
|
+
if (value && 'start' in value && 'end' in value) {
|
|
87
|
+
startDate = new Date(value.start);
|
|
88
|
+
endDate = new Date(value.end);
|
|
89
|
+
startSpecificity = specificities[0];
|
|
90
|
+
endSpecificity = specificities[1];
|
|
91
|
+
}
|
|
92
|
+
else if (value instanceof Date) {
|
|
93
|
+
startDate = new Date(value);
|
|
94
|
+
endDate = new Date(value);
|
|
95
|
+
startSpecificity = specificities[0];
|
|
96
|
+
endSpecificity = specificities[0];
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
return '[* TO *]';
|
|
100
|
+
}
|
|
101
|
+
if (startSpecificity === 'year') {
|
|
102
|
+
startDate.setUTCMonth(0, 1);
|
|
103
|
+
startDate.setUTCHours(0, 0, 0, 0);
|
|
104
|
+
}
|
|
105
|
+
else if (startSpecificity === 'month') {
|
|
106
|
+
startDate.setUTCDate(1);
|
|
107
|
+
startDate.setUTCHours(0, 0, 0, 0);
|
|
108
|
+
}
|
|
109
|
+
else if (startSpecificity === 'day') {
|
|
110
|
+
startDate.setUTCHours(0, 0, 0, 0);
|
|
111
|
+
}
|
|
112
|
+
else if (startSpecificity === 'time') {
|
|
113
|
+
startDate.setUTCSeconds(0, 0);
|
|
114
|
+
}
|
|
115
|
+
if (endSpecificity === 'year') {
|
|
116
|
+
endDate.setUTCMonth(11, 31);
|
|
117
|
+
endDate.setUTCHours(23, 59, 59, 999);
|
|
118
|
+
}
|
|
119
|
+
else if (endSpecificity === 'month') {
|
|
120
|
+
endDate.setUTCMonth(endDate.getUTCMonth() + 1);
|
|
121
|
+
endDate.setUTCDate(0);
|
|
122
|
+
endDate.setUTCHours(23, 59, 59, 999);
|
|
123
|
+
}
|
|
124
|
+
else if (endSpecificity === 'day') {
|
|
125
|
+
endDate.setUTCHours(23, 59, 59, 999);
|
|
126
|
+
}
|
|
127
|
+
else if (endSpecificity === 'time') {
|
|
128
|
+
endDate.setUTCSeconds(59, 999);
|
|
129
|
+
}
|
|
130
|
+
return `[${startDate.toISOString().replace(/:/g, '\\:')} TO ${endDate.toISOString().replace(/:/g, '\\:')}]`;
|
|
131
|
+
}
|
|
132
|
+
/** Creates a DB date range array from a Date or range of Dates, expanding based on specificity */
|
|
133
|
+
function createDbDateRange(value, specificities) {
|
|
134
|
+
let startDate;
|
|
135
|
+
let endDate;
|
|
136
|
+
let startSpecificity;
|
|
137
|
+
let endSpecificity;
|
|
138
|
+
if (value && 'start' in value && 'end' in value) {
|
|
139
|
+
startDate = new Date(value.start);
|
|
140
|
+
endDate = new Date(value.end);
|
|
141
|
+
startSpecificity = specificities[0];
|
|
142
|
+
endSpecificity = specificities[1];
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
startDate = new Date(value);
|
|
146
|
+
endDate = new Date(value);
|
|
147
|
+
startSpecificity = specificities[0];
|
|
148
|
+
endSpecificity = specificities[0];
|
|
149
|
+
}
|
|
150
|
+
let start;
|
|
151
|
+
let end;
|
|
152
|
+
if (startSpecificity === 'year') {
|
|
153
|
+
startDate.setUTCMonth(0, 1);
|
|
154
|
+
startDate.setUTCHours(0, 0, 0, 0);
|
|
155
|
+
start = startDate;
|
|
156
|
+
}
|
|
157
|
+
else if (startSpecificity === 'month') {
|
|
158
|
+
startDate.setUTCDate(1);
|
|
159
|
+
startDate.setUTCHours(0, 0, 0, 0);
|
|
160
|
+
start = startDate;
|
|
161
|
+
}
|
|
162
|
+
else if (startSpecificity === 'day') {
|
|
163
|
+
startDate.setUTCHours(0, 0, 0, 0);
|
|
164
|
+
start = startDate;
|
|
165
|
+
}
|
|
166
|
+
else if (startSpecificity === 'time') {
|
|
167
|
+
startDate.setUTCSeconds(0, 0);
|
|
168
|
+
start = startDate;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
start = startDate;
|
|
172
|
+
}
|
|
173
|
+
if (endSpecificity === 'year') {
|
|
174
|
+
endDate.setUTCMonth(11, 31);
|
|
175
|
+
endDate.setUTCHours(23, 59, 59, 999);
|
|
176
|
+
end = endDate;
|
|
177
|
+
}
|
|
178
|
+
else if (endSpecificity === 'month') {
|
|
179
|
+
endDate.setUTCMonth(endDate.getUTCMonth() + 1);
|
|
180
|
+
endDate.setUTCDate(0);
|
|
181
|
+
endDate.setUTCHours(23, 59, 59, 999);
|
|
182
|
+
end = endDate;
|
|
183
|
+
}
|
|
184
|
+
else if (endSpecificity === 'day') {
|
|
185
|
+
endDate.setUTCHours(23, 59, 59, 999);
|
|
186
|
+
end = endDate;
|
|
187
|
+
}
|
|
188
|
+
else if (endSpecificity === 'time') {
|
|
189
|
+
endDate.setUTCSeconds(59, 999);
|
|
190
|
+
end = endDate;
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
end = endDate;
|
|
194
|
+
}
|
|
195
|
+
return [
|
|
196
|
+
start.toISOString().replace(/\..+/, '').replace('T', ' '),
|
|
197
|
+
end.toISOString().replace(/\..+/, '').replace('T', ' '),
|
|
198
|
+
];
|
|
199
|
+
}
|
|
200
|
+
/** Determines the date specificity from the text format */
|
|
201
|
+
function getDateSpecificity(input) {
|
|
202
|
+
input = input.trim();
|
|
203
|
+
// MM/dd/yy, M/d/yy (2-digit year)
|
|
204
|
+
if (/^(\d{1,2})\/(\d{1,2})\/(\d{2})$/.test(input)) {
|
|
205
|
+
return 'day';
|
|
206
|
+
}
|
|
207
|
+
// MM/dd/yyyy, M/d/yyyy (4-digit year)
|
|
208
|
+
if (/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/.test(input)) {
|
|
209
|
+
return 'day';
|
|
210
|
+
}
|
|
211
|
+
// MM/dd/yyyy, t — date with time (4-digit year only)
|
|
212
|
+
if (/^(\d{1,2})\/(\d{1,2})\/(\d{4}),?\s+(\d{1,2}):(\d{2})\s*(AM|PM)?$/i.test(input)) {
|
|
213
|
+
return 'time';
|
|
214
|
+
}
|
|
215
|
+
// "Month DD, YYYY, time" with optional weekday
|
|
216
|
+
if (/^(?:[A-Za-z]+,\s+)?([A-Za-z]+)\s+(\d{1,2}),?\s+(\d{4}),?\s+(\d{1,2}):(\d{2})\s*(AM|PM)?$/i.test(input)) {
|
|
217
|
+
return 'time';
|
|
218
|
+
}
|
|
219
|
+
// "Month DD, YYYY" with optional weekday
|
|
220
|
+
if (/^(?:[A-Za-z]+,\s+)?([A-Za-z]+)\s+(\d{1,2}),?\s+(\d{4})$/.test(input)) {
|
|
221
|
+
return 'day';
|
|
222
|
+
}
|
|
223
|
+
// yy (2-digit year)
|
|
224
|
+
if (/^(\d{2})$/.test(input)) {
|
|
225
|
+
return 'year';
|
|
226
|
+
}
|
|
227
|
+
// yyyy (4-digit year)
|
|
228
|
+
if (/^(\d{4})$/.test(input)) {
|
|
229
|
+
return 'year';
|
|
230
|
+
}
|
|
231
|
+
// "MMM yyyy" or "MMMM yyyy" — "Aug 2015", "August 2015"
|
|
232
|
+
if (/^([A-Za-z]+)\s+(\d{4})$/.test(input)) {
|
|
233
|
+
return 'month';
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
export class KRQuery {
|
|
238
|
+
constructor() {
|
|
239
|
+
this.field = '';
|
|
240
|
+
this.operator = 'equals';
|
|
241
|
+
this.value = null;
|
|
242
|
+
this.kql = '';
|
|
243
|
+
this.text = '';
|
|
244
|
+
this.type = '';
|
|
245
|
+
this.specificity = [];
|
|
246
|
+
}
|
|
247
|
+
// --------------------------------------------------------------------------
|
|
248
|
+
// Public Methods
|
|
249
|
+
// --------------------------------------------------------------------------
|
|
250
|
+
/** Parse a KQL text string and populate operator/value/values */
|
|
251
|
+
setKql(kql) {
|
|
252
|
+
this.kql = kql.trim();
|
|
253
|
+
this.text = '';
|
|
254
|
+
this.specificity = [];
|
|
255
|
+
this.value = null;
|
|
256
|
+
if (!this.kql) {
|
|
257
|
+
if (this.type === 'string') {
|
|
258
|
+
this.operator = 'contains';
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
this.operator = 'equals';
|
|
262
|
+
}
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if (kql.startsWith('=')) {
|
|
266
|
+
this.operator = 'equals';
|
|
267
|
+
this.text = kql.substring(1).trim();
|
|
268
|
+
this.value = this._parse(this.text);
|
|
269
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
270
|
+
}
|
|
271
|
+
else if (kql.startsWith('!=')) {
|
|
272
|
+
this.operator = 'n_equals';
|
|
273
|
+
this.text = kql.substring(2).trim();
|
|
274
|
+
this.value = this._parse(this.text);
|
|
275
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
276
|
+
}
|
|
277
|
+
else if (kql.startsWith('~') && this.type === 'string') {
|
|
278
|
+
this.operator = 'contains';
|
|
279
|
+
this.text = kql.substring(1).trim();
|
|
280
|
+
this.value = this._parse(this.text);
|
|
281
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
282
|
+
}
|
|
283
|
+
else if (kql.startsWith('!~') && this.type === 'string') {
|
|
284
|
+
this.operator = 'n_contains';
|
|
285
|
+
this.text = kql.substring(2).trim();
|
|
286
|
+
this.value = this._parse(this.text);
|
|
287
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
288
|
+
}
|
|
289
|
+
else if (kql.endsWith('*') && this.type === 'string') {
|
|
290
|
+
this.operator = 'starts_with';
|
|
291
|
+
this.text = kql.substring(0, kql.length - 1);
|
|
292
|
+
this.value = this._parse(this.text);
|
|
293
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
294
|
+
}
|
|
295
|
+
else if (kql.startsWith('*') && this.type === 'string') {
|
|
296
|
+
this.operator = 'ends_with';
|
|
297
|
+
this.text = kql.substring(1);
|
|
298
|
+
this.value = this._parse(this.text);
|
|
299
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
300
|
+
}
|
|
301
|
+
else if (kql === 'EMPTY') {
|
|
302
|
+
this.operator = 'empty';
|
|
303
|
+
this.value = null;
|
|
304
|
+
this.specificity = [];
|
|
305
|
+
}
|
|
306
|
+
else if (kql === 'NOT EMPTY') {
|
|
307
|
+
this.operator = 'n_empty';
|
|
308
|
+
this.value = null;
|
|
309
|
+
this.specificity = [];
|
|
310
|
+
}
|
|
311
|
+
else if (kql.startsWith('<=')) {
|
|
312
|
+
this.operator = 'less_than_equal';
|
|
313
|
+
this.text = kql.substring(2).trim();
|
|
314
|
+
this.value = this._parse(this.text);
|
|
315
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
316
|
+
}
|
|
317
|
+
else if (kql.startsWith('<')) {
|
|
318
|
+
this.operator = 'less_than';
|
|
319
|
+
this.text = kql.substring(1).trim();
|
|
320
|
+
this.value = this._parse(this.text);
|
|
321
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
322
|
+
}
|
|
323
|
+
else if (kql.startsWith('>=')) {
|
|
324
|
+
this.operator = 'greater_than_equal';
|
|
325
|
+
this.text = kql.substring(2).trim();
|
|
326
|
+
this.value = this._parse(this.text);
|
|
327
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
328
|
+
}
|
|
329
|
+
else if (kql.startsWith('>')) {
|
|
330
|
+
this.operator = 'greater_than';
|
|
331
|
+
this.text = kql.substring(1).trim();
|
|
332
|
+
this.value = this._parse(this.text);
|
|
333
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
334
|
+
}
|
|
335
|
+
else if (kql.startsWith('[') && kql.endsWith(']') && kql.indexOf(' TO ') > -1) {
|
|
336
|
+
this.operator = 'between';
|
|
337
|
+
const toIndex = kql.indexOf(' TO ');
|
|
338
|
+
const rawStart = kql.substring(1, toIndex).trim();
|
|
339
|
+
const rawEnd = kql.substring(toIndex + 4, kql.length - 1).trim();
|
|
340
|
+
this.text = `${rawStart} - ${rawEnd}`;
|
|
341
|
+
this.value = {
|
|
342
|
+
start: this._parse(rawStart),
|
|
343
|
+
end: this._parse(rawEnd)
|
|
344
|
+
};
|
|
345
|
+
this.specificity = [getDateSpecificity(rawStart), getDateSpecificity(rawEnd)];
|
|
346
|
+
}
|
|
347
|
+
else if (kql.startsWith('(') && kql.endsWith(')')) {
|
|
348
|
+
this.operator = 'in';
|
|
349
|
+
this.text = kql.substring(1, kql.length - 1).trim();
|
|
350
|
+
this.value = this.text.split(',')
|
|
351
|
+
.map(v => v.trim())
|
|
352
|
+
.map(v => this._parse(v));
|
|
353
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
if (this.type === 'date') {
|
|
357
|
+
this.operator = 'equals';
|
|
358
|
+
this.text = this.kql;
|
|
359
|
+
this.value = this._parse(this.text);
|
|
360
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
361
|
+
}
|
|
362
|
+
else if (this.type === 'boolean') {
|
|
363
|
+
this.operator = 'equals';
|
|
364
|
+
this.text = this.kql;
|
|
365
|
+
this.value = this._parse(this.text);
|
|
366
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
367
|
+
}
|
|
368
|
+
else if (this.type === 'number') {
|
|
369
|
+
this.operator = 'equals';
|
|
370
|
+
this.text = this.kql;
|
|
371
|
+
this.value = this._parse(this.text);
|
|
372
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
373
|
+
}
|
|
374
|
+
else {
|
|
375
|
+
this.operator = 'contains';
|
|
376
|
+
this.text = kql;
|
|
377
|
+
this.value = this._parse(this.text);
|
|
378
|
+
this.specificity = [getDateSpecificity(this.text)];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
setOperator(operator) {
|
|
383
|
+
// Clear value/text when switching away from operators
|
|
384
|
+
// whose values are incompatible with other operators.
|
|
385
|
+
if (this.operator === 'empty' || this.operator === 'n_empty') {
|
|
386
|
+
this.value = null;
|
|
387
|
+
this.text = '';
|
|
388
|
+
}
|
|
389
|
+
else if (this.operator === 'between') {
|
|
390
|
+
this.value = null;
|
|
391
|
+
this.text = '';
|
|
392
|
+
}
|
|
393
|
+
else if (this.operator === 'in') {
|
|
394
|
+
this.value = [];
|
|
395
|
+
this.text = '';
|
|
396
|
+
}
|
|
397
|
+
this.operator = operator;
|
|
398
|
+
// Set up initial value/text for the new operator.
|
|
399
|
+
if (this.operator === 'empty') {
|
|
400
|
+
this.value = 'EMPTY';
|
|
401
|
+
this.text = 'EMPTY';
|
|
402
|
+
}
|
|
403
|
+
else if (this.operator === 'n_empty') {
|
|
404
|
+
this.value = 'NOT EMPTY';
|
|
405
|
+
this.text = 'NOT EMPTY';
|
|
406
|
+
}
|
|
407
|
+
else if (this.operator === 'between') {
|
|
408
|
+
this.value = null;
|
|
409
|
+
this.text = '';
|
|
410
|
+
}
|
|
411
|
+
else if (this.operator === 'in') {
|
|
412
|
+
this.value = [];
|
|
413
|
+
this.text = '';
|
|
414
|
+
}
|
|
415
|
+
this._buildKql();
|
|
416
|
+
}
|
|
417
|
+
setValue(value, specificity) {
|
|
418
|
+
this.value = value;
|
|
419
|
+
if (specificity) {
|
|
420
|
+
this.specificity = [specificity];
|
|
421
|
+
}
|
|
422
|
+
else if (value instanceof Date) {
|
|
423
|
+
this.specificity = ['time'];
|
|
424
|
+
}
|
|
425
|
+
else {
|
|
426
|
+
this.specificity = [];
|
|
427
|
+
}
|
|
428
|
+
this._buildText();
|
|
429
|
+
this._buildKql();
|
|
430
|
+
}
|
|
431
|
+
setStart(value, specificity) {
|
|
432
|
+
if (this.value?.start === undefined) {
|
|
433
|
+
this.value = { start: null, end: null };
|
|
434
|
+
}
|
|
435
|
+
this.value.start = value;
|
|
436
|
+
if (specificity) {
|
|
437
|
+
this.specificity[0] = specificity;
|
|
438
|
+
}
|
|
439
|
+
else if (value instanceof Date) {
|
|
440
|
+
this.specificity[0] = 'time';
|
|
441
|
+
}
|
|
442
|
+
this._buildText();
|
|
443
|
+
this._buildKql();
|
|
444
|
+
}
|
|
445
|
+
setEnd(value, specificity) {
|
|
446
|
+
if (this.value?.end === undefined) {
|
|
447
|
+
this.value = { start: null, end: null };
|
|
448
|
+
}
|
|
449
|
+
this.value.end = value;
|
|
450
|
+
if (specificity) {
|
|
451
|
+
this.specificity[1] = specificity;
|
|
452
|
+
}
|
|
453
|
+
else if (value instanceof Date) {
|
|
454
|
+
this.specificity[1] = 'time';
|
|
455
|
+
}
|
|
456
|
+
this._buildText();
|
|
457
|
+
this._buildKql();
|
|
458
|
+
}
|
|
459
|
+
clear() {
|
|
460
|
+
if (this.type === 'string') {
|
|
461
|
+
this.operator = 'contains';
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
this.operator = 'equals';
|
|
465
|
+
}
|
|
466
|
+
this.value = null;
|
|
467
|
+
this.kql = '';
|
|
468
|
+
this.text = '';
|
|
469
|
+
this.specificity = [];
|
|
470
|
+
}
|
|
471
|
+
isEmpty() {
|
|
472
|
+
if (this.operator === 'empty' || this.operator === 'n_empty') {
|
|
473
|
+
return !this.text;
|
|
474
|
+
}
|
|
475
|
+
if (this.operator === 'between') {
|
|
476
|
+
return !this.value?.start && !this.value?.end;
|
|
477
|
+
}
|
|
478
|
+
if (this.operator === 'in') {
|
|
479
|
+
return !this.value?.length;
|
|
480
|
+
}
|
|
481
|
+
return this.value === undefined || this.value === null || this.value === '';
|
|
482
|
+
}
|
|
483
|
+
/** Returns true if the value array contains the given value. Only applies to 'in' operator. */
|
|
484
|
+
has(val) {
|
|
485
|
+
if (this.operator !== 'in' || !Array.isArray(this.value)) {
|
|
486
|
+
return false;
|
|
487
|
+
}
|
|
488
|
+
// Bucket values from Solr arrive as strings ("true"/"false") but
|
|
489
|
+
// the value array stores actual booleans, so coerce before comparing.
|
|
490
|
+
if (this.type === 'boolean' && typeof val === 'string') {
|
|
491
|
+
if (val === 'true') {
|
|
492
|
+
val = true;
|
|
493
|
+
}
|
|
494
|
+
else if (val === 'false') {
|
|
495
|
+
val = false;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
return this.value.indexOf(val) >= 0;
|
|
499
|
+
}
|
|
500
|
+
/** Adds or removes a value from the 'in' list and rebuilds text/kql. */
|
|
501
|
+
toggle(val) {
|
|
502
|
+
if (this.operator !== 'in') {
|
|
503
|
+
this.setOperator('in');
|
|
504
|
+
}
|
|
505
|
+
// Bucket values from Solr arrive as strings ("true"/"false") —
|
|
506
|
+
// coerce to actual booleans so isValid() and toSolrData() work correctly.
|
|
507
|
+
if (this.type === 'boolean' && typeof val === 'string') {
|
|
508
|
+
if (val === 'true') {
|
|
509
|
+
val = true;
|
|
510
|
+
}
|
|
511
|
+
else if (val === 'false') {
|
|
512
|
+
val = false;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
const index = this.value.indexOf(val);
|
|
516
|
+
if (index >= 0) {
|
|
517
|
+
this.value.splice(index, 1);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
this.value.push(val);
|
|
521
|
+
}
|
|
522
|
+
this._buildText();
|
|
523
|
+
this._buildKql();
|
|
524
|
+
}
|
|
525
|
+
isValid() {
|
|
526
|
+
if (!this.kql) {
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
if (!this.field) {
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
if (!this.operator) {
|
|
533
|
+
return false;
|
|
534
|
+
}
|
|
535
|
+
if (this.operator === 'empty') {
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
if (this.operator === 'n_empty') {
|
|
539
|
+
return true;
|
|
540
|
+
}
|
|
541
|
+
// For 'in' operator, valid as long as there's at least one value (including null for empty buckets)
|
|
542
|
+
if (this.operator === 'in') {
|
|
543
|
+
return Array.isArray(this.value) && this.value.length > 0;
|
|
544
|
+
}
|
|
545
|
+
if (this.type === 'date') {
|
|
546
|
+
if (this.operator === 'between') {
|
|
547
|
+
return this.value?.start instanceof Date
|
|
548
|
+
&& this.value?.end instanceof Date
|
|
549
|
+
&& !isNaN(this.value.start.getTime())
|
|
550
|
+
&& !isNaN(this.value.end.getTime());
|
|
551
|
+
}
|
|
552
|
+
return this.value instanceof Date && !isNaN(this.value.getTime());
|
|
553
|
+
}
|
|
554
|
+
if (this.type === 'number') {
|
|
555
|
+
if (this.operator === 'between') {
|
|
556
|
+
return typeof this.value?.start === 'number' && typeof this.value?.end === 'number';
|
|
557
|
+
}
|
|
558
|
+
return typeof this.value === 'number';
|
|
559
|
+
}
|
|
560
|
+
if (this.type === 'boolean') {
|
|
561
|
+
return typeof this.value === 'boolean';
|
|
562
|
+
}
|
|
563
|
+
return typeof this.value === 'string' && !!this.value.trim().length;
|
|
564
|
+
}
|
|
565
|
+
/** Converts this filter to a Solr filter field object, or null if invalid */
|
|
566
|
+
toSolrData() {
|
|
567
|
+
const data = {
|
|
568
|
+
boost: null,
|
|
569
|
+
fuzzy: null,
|
|
570
|
+
not: false,
|
|
571
|
+
name: null,
|
|
572
|
+
operation: null,
|
|
573
|
+
tagged: false,
|
|
574
|
+
value: null
|
|
575
|
+
};
|
|
576
|
+
data.name = this.field;
|
|
577
|
+
switch (this.operator) {
|
|
578
|
+
case 'equals':
|
|
579
|
+
if (this.value instanceof Date) {
|
|
580
|
+
data.operation = 'EXPRESSION';
|
|
581
|
+
data.value = createSolrDateRange(this.value, this.specificity);
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
data.operation = 'IS';
|
|
585
|
+
data.value = this.value;
|
|
586
|
+
}
|
|
587
|
+
break;
|
|
588
|
+
case 'n_equals':
|
|
589
|
+
if (this.value instanceof Date) {
|
|
590
|
+
data.operation = 'EXPRESSION';
|
|
591
|
+
data.not = true;
|
|
592
|
+
data.value = createSolrDateRange(this.value, this.specificity);
|
|
593
|
+
}
|
|
594
|
+
else {
|
|
595
|
+
data.operation = 'IS';
|
|
596
|
+
data.not = true;
|
|
597
|
+
data.value = this.value;
|
|
598
|
+
}
|
|
599
|
+
break;
|
|
600
|
+
case 'contains':
|
|
601
|
+
data.operation = 'EXPRESSION';
|
|
602
|
+
data.value = `*${termify(this.value, false)}*`;
|
|
603
|
+
break;
|
|
604
|
+
case 'n_contains':
|
|
605
|
+
data.operation = 'EXPRESSION';
|
|
606
|
+
data.not = true;
|
|
607
|
+
data.value = `*${termify(this.value, false)}*`;
|
|
608
|
+
break;
|
|
609
|
+
case 'starts_with':
|
|
610
|
+
data.operation = 'EXPRESSION';
|
|
611
|
+
data.value = `${termify(this.value, false)}*`;
|
|
612
|
+
break;
|
|
613
|
+
case 'ends_with':
|
|
614
|
+
data.operation = 'EXPRESSION';
|
|
615
|
+
data.value = `*${termify(this.value, false)}`;
|
|
616
|
+
break;
|
|
617
|
+
case 'empty':
|
|
618
|
+
data.operation = 'EXPRESSION';
|
|
619
|
+
data.not = true;
|
|
620
|
+
data.value = '[* TO *]';
|
|
621
|
+
break;
|
|
622
|
+
case 'n_empty':
|
|
623
|
+
data.operation = 'EXPRESSION';
|
|
624
|
+
data.value = '[* TO *]';
|
|
625
|
+
break;
|
|
626
|
+
case 'less_than':
|
|
627
|
+
if (this.value instanceof Date) {
|
|
628
|
+
data.operation = 'EXPRESSION';
|
|
629
|
+
data.value = `[* TO ${termifyDate(this.value)}}`;
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
632
|
+
data.operation = 'EXPRESSION';
|
|
633
|
+
data.value = `[* TO ${termify(this.value, true)}}`;
|
|
634
|
+
}
|
|
635
|
+
break;
|
|
636
|
+
case 'less_than_equal':
|
|
637
|
+
if (this.value instanceof Date) {
|
|
638
|
+
data.operation = 'EXPRESSION';
|
|
639
|
+
data.value = `[* TO ${termifyDate(this.value, true)}]`;
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
data.operation = 'EXPRESSION';
|
|
643
|
+
data.value = `[* TO ${termify(this.value, true)}]`;
|
|
644
|
+
}
|
|
645
|
+
break;
|
|
646
|
+
case 'greater_than':
|
|
647
|
+
if (this.value instanceof Date) {
|
|
648
|
+
data.operation = 'EXPRESSION';
|
|
649
|
+
data.value = `{${termifyDate(this.value, true)} TO *]`;
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
data.operation = 'EXPRESSION';
|
|
653
|
+
data.value = `{${termify(this.value, true)} TO *]`;
|
|
654
|
+
}
|
|
655
|
+
break;
|
|
656
|
+
case 'greater_than_equal':
|
|
657
|
+
if (this.value instanceof Date) {
|
|
658
|
+
data.operation = 'EXPRESSION';
|
|
659
|
+
data.value = `[${termifyDate(this.value)} TO *]`;
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
data.operation = 'EXPRESSION';
|
|
663
|
+
data.value = `[${termify(this.value, true)} TO *]`;
|
|
664
|
+
}
|
|
665
|
+
break;
|
|
666
|
+
case 'between':
|
|
667
|
+
if (this.value?.start instanceof Date && this.value?.end instanceof Date) {
|
|
668
|
+
data.operation = 'EXPRESSION';
|
|
669
|
+
data.value = createSolrDateRange(this.value, this.specificity);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
data.operation = 'EXPRESSION';
|
|
673
|
+
data.value = `[${this.value.start} TO ${this.value.end}]`;
|
|
674
|
+
}
|
|
675
|
+
break;
|
|
676
|
+
case 'in':
|
|
677
|
+
if (Array.isArray(this.value) && this.value.length > 0) {
|
|
678
|
+
data.operation = 'EXPRESSION';
|
|
679
|
+
data.value = `(${this.value.map((v) => termify(v, true)).join(' OR ')})`;
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
data.operation = 'EXPRESSION';
|
|
683
|
+
data.value = termify(this.value, true);
|
|
684
|
+
}
|
|
685
|
+
break;
|
|
686
|
+
default:
|
|
687
|
+
data.operation = 'EXPRESSION';
|
|
688
|
+
data.value = termify(this.value, true);
|
|
689
|
+
}
|
|
690
|
+
return data;
|
|
691
|
+
}
|
|
692
|
+
/** Converts this filter to a DB filter field object, or null if invalid */
|
|
693
|
+
toDbParams() {
|
|
694
|
+
const params = {
|
|
695
|
+
boost: null,
|
|
696
|
+
fuzzy: null,
|
|
697
|
+
not: false,
|
|
698
|
+
name: null,
|
|
699
|
+
operation: null,
|
|
700
|
+
tagged: false,
|
|
701
|
+
value: null,
|
|
702
|
+
values: null
|
|
703
|
+
};
|
|
704
|
+
params.name = this.field;
|
|
705
|
+
switch (this.operator) {
|
|
706
|
+
case 'equals':
|
|
707
|
+
if (this.type === 'date') {
|
|
708
|
+
params.operation = 'BETWEEN';
|
|
709
|
+
params.values = createDbDateRange(this.value, this.specificity);
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
712
|
+
params.operation = 'IS';
|
|
713
|
+
params.value = this.value;
|
|
714
|
+
}
|
|
715
|
+
break;
|
|
716
|
+
case 'n_equals':
|
|
717
|
+
if (this.type === 'date') {
|
|
718
|
+
params.operation = 'BETWEEN';
|
|
719
|
+
params.not = true;
|
|
720
|
+
params.values = createDbDateRange(this.value, this.specificity);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
params.operation = 'IS';
|
|
724
|
+
params.value = this.value;
|
|
725
|
+
params.not = true;
|
|
726
|
+
}
|
|
727
|
+
break;
|
|
728
|
+
case 'contains':
|
|
729
|
+
params.operation = 'CONTAINS';
|
|
730
|
+
params.value = this.value;
|
|
731
|
+
break;
|
|
732
|
+
case 'n_contains':
|
|
733
|
+
params.operation = 'CONTAINS';
|
|
734
|
+
params.value = this.value;
|
|
735
|
+
params.not = true;
|
|
736
|
+
break;
|
|
737
|
+
case 'starts_with':
|
|
738
|
+
params.operation = 'STARTS_WITH';
|
|
739
|
+
params.value = this.value;
|
|
740
|
+
break;
|
|
741
|
+
case 'ends_with':
|
|
742
|
+
params.operation = 'ENDS_WITH';
|
|
743
|
+
params.value = this.value;
|
|
744
|
+
break;
|
|
745
|
+
case 'empty':
|
|
746
|
+
params.operation = 'IS_BLANK';
|
|
747
|
+
break;
|
|
748
|
+
case 'n_empty':
|
|
749
|
+
params.operation = 'IS_NOT_BLANK';
|
|
750
|
+
break;
|
|
751
|
+
case 'less_than':
|
|
752
|
+
params.operation = 'LESS_THAN';
|
|
753
|
+
params.value = this.value;
|
|
754
|
+
break;
|
|
755
|
+
case 'less_than_equal':
|
|
756
|
+
params.operation = 'LESS_THAN_EQUAL';
|
|
757
|
+
params.value = this.value;
|
|
758
|
+
break;
|
|
759
|
+
case 'greater_than':
|
|
760
|
+
params.operation = 'GREATER_THAN';
|
|
761
|
+
params.value = this.value;
|
|
762
|
+
break;
|
|
763
|
+
case 'greater_than_equal':
|
|
764
|
+
params.operation = 'GREATER_THAN_EQUAL';
|
|
765
|
+
params.value = this.value;
|
|
766
|
+
break;
|
|
767
|
+
case 'between':
|
|
768
|
+
params.operation = 'BETWEEN';
|
|
769
|
+
if (this.type === 'date') {
|
|
770
|
+
params.values = createDbDateRange(this.value, this.specificity);
|
|
771
|
+
}
|
|
772
|
+
else {
|
|
773
|
+
params.values = [this.value.start, this.value.end];
|
|
774
|
+
}
|
|
775
|
+
break;
|
|
776
|
+
case 'in':
|
|
777
|
+
params.operation = 'IN';
|
|
778
|
+
params.values = this.value ?? [];
|
|
779
|
+
break;
|
|
780
|
+
default:
|
|
781
|
+
throw Error(`${this.operator} operator not supported by db params.`);
|
|
782
|
+
}
|
|
783
|
+
return params;
|
|
784
|
+
}
|
|
785
|
+
// --------------------------------------------------------------------------
|
|
786
|
+
// Private Methods
|
|
787
|
+
// --------------------------------------------------------------------------
|
|
788
|
+
_buildText() {
|
|
789
|
+
if (this.isEmpty()) {
|
|
790
|
+
this.text = '';
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
if (this.operator === 'between') {
|
|
794
|
+
this.text = `${this._format(this.value?.start)} - ${this._format(this.value?.end)}`;
|
|
795
|
+
}
|
|
796
|
+
else if (this.operator === 'in') {
|
|
797
|
+
this.text = this.value.map((v) => this._format(v)).join(',');
|
|
798
|
+
}
|
|
799
|
+
else {
|
|
800
|
+
this.text = this._format(this.value);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
_format(value) {
|
|
804
|
+
if (value instanceof Date && !isNaN(value.getTime())) {
|
|
805
|
+
const month = String(value.getUTCMonth() + 1).padStart(2, '0');
|
|
806
|
+
const day = String(value.getUTCDate()).padStart(2, '0');
|
|
807
|
+
const year = value.getUTCFullYear();
|
|
808
|
+
return `${month}/${day}/${year}`;
|
|
809
|
+
}
|
|
810
|
+
if (typeof value === 'boolean') {
|
|
811
|
+
return value ? 'Yes' : 'No';
|
|
812
|
+
}
|
|
813
|
+
return String(value ?? '');
|
|
814
|
+
}
|
|
815
|
+
/** Parse a text value according to this query's type */
|
|
816
|
+
_parse(text) {
|
|
817
|
+
if (typeof text === 'undefined' || text === null || text === '') {
|
|
818
|
+
return null;
|
|
819
|
+
}
|
|
820
|
+
if (this.type) {
|
|
821
|
+
switch (this.type) {
|
|
822
|
+
case 'boolean': {
|
|
823
|
+
const lcInput = text.toString().toLowerCase();
|
|
824
|
+
if (text === true) {
|
|
825
|
+
return true;
|
|
826
|
+
}
|
|
827
|
+
if (text === false) {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
if (lcInput === 'yes') {
|
|
831
|
+
return true;
|
|
832
|
+
}
|
|
833
|
+
if (lcInput === 'no') {
|
|
834
|
+
return false;
|
|
835
|
+
}
|
|
836
|
+
if (lcInput === '1') {
|
|
837
|
+
return true;
|
|
838
|
+
}
|
|
839
|
+
if (lcInput === '0') {
|
|
840
|
+
return false;
|
|
841
|
+
}
|
|
842
|
+
return null;
|
|
843
|
+
}
|
|
844
|
+
case 'date':
|
|
845
|
+
return this._parseDate(text);
|
|
846
|
+
case 'number': {
|
|
847
|
+
const num = Number(text.toString().replace(/[$,]+/g, ''));
|
|
848
|
+
if (isNaN(num)) {
|
|
849
|
+
return null;
|
|
850
|
+
}
|
|
851
|
+
return num;
|
|
852
|
+
}
|
|
853
|
+
default:
|
|
854
|
+
return text.toString();
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
return text;
|
|
858
|
+
}
|
|
859
|
+
/** Parse a date string into a UTC Date */
|
|
860
|
+
_parseDate(input) {
|
|
861
|
+
if (typeof input === 'undefined' || input === null || input === '') {
|
|
862
|
+
return null;
|
|
863
|
+
}
|
|
864
|
+
const months = {
|
|
865
|
+
jan: 0,
|
|
866
|
+
january: 0,
|
|
867
|
+
feb: 1,
|
|
868
|
+
february: 1,
|
|
869
|
+
mar: 2,
|
|
870
|
+
march: 2,
|
|
871
|
+
apr: 3,
|
|
872
|
+
april: 3,
|
|
873
|
+
may: 4,
|
|
874
|
+
jun: 5,
|
|
875
|
+
june: 5,
|
|
876
|
+
jul: 6,
|
|
877
|
+
july: 6,
|
|
878
|
+
aug: 7,
|
|
879
|
+
august: 7,
|
|
880
|
+
sep: 8,
|
|
881
|
+
september: 8,
|
|
882
|
+
oct: 9,
|
|
883
|
+
october: 9,
|
|
884
|
+
nov: 10,
|
|
885
|
+
november: 10,
|
|
886
|
+
dec: 11,
|
|
887
|
+
december: 11
|
|
888
|
+
};
|
|
889
|
+
let m;
|
|
890
|
+
// MM/dd/yy, M/d/yy (2-digit year)
|
|
891
|
+
m = input.match(/^(\d{1,2})\/(\d{1,2})\/(\d{2})$/);
|
|
892
|
+
if (m) {
|
|
893
|
+
return new Date(Date.UTC(2000 + parseInt(m[3]), parseInt(m[1]) - 1, parseInt(m[2])));
|
|
894
|
+
}
|
|
895
|
+
// MM/dd/yyyy, M/d/yyyy (4-digit year)
|
|
896
|
+
m = input.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
|
|
897
|
+
if (m) {
|
|
898
|
+
return new Date(Date.UTC(parseInt(m[3]), parseInt(m[1]) - 1, parseInt(m[2])));
|
|
899
|
+
}
|
|
900
|
+
// MM/dd/yyyy, t — date with time (4-digit year only, matching NGX)
|
|
901
|
+
m = input.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4}),?\s+(\d{1,2}):(\d{2})\s*(AM|PM)?$/i);
|
|
902
|
+
if (m) {
|
|
903
|
+
let hour = parseInt(m[4]);
|
|
904
|
+
if (m[6]) {
|
|
905
|
+
if (m[6].toUpperCase() === 'PM' && hour !== 12) {
|
|
906
|
+
hour += 12;
|
|
907
|
+
}
|
|
908
|
+
if (m[6].toUpperCase() === 'AM' && hour === 12) {
|
|
909
|
+
hour = 0;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return new Date(Date.UTC(parseInt(m[3]), parseInt(m[1]) - 1, parseInt(m[2]), hour, parseInt(m[5])));
|
|
913
|
+
}
|
|
914
|
+
// "Month DD, YYYY, time" with optional weekday
|
|
915
|
+
// Handles: "Oct 14, 1983, 9:32 AM", "October 14, 1983, 9:32 AM", "Tuesday, October 14, 1983, 9:32 AM"
|
|
916
|
+
m = input.match(/^(?:[A-Za-z]+,\s+)?([A-Za-z]+)\s+(\d{1,2}),?\s+(\d{4}),?\s+(\d{1,2}):(\d{2})\s*(AM|PM)?$/i);
|
|
917
|
+
if (m) {
|
|
918
|
+
const month = months[m[1].toLowerCase()];
|
|
919
|
+
if (month !== undefined) {
|
|
920
|
+
let hour = parseInt(m[4]);
|
|
921
|
+
if (m[6]) {
|
|
922
|
+
if (m[6].toUpperCase() === 'PM' && hour !== 12) {
|
|
923
|
+
hour += 12;
|
|
924
|
+
}
|
|
925
|
+
if (m[6].toUpperCase() === 'AM' && hour === 12) {
|
|
926
|
+
hour = 0;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return new Date(Date.UTC(parseInt(m[3]), month, parseInt(m[2]), hour, parseInt(m[5])));
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
// "Month DD, YYYY" with optional weekday
|
|
933
|
+
// Handles: "Oct 14, 1983", "October 14, 1983", "Tuesday, October 14, 1983"
|
|
934
|
+
m = input.match(/^(?:[A-Za-z]+,\s+)?([A-Za-z]+)\s+(\d{1,2}),?\s+(\d{4})$/);
|
|
935
|
+
if (m) {
|
|
936
|
+
const month = months[m[1].toLowerCase()];
|
|
937
|
+
if (month !== undefined) {
|
|
938
|
+
return new Date(Date.UTC(parseInt(m[3]), month, parseInt(m[2])));
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
// yy (2-digit year)
|
|
942
|
+
m = input.match(/^(\d{2})$/);
|
|
943
|
+
if (m) {
|
|
944
|
+
return new Date(Date.UTC(2000 + parseInt(m[1]), 0, 1));
|
|
945
|
+
}
|
|
946
|
+
// yyyy (4-digit year)
|
|
947
|
+
m = input.match(/^(\d{4})$/);
|
|
948
|
+
if (m) {
|
|
949
|
+
return new Date(Date.UTC(parseInt(m[1]), 0, 1));
|
|
950
|
+
}
|
|
951
|
+
// "MMM yyyy" or "MMMM yyyy" — "Aug 2015", "August 2015"
|
|
952
|
+
m = input.match(/^([A-Za-z]+)\s+(\d{4})$/);
|
|
953
|
+
if (m) {
|
|
954
|
+
const month = months[m[1].toLowerCase()];
|
|
955
|
+
if (month !== undefined) {
|
|
956
|
+
return new Date(Date.UTC(parseInt(m[2]), month, 1));
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
return null;
|
|
960
|
+
}
|
|
961
|
+
/** Build a KQL display string from current operator/value state */
|
|
962
|
+
_buildKql() {
|
|
963
|
+
if (this.isEmpty()) {
|
|
964
|
+
this.kql = '';
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
switch (this.operator) {
|
|
968
|
+
case 'equals':
|
|
969
|
+
this.kql = `= ${this.text}`;
|
|
970
|
+
break;
|
|
971
|
+
case 'n_equals':
|
|
972
|
+
this.kql = `!= ${this.text}`;
|
|
973
|
+
break;
|
|
974
|
+
case 'contains':
|
|
975
|
+
this.kql = `~ ${this.text}`;
|
|
976
|
+
break;
|
|
977
|
+
case 'n_contains':
|
|
978
|
+
this.kql = `!~ ${this.text}`;
|
|
979
|
+
break;
|
|
980
|
+
case 'starts_with':
|
|
981
|
+
this.kql = `${this.text}*`;
|
|
982
|
+
break;
|
|
983
|
+
case 'ends_with':
|
|
984
|
+
this.kql = `*${this.text}`;
|
|
985
|
+
break;
|
|
986
|
+
case 'empty':
|
|
987
|
+
this.kql = 'EMPTY';
|
|
988
|
+
break;
|
|
989
|
+
case 'n_empty':
|
|
990
|
+
this.kql = 'NOT EMPTY';
|
|
991
|
+
break;
|
|
992
|
+
case 'less_than':
|
|
993
|
+
this.kql = `< ${this.text}`;
|
|
994
|
+
break;
|
|
995
|
+
case 'less_than_equal':
|
|
996
|
+
this.kql = `<= ${this.text}`;
|
|
997
|
+
break;
|
|
998
|
+
case 'greater_than':
|
|
999
|
+
this.kql = `> ${this.text}`;
|
|
1000
|
+
break;
|
|
1001
|
+
case 'greater_than_equal':
|
|
1002
|
+
this.kql = `>= ${this.text}`;
|
|
1003
|
+
break;
|
|
1004
|
+
case 'between':
|
|
1005
|
+
this.kql = `[${this._format(this.value?.start ?? '')} TO ${this._format(this.value?.end ?? '')}]`;
|
|
1006
|
+
break;
|
|
1007
|
+
case 'in':
|
|
1008
|
+
this.kql = `(${this.value.map((v) => this._format(v)).join(',')})`;
|
|
1009
|
+
break;
|
|
1010
|
+
default:
|
|
1011
|
+
throw Error(`Unknown operator ${this.operator}`);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
//# sourceMappingURL=query.js.map
|