search-input-query-parser 0.3.0 → 0.4.0
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/README.md +4 -2
- package/dist/cjs/validate-expression-fields.js +24 -1
- package/dist/cjs/validator.js +1 -0
- package/dist/esm/validate-expression-fields.js +24 -1
- package/dist/esm/validator.js +1 -0
- package/dist/types/validator.d.ts +1 -0
- package/package.json +1 -1
- package/src/parser.test.ts +35 -0
- package/src/validate-expression-fields.ts +25 -1
- package/src/validator.ts +1 -0
package/README.md
CHANGED
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
const schemas: FieldSchema[] = [
|
|
21
21
|
{ name: 'title', type: 'string' },
|
|
22
22
|
{ name: 'price', type: 'number' },
|
|
23
|
-
{ name: 'date', type: 'date' }
|
|
23
|
+
{ name: 'date', type: 'date' },
|
|
24
|
+
{ name: 'in_stock', type: 'boolean' }
|
|
24
25
|
];
|
|
25
26
|
|
|
26
27
|
// Parse a search query
|
|
@@ -58,7 +59,8 @@ const searchableColumns = ['title', 'description'];
|
|
|
58
59
|
const schemas: FieldSchema[] = [
|
|
59
60
|
{ name: 'title', type: 'string' },
|
|
60
61
|
{ name: 'price', type: 'number' },
|
|
61
|
-
{ name: 'date', type: 'date' }
|
|
62
|
+
{ name: 'date', type: 'date' },
|
|
63
|
+
{ name: 'in_stock', type: 'boolean' }
|
|
62
64
|
];
|
|
63
65
|
|
|
64
66
|
// Convert a search query to SQL
|
|
@@ -92,6 +92,19 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
break;
|
|
95
|
+
case "boolean":
|
|
96
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
97
|
+
errors.push({
|
|
98
|
+
message: "Invalid boolean value",
|
|
99
|
+
code: validator_1.SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
100
|
+
position: expr.position +
|
|
101
|
+
expr.field.length +
|
|
102
|
+
3 +
|
|
103
|
+
index * (value.length + 1),
|
|
104
|
+
length: value.length,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
95
108
|
}
|
|
96
109
|
});
|
|
97
110
|
}
|
|
@@ -100,7 +113,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
100
113
|
case "WILDCARD": {
|
|
101
114
|
// For wildcard patterns, validate against field type constraints
|
|
102
115
|
const schema = schemas.get(expr.prefix.toLowerCase());
|
|
103
|
-
if ((schema === null || schema === void 0 ? void 0 : schema.type) === "number" || (schema === null || schema === void 0 ? void 0 : schema.type) === "date") {
|
|
116
|
+
if ((schema === null || schema === void 0 ? void 0 : schema.type) === "number" || (schema === null || schema === void 0 ? void 0 : schema.type) === "date" || (schema === null || schema === void 0 ? void 0 : schema.type) === "boolean") {
|
|
104
117
|
errors.push({
|
|
105
118
|
code: validator_1.SearchQueryErrorCode.VALUE_WILDCARD_NOT_PERMITTED,
|
|
106
119
|
value: schema.type,
|
|
@@ -239,6 +252,16 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
239
252
|
}
|
|
240
253
|
}
|
|
241
254
|
}
|
|
255
|
+
if (schema.type === "boolean") {
|
|
256
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
257
|
+
errors.push({
|
|
258
|
+
message: "Invalid boolean value",
|
|
259
|
+
code: validator_1.SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
260
|
+
position: valueStartPosition,
|
|
261
|
+
length: value.length,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
242
265
|
break;
|
|
243
266
|
}
|
|
244
267
|
}
|
package/dist/cjs/validator.js
CHANGED
|
@@ -29,6 +29,7 @@ var SearchQueryErrorCode;
|
|
|
29
29
|
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_MISSING"] = 3005] = "VALUE_RANGE_MISSING";
|
|
30
30
|
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_START_EXCEEDS_END"] = 3006] = "VALUE_RANGE_START_EXCEEDS_END";
|
|
31
31
|
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_WILDCARD_NOT_PERMITTED"] = 3007] = "VALUE_WILDCARD_NOT_PERMITTED";
|
|
32
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_BOOLEAN_INVALID"] = 3008] = "VALUE_BOOLEAN_INVALID";
|
|
32
33
|
// Wildcard Errors (4000-4999)
|
|
33
34
|
SearchQueryErrorCode[SearchQueryErrorCode["WILDCARD_POSITION_INVALID"] = 4001] = "WILDCARD_POSITION_INVALID";
|
|
34
35
|
SearchQueryErrorCode[SearchQueryErrorCode["WILDCARD_MULTIPLE_NOT_PERMITTED"] = 4002] = "WILDCARD_MULTIPLE_NOT_PERMITTED";
|
|
@@ -89,6 +89,19 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
break;
|
|
92
|
+
case "boolean":
|
|
93
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
94
|
+
errors.push({
|
|
95
|
+
message: "Invalid boolean value",
|
|
96
|
+
code: SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
97
|
+
position: expr.position +
|
|
98
|
+
expr.field.length +
|
|
99
|
+
3 +
|
|
100
|
+
index * (value.length + 1),
|
|
101
|
+
length: value.length,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
break;
|
|
92
105
|
}
|
|
93
106
|
});
|
|
94
107
|
}
|
|
@@ -97,7 +110,7 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
97
110
|
case "WILDCARD": {
|
|
98
111
|
// For wildcard patterns, validate against field type constraints
|
|
99
112
|
const schema = schemas.get(expr.prefix.toLowerCase());
|
|
100
|
-
if ((schema === null || schema === void 0 ? void 0 : schema.type) === "number" || (schema === null || schema === void 0 ? void 0 : schema.type) === "date") {
|
|
113
|
+
if ((schema === null || schema === void 0 ? void 0 : schema.type) === "number" || (schema === null || schema === void 0 ? void 0 : schema.type) === "date" || (schema === null || schema === void 0 ? void 0 : schema.type) === "boolean") {
|
|
101
114
|
errors.push({
|
|
102
115
|
code: SearchQueryErrorCode.VALUE_WILDCARD_NOT_PERMITTED,
|
|
103
116
|
value: schema.type,
|
|
@@ -236,6 +249,16 @@ const validateFieldValue = (expr, allowedFields, errors, schemas) => {
|
|
|
236
249
|
}
|
|
237
250
|
}
|
|
238
251
|
}
|
|
252
|
+
if (schema.type === "boolean") {
|
|
253
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
254
|
+
errors.push({
|
|
255
|
+
message: "Invalid boolean value",
|
|
256
|
+
code: SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
257
|
+
position: valueStartPosition,
|
|
258
|
+
length: value.length,
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
}
|
|
239
262
|
break;
|
|
240
263
|
}
|
|
241
264
|
}
|
package/dist/esm/validator.js
CHANGED
|
@@ -26,6 +26,7 @@ export var SearchQueryErrorCode;
|
|
|
26
26
|
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_MISSING"] = 3005] = "VALUE_RANGE_MISSING";
|
|
27
27
|
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_RANGE_START_EXCEEDS_END"] = 3006] = "VALUE_RANGE_START_EXCEEDS_END";
|
|
28
28
|
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_WILDCARD_NOT_PERMITTED"] = 3007] = "VALUE_WILDCARD_NOT_PERMITTED";
|
|
29
|
+
SearchQueryErrorCode[SearchQueryErrorCode["VALUE_BOOLEAN_INVALID"] = 3008] = "VALUE_BOOLEAN_INVALID";
|
|
29
30
|
// Wildcard Errors (4000-4999)
|
|
30
31
|
SearchQueryErrorCode[SearchQueryErrorCode["WILDCARD_POSITION_INVALID"] = 4001] = "WILDCARD_POSITION_INVALID";
|
|
31
32
|
SearchQueryErrorCode[SearchQueryErrorCode["WILDCARD_MULTIPLE_NOT_PERMITTED"] = 4002] = "WILDCARD_MULTIPLE_NOT_PERMITTED";
|
|
@@ -21,6 +21,7 @@ export declare enum SearchQueryErrorCode {
|
|
|
21
21
|
VALUE_RANGE_MISSING = 3005,
|
|
22
22
|
VALUE_RANGE_START_EXCEEDS_END = 3006,
|
|
23
23
|
VALUE_WILDCARD_NOT_PERMITTED = 3007,
|
|
24
|
+
VALUE_BOOLEAN_INVALID = 3008,
|
|
24
25
|
WILDCARD_POSITION_INVALID = 4001,
|
|
25
26
|
WILDCARD_MULTIPLE_NOT_PERMITTED = 4002,
|
|
26
27
|
IN_LIST_EMPTY = 5001,
|
package/package.json
CHANGED
package/src/parser.test.ts
CHANGED
|
@@ -33,6 +33,7 @@ describe("Search Query Parser", () => {
|
|
|
33
33
|
{ name: "amount", type: "number" },
|
|
34
34
|
{ name: "date", type: "date" },
|
|
35
35
|
{ name: "title", type: "string" },
|
|
36
|
+
{ name: "in_stock", type: "boolean" },
|
|
36
37
|
];
|
|
37
38
|
|
|
38
39
|
const testSchemaQuery = (input: string, expected: string) => {
|
|
@@ -334,6 +335,29 @@ describe("Search Query Parser", () => {
|
|
|
334
335
|
});
|
|
335
336
|
});
|
|
336
337
|
|
|
338
|
+
describe("Boolean field support", () => {
|
|
339
|
+
test("parses boolean values", () => {
|
|
340
|
+
testSchemaQuery("in_stock:true", "in_stock:true");
|
|
341
|
+
testSchemaQuery("in_stock:false", "in_stock:false");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test("parses boolean fields with other fields", () => {
|
|
345
|
+
testSchemaQuery("title:red AND in_stock:true", "(title:red AND in_stock:true)");
|
|
346
|
+
testSchemaQuery("in_stock:true AND amount:42", "(in_stock:true AND amount:42)");
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test("parses boolean fields with complex expressions", () => {
|
|
350
|
+
testSchemaQuery(
|
|
351
|
+
"title:red AND in_stock:true OR amount:42",
|
|
352
|
+
"((title:red AND in_stock:true) OR amount:42)"
|
|
353
|
+
);
|
|
354
|
+
testSchemaQuery(
|
|
355
|
+
"title:red AND (in_stock:true OR amount:42)",
|
|
356
|
+
"(title:red AND (in_stock:true OR amount:42))"
|
|
357
|
+
);
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
|
|
337
361
|
describe("Range Query Support", () => {
|
|
338
362
|
test("parses comparison operators", () => {
|
|
339
363
|
testSchemaQuery("price:>100", "price:>100");
|
|
@@ -750,6 +774,17 @@ describe("Search Query Parser", () => {
|
|
|
750
774
|
},
|
|
751
775
|
]);
|
|
752
776
|
});
|
|
777
|
+
|
|
778
|
+
test("handles invalid boolean values", () => {
|
|
779
|
+
testSchemaErrorQuery("in_stock:maybe", [
|
|
780
|
+
{
|
|
781
|
+
message: "Invalid boolean value",
|
|
782
|
+
code: SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
783
|
+
position: 9,
|
|
784
|
+
length: 5,
|
|
785
|
+
},
|
|
786
|
+
]);
|
|
787
|
+
});
|
|
753
788
|
});
|
|
754
789
|
|
|
755
790
|
describe("Wildcard Error Cases", () => {
|
|
@@ -119,6 +119,20 @@ const validateFieldValue = (
|
|
|
119
119
|
});
|
|
120
120
|
}
|
|
121
121
|
break;
|
|
122
|
+
case "boolean":
|
|
123
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
124
|
+
errors.push({
|
|
125
|
+
message: "Invalid boolean value",
|
|
126
|
+
code: SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
127
|
+
position:
|
|
128
|
+
expr.position +
|
|
129
|
+
expr.field.length +
|
|
130
|
+
3 +
|
|
131
|
+
index * (value.length + 1),
|
|
132
|
+
length: value.length,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
122
136
|
}
|
|
123
137
|
});
|
|
124
138
|
}
|
|
@@ -128,7 +142,7 @@ const validateFieldValue = (
|
|
|
128
142
|
case "WILDCARD": {
|
|
129
143
|
// For wildcard patterns, validate against field type constraints
|
|
130
144
|
const schema = schemas.get(expr.prefix.toLowerCase());
|
|
131
|
-
if (schema?.type === "number" || schema?.type === "date") {
|
|
145
|
+
if (schema?.type === "number" || schema?.type === "date" || schema?.type === "boolean") {
|
|
132
146
|
errors.push({
|
|
133
147
|
code: SearchQueryErrorCode.VALUE_WILDCARD_NOT_PERMITTED,
|
|
134
148
|
value: schema.type,
|
|
@@ -285,6 +299,16 @@ const validateFieldValue = (
|
|
|
285
299
|
}
|
|
286
300
|
}
|
|
287
301
|
}
|
|
302
|
+
if (schema.type === "boolean") {
|
|
303
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
304
|
+
errors.push({
|
|
305
|
+
message: "Invalid boolean value",
|
|
306
|
+
code: SearchQueryErrorCode.VALUE_BOOLEAN_INVALID,
|
|
307
|
+
position: valueStartPosition,
|
|
308
|
+
length: value.length,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
288
312
|
break;
|
|
289
313
|
}
|
|
290
314
|
}
|
package/src/validator.ts
CHANGED