@nymphjs/query-parser 1.0.0-alpha.4 → 1.0.0-alpha.40

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.
@@ -1,31 +1,77 @@
1
1
  import type { EntityConstructor, Options, Selector } from '@nymphjs/client';
2
+ import splitn from '@sciactive/splitn';
3
+
4
+ export type BareQueryHandler = (
5
+ input: string,
6
+ entityClass?: EntityConstructor,
7
+ defaultFields?: string[]
8
+ ) => Partial<Selector>;
9
+
10
+ export type QRefMap = {
11
+ [k: string]: { class: EntityConstructor; defaultFields?: string[] };
12
+ };
2
13
 
3
14
  export default function queryParser<
4
15
  T extends EntityConstructor = EntityConstructor
5
- >(
6
- query: string,
7
- entityClass: T,
8
- defaultFields: string[] = ['name'],
9
- qrefMap: {
10
- [k: string]: { class: EntityConstructor; defaultFields?: string[] };
11
- } = {}
12
- ): [Options<T>, ...Selector[]] {
16
+ >({
17
+ query,
18
+ entityClass,
19
+ defaultFields = ['name'],
20
+ qrefMap = {},
21
+ bareHandler = (input, _class, defaultFields = ['name']) => {
22
+ if (!input.match(/[_%]/)) {
23
+ input = `%${input}%`;
24
+ }
25
+ if (defaultFields.length) {
26
+ return {
27
+ type: '|',
28
+ ilike: defaultFields.map((field) => [field, input]) as [
29
+ string,
30
+ string
31
+ ][],
32
+ };
33
+ }
34
+ return {};
35
+ },
36
+ }: {
37
+ query: string;
38
+ entityClass: T;
39
+ defaultFields?: string[];
40
+ qrefMap?: QRefMap;
41
+ bareHandler?: BareQueryHandler;
42
+ }): [Options<T>, ...Selector[]] {
13
43
  const options: Options<T> = { class: entityClass };
14
44
  return [
15
45
  options,
16
- ...selectorsParser(query, '&', defaultFields, qrefMap, options),
46
+ ...selectorsParser({
47
+ query,
48
+ entityClass,
49
+ type: '&',
50
+ defaultFields,
51
+ qrefMap,
52
+ options,
53
+ bareHandler,
54
+ }),
17
55
  ];
18
56
  }
19
57
 
20
- function selectorsParser(
21
- query: string,
22
- type: '&' | '|' | '!&' | '!|' = '&',
23
- defaultFields: string[],
24
- qrefMap: {
25
- [k: string]: { class: EntityConstructor; defaultFields?: string[] };
26
- },
27
- options?: Options
28
- ): Selector[] {
58
+ function selectorsParser({
59
+ query,
60
+ entityClass,
61
+ type,
62
+ defaultFields,
63
+ qrefMap,
64
+ options,
65
+ bareHandler,
66
+ }: {
67
+ query: string;
68
+ entityClass: EntityConstructor;
69
+ type: '&' | '|' | '!&' | '!|';
70
+ defaultFields: string[];
71
+ qrefMap: QRefMap;
72
+ options?: Options;
73
+ bareHandler: BareQueryHandler;
74
+ }): Selector[] {
29
75
  const selector: Selector = { type };
30
76
  let curQuery = query;
31
77
 
@@ -91,12 +137,24 @@ function selectorsParser(
91
137
  selectorQuery = selectorQuery.slice(1);
92
138
  }
93
139
  selector.selector.push(
94
- ...selectorsParser(selectorQuery, type, defaultFields, qrefMap)
140
+ ...selectorsParser({
141
+ query: selectorQuery,
142
+ entityClass,
143
+ type,
144
+ defaultFields,
145
+ qrefMap,
146
+ bareHandler,
147
+ })
95
148
  );
96
149
  }
97
150
  }
98
151
 
99
- curQuery = selectorParser(curQuery, selector, qrefMap);
152
+ curQuery = selectorParser({
153
+ query: curQuery,
154
+ selector,
155
+ qrefMap,
156
+ bareHandler,
157
+ });
100
158
 
101
159
  if (options) {
102
160
  const limitRegex = /(?: |^)limit:(\d+)(?= |$)/;
@@ -124,18 +182,14 @@ function selectorsParser(
124
182
  curQuery = curQuery.trim();
125
183
 
126
184
  if (curQuery.length) {
127
- if (!curQuery.match(/[_%]/)) {
128
- curQuery = `%${curQuery}%`;
129
- }
130
- if (defaultFields.length) {
185
+ const bareSelector = bareHandler(curQuery, entityClass, defaultFields);
186
+
187
+ if (Object.keys(bareSelector).length) {
131
188
  return [
132
189
  ...(Object.keys(selector).length > 1 ? [selector] : []),
133
190
  {
134
191
  type: '|',
135
- ilike: defaultFields.map((field) => [field, curQuery]) as [
136
- string,
137
- string
138
- ][],
192
+ ...bareSelector,
139
193
  },
140
194
  ];
141
195
  }
@@ -155,13 +209,17 @@ function selectorsParser(
155
209
  return [selector];
156
210
  }
157
211
 
158
- function selectorParser(
159
- query: string,
160
- selector: Selector,
161
- qrefMap: {
162
- [k: string]: { class: EntityConstructor; defaultFields?: string[] };
163
- }
164
- ): string {
212
+ function selectorParser({
213
+ query,
214
+ selector,
215
+ qrefMap,
216
+ bareHandler,
217
+ }: {
218
+ query: string;
219
+ selector: Selector;
220
+ qrefMap: QRefMap;
221
+ bareHandler: BareQueryHandler;
222
+ }): string {
165
223
  let curQuery = query;
166
224
 
167
225
  // eg. user<{User name="Hunter"}> or user!<{User name="Hunter"}>
@@ -172,19 +230,20 @@ function selectorParser(
172
230
  selector['!qref'] = [];
173
231
  for (let match of qrefMatch) {
174
232
  try {
175
- let [name, value] = match.trim().slice(0, -1).split('<', 2);
233
+ let [name, value] = splitn(match.trim().slice(0, -1), '<', 2);
176
234
  value = unQuoteCurlies(value.slice(1, -1));
177
235
  let [className, qrefQuery] = value.split(' ', 2);
178
236
  const EntityClass = qrefMap[className].class;
179
237
  if (EntityClass == null) {
180
238
  continue;
181
239
  }
182
- const qref = queryParser(
183
- qrefQuery,
184
- EntityClass,
185
- qrefMap[className].defaultFields,
186
- qrefMap
187
- );
240
+ const qref = queryParser({
241
+ query: qrefQuery,
242
+ entityClass: EntityClass,
243
+ defaultFields: qrefMap[className].defaultFields,
244
+ qrefMap,
245
+ bareHandler,
246
+ });
188
247
  if (name.endsWith('!')) {
189
248
  selector['!qref'].push([name.slice(0, -1), qref]);
190
249
  } else {
@@ -455,6 +514,33 @@ function selectorParser(
455
514
  }
456
515
  curQuery = curQuery.replace(truthyRegex, '');
457
516
 
517
+ // eg. <archived> or <!archived>
518
+ const tagRegex = /(?: |^)<(!?\w+)>(?= |$)/g;
519
+ const tagMatch = curQuery.match(tagRegex);
520
+ if (tagMatch) {
521
+ selector.tag = [];
522
+ selector['!tag'] = [];
523
+ for (let match of tagMatch) {
524
+ try {
525
+ let name = match.trim().replace(/^<|>$/g, '');
526
+ if (name.startsWith('!')) {
527
+ selector['!tag'].push(name.slice(1));
528
+ } else {
529
+ selector.tag.push(name);
530
+ }
531
+ } catch (e: any) {
532
+ continue;
533
+ }
534
+ }
535
+ if (!selector.tag.length) {
536
+ delete selector.tag;
537
+ }
538
+ if (!selector['!tag'].length) {
539
+ delete selector['!tag'];
540
+ }
541
+ }
542
+ curQuery = curQuery.replace(tagRegex, '');
543
+
458
544
  // eg. cdate>15
459
545
  const gtRegex = /(?: |^)(\w+)>(-?\d+(?:\.\d+)?)(?= |$)/g;
460
546
  const gtMatch = curQuery.match(gtRegex);