search-input-query-parser 0.1.1 → 0.1.3

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.
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.searchStringToTsVectorSql = exports.searchQueryToTsVectorSql = void 0;
4
+ const parser_1 = require("./parser");
5
+ // Constants
6
+ const SPECIAL_CHARS = ["%", "_"];
7
+ const ESCAPE_CHAR = "\\";
8
+ // Helper Functions
9
+ const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
+ const stripQuotes = (value) => {
11
+ if (value.startsWith('"') && value.endsWith('"')) {
12
+ return value.slice(1, -1);
13
+ }
14
+ return value;
15
+ };
16
+ const cleanQuotedString = (value, stripOuterQuotes = true) => {
17
+ // First strip the outer quotes if requested
18
+ let cleaned = stripOuterQuotes ? stripQuotes(value) : value;
19
+ // Replace escaped quotes with regular quotes
20
+ cleaned = cleaned.replace(/\\"/g, '"');
21
+ // Clean up any remaining escape characters
22
+ cleaned = cleaned.replace(/\\\\/g, "\\");
23
+ return cleaned;
24
+ };
25
+ // Create a new parameter placeholder and update state
26
+ const nextParam = (state) => {
27
+ const paramName = `$${state.paramCounter}`;
28
+ const newState = {
29
+ ...state,
30
+ paramCounter: state.paramCounter + 1,
31
+ };
32
+ return [paramName, newState];
33
+ };
34
+ // Add a value to the state and return updated state
35
+ const addValue = (state, value) => ({
36
+ ...state,
37
+ values: [...state.values, value],
38
+ });
39
+ /**
40
+ * Convert a wildcard pattern to SQL
41
+ */
42
+ const wildcardPatternToSql = (expr, state) => {
43
+ if (expr.prefix === "") {
44
+ return ["1=1", state];
45
+ }
46
+ const [paramName, newState] = nextParam(state);
47
+ const cleanedPrefix = cleanQuotedString(expr.prefix);
48
+ const langConfig = state.language || "english";
49
+ const conditions = state.searchableColumns.map((column) => `to_tsvector('${langConfig}', ${column})`);
50
+ const tsvectorCondition = `(${conditions.join(" || ")}) @@ to_tsquery('${langConfig}', ${paramName})`;
51
+ return [tsvectorCondition, addValue(newState, `${cleanedPrefix}:*`)];
52
+ };
53
+ /**
54
+ * Convert a search term to SQL conditions based on search type
55
+ */
56
+ const searchTermToSql = (value, state) => {
57
+ const [paramName, newState] = nextParam(state);
58
+ const hasWildcard = value.endsWith("*");
59
+ const cleanedValue = cleanQuotedString(value);
60
+ const baseValue = hasWildcard ? cleanedValue.slice(0, -1) : cleanedValue;
61
+ const langConfig = state.language || "english";
62
+ const conditions = state.searchableColumns.map((column) => `to_tsvector('${langConfig}', ${column})`);
63
+ const tsvectorCondition = `(${conditions.join(" || ")}) @@ ${hasWildcard ? "to_tsquery" : "plainto_tsquery"}('${langConfig}', ${paramName})`;
64
+ return [
65
+ tsvectorCondition,
66
+ addValue(newState, hasWildcard ? `${baseValue}:*` : baseValue),
67
+ ];
68
+ };
69
+ /**
70
+ * Convert a field:value pair to SQL based on search type
71
+ */
72
+ const fieldValueToSql = (field, value, state) => {
73
+ const [paramName, newState] = nextParam(state);
74
+ const schema = state.schemas.get(field.toLowerCase());
75
+ const hasWildcard = value.endsWith("*");
76
+ const cleanedValue = cleanQuotedString(value);
77
+ const baseValue = hasWildcard ? cleanedValue.slice(0, -1) : cleanedValue;
78
+ // Rest of the function remains the same...
79
+ switch (schema === null || schema === void 0 ? void 0 : schema.type) {
80
+ case "date":
81
+ return [
82
+ `${field} = ${paramName}`,
83
+ addValue(newState, cleanedValue),
84
+ ];
85
+ case "number":
86
+ return [
87
+ `${field} = ${paramName}`,
88
+ addValue(newState, Number(cleanedValue)),
89
+ ];
90
+ default:
91
+ const langConfig = state.language || "english";
92
+ return [
93
+ `to_tsvector('${langConfig}', ${field}) @@ ${hasWildcard ? "to_tsquery" : "plainto_tsquery"}('${langConfig}', ${paramName})`,
94
+ addValue(newState, hasWildcard ? `${baseValue}:*` : baseValue),
95
+ ];
96
+ }
97
+ };
98
+ /**
99
+ * Convert a range expression to SQL
100
+ */
101
+ const rangeToSql = (field, operator, value, value2, state) => {
102
+ const schema = state.schemas.get(field.toLowerCase());
103
+ const isDateField = (schema === null || schema === void 0 ? void 0 : schema.type) === "date";
104
+ if (operator === "BETWEEN" && value2) {
105
+ const [param1, state1] = nextParam(state);
106
+ const [param2, state2] = nextParam(state1);
107
+ let val1 = isDateField ? value : Number(value);
108
+ let val2 = isDateField ? value2 : Number(value2);
109
+ return [
110
+ `${field} BETWEEN ${param1} AND ${param2}`,
111
+ addValue(addValue(state2, val1), val2),
112
+ ];
113
+ }
114
+ const [paramName, newState] = nextParam(state);
115
+ const val = isDateField ? value : Number(value);
116
+ return [
117
+ `${field} ${operator} ${paramName}`,
118
+ addValue(newState, val),
119
+ ];
120
+ };
121
+ const inExpressionToSql = (field, values, state) => {
122
+ let currentState = state;
123
+ const cleanedValues = values.map((v) => cleanQuotedString(v));
124
+ const paramNames = [];
125
+ const schema = state.schemas.get(field.toLowerCase());
126
+ for (const value of cleanedValues) {
127
+ const [paramName, newState] = nextParam(currentState);
128
+ paramNames.push(paramName);
129
+ currentState = addValue(newState, (schema === null || schema === void 0 ? void 0 : schema.type) === "number" ? Number(value) : value);
130
+ }
131
+ return [
132
+ `${field} IN (${paramNames
133
+ .map((p) => p)
134
+ .join(", ")})`,
135
+ currentState,
136
+ ];
137
+ };
138
+ /**
139
+ * Convert a binary operation (AND/OR) to SQL
140
+ */
141
+ const binaryOpToSql = (operator, left, right, state) => {
142
+ const [leftText, leftState] = expressionToSql(left, state);
143
+ const [rightText, rightState] = expressionToSql(right, leftState);
144
+ return [`(${leftText} ${operator} ${rightText})`, rightState];
145
+ };
146
+ /**
147
+ * Convert a single expression to SQL
148
+ */
149
+ const expressionToSql = (expr, state) => {
150
+ var _a;
151
+ switch (expr.type) {
152
+ case "SEARCH_TERM":
153
+ return searchTermToSql(expr.value, state);
154
+ case "WILDCARD":
155
+ return wildcardPatternToSql(expr, state);
156
+ case "IN":
157
+ return inExpressionToSql(expr.field.value, expr.values.map((v) => v.value), state);
158
+ case "FIELD_VALUE":
159
+ return fieldValueToSql(expr.field.value, expr.value.value, state);
160
+ case "RANGE":
161
+ return rangeToSql(expr.field.value, expr.operator, expr.value.value, (_a = expr.value2) === null || _a === void 0 ? void 0 : _a.value, state);
162
+ case "AND":
163
+ return binaryOpToSql("AND", expr.left, expr.right, state);
164
+ case "OR":
165
+ return binaryOpToSql("OR", expr.left, expr.right, state);
166
+ case "NOT": {
167
+ const [sqlText, newState] = expressionToSql(expr.expression, state);
168
+ return [`NOT ${sqlText}`, newState];
169
+ }
170
+ }
171
+ };
172
+ /**
173
+ * Convert a SearchQuery to a SQL WHERE clause with specified search type
174
+ */
175
+ const searchQueryToTsVectorSql = (query, searchableColumns, schemas = [], options = {}) => {
176
+ const initialState = {
177
+ paramCounter: 1,
178
+ values: [],
179
+ searchableColumns,
180
+ schemas: new Map(schemas.map((s) => [s.name.toLowerCase(), s])),
181
+ language: options.language,
182
+ };
183
+ if (!query.expression) {
184
+ return { text: "1=1", values: [] };
185
+ }
186
+ const [text, finalState] = expressionToSql(query.expression, initialState);
187
+ return { text, values: finalState.values };
188
+ };
189
+ exports.searchQueryToTsVectorSql = searchQueryToTsVectorSql;
190
+ /**
191
+ * Convert a search string directly to SQL
192
+ */
193
+ const searchStringToTsVectorSql = (searchString, searchableColumns, schemas = [], options = {}) => {
194
+ const query = (0, parser_1.parseSearchInputQuery)(searchString, schemas);
195
+ if (query.type === "SEARCH_QUERY_ERROR") {
196
+ throw new Error(`Parse error: ${query.errors[0].message}`);
197
+ }
198
+ return (0, exports.searchQueryToTsVectorSql)(query, searchableColumns, schemas, options);
199
+ };
200
+ exports.searchStringToTsVectorSql = searchStringToTsVectorSql;
@@ -0,0 +1,203 @@
1
+ import { parseSearchInputQuery, } from "./parser";
2
+ // Constants
3
+ const SPECIAL_CHARS = ["%", "_"];
4
+ const ESCAPE_CHAR = "\\";
5
+ // Helper Functions
6
+ const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7
+ const escapeSpecialChars = (value) => SPECIAL_CHARS.reduce((escaped, char) => escaped.replace(new RegExp(escapeRegExp(char), "g"), ESCAPE_CHAR + char), value);
8
+ const stripQuotes = (value) => {
9
+ if (value.startsWith('"') && value.endsWith('"')) {
10
+ return value.slice(1, -1);
11
+ }
12
+ return value;
13
+ };
14
+ const cleanQuotedString = (value, stripOuterQuotes = true) => {
15
+ // First strip the outer quotes if requested
16
+ let cleaned = stripOuterQuotes ? stripQuotes(value) : value;
17
+ // Replace escaped quotes with regular quotes
18
+ cleaned = cleaned.replace(/\\"/g, '"');
19
+ // Clean up any remaining escape characters
20
+ cleaned = cleaned.replace(/\\\\/g, "\\");
21
+ return cleaned;
22
+ };
23
+ // Create a new parameter placeholder and update state
24
+ const nextParam = (state) => {
25
+ const paramName = `$${state.paramCounter}`;
26
+ const newState = {
27
+ ...state,
28
+ paramCounter: state.paramCounter + 1,
29
+ };
30
+ return [paramName, newState];
31
+ };
32
+ // Add a value to the state and return updated state
33
+ const addValue = (state, value) => ({
34
+ ...state,
35
+ values: [...state.values, value],
36
+ });
37
+ /**
38
+ * Convert a wildcard pattern to SQL
39
+ */
40
+ const wildcardPatternToSql = (expr, state) => {
41
+ if (expr.prefix === "") {
42
+ return ["1=1", state];
43
+ }
44
+ const [paramName, newState] = nextParam(state);
45
+ const cleanedPrefix = cleanQuotedString(expr.prefix);
46
+ const escapedPrefix = escapeSpecialChars(cleanedPrefix);
47
+ const conditions = state.searchableColumns.map((column) => `lower(${column}) LIKE lower(${paramName})`);
48
+ const sql = conditions.length === 1
49
+ ? conditions[0]
50
+ : `(${conditions.join(" OR ")})`;
51
+ return [sql, addValue(newState, `${escapedPrefix}%`)];
52
+ };
53
+ /**
54
+ * Convert a search term to SQL conditions based on search type
55
+ */
56
+ const searchTermToSql = (value, state) => {
57
+ const [paramName, newState] = nextParam(state);
58
+ const hasWildcard = value.endsWith("*");
59
+ const cleanedValue = cleanQuotedString(value);
60
+ const baseValue = hasWildcard ? cleanedValue.slice(0, -1) : cleanedValue;
61
+ // Use lower() for case-insensitive search in SQLite
62
+ const escapedTerm = escapeSpecialChars(baseValue);
63
+ const conditions = state.searchableColumns.map((column) => `lower(${column}) LIKE lower(${paramName})`);
64
+ const sql = conditions.length === 1
65
+ ? conditions[0]
66
+ : `(${conditions.join(" OR ")})`;
67
+ if (hasWildcard) {
68
+ return [sql, addValue(newState, `${escapedTerm}%`)];
69
+ }
70
+ else {
71
+ return [sql, addValue(newState, `%${escapedTerm}%`)];
72
+ }
73
+ };
74
+ /**
75
+ * Convert a field:value pair to SQL based on search type
76
+ */
77
+ const fieldValueToSql = (field, value, state) => {
78
+ const [paramName, newState] = nextParam(state);
79
+ const schema = state.schemas.get(field.toLowerCase());
80
+ const hasWildcard = value.endsWith("*");
81
+ const cleanedValue = cleanQuotedString(value);
82
+ const baseValue = hasWildcard ? cleanedValue.slice(0, -1) : cleanedValue;
83
+ // Rest of the function remains the same...
84
+ switch (schema === null || schema === void 0 ? void 0 : schema.type) {
85
+ case "date":
86
+ return [
87
+ `${field} = ${paramName}`,
88
+ addValue(newState, cleanedValue),
89
+ ];
90
+ case "number":
91
+ return [
92
+ `${field} = ${paramName}`,
93
+ addValue(newState, Number(cleanedValue)),
94
+ ];
95
+ default:
96
+ const escapedValue = escapeSpecialChars(baseValue);
97
+ return [
98
+ `lower(${field}) LIKE lower(${paramName})`,
99
+ addValue(newState, hasWildcard ? `${escapedValue}%` : `%${escapedValue}%`),
100
+ ];
101
+ }
102
+ };
103
+ /**
104
+ * Convert a range expression to SQL
105
+ */
106
+ const rangeToSql = (field, operator, value, value2, state) => {
107
+ const schema = state.schemas.get(field.toLowerCase());
108
+ const isDateField = (schema === null || schema === void 0 ? void 0 : schema.type) === "date";
109
+ if (operator === "BETWEEN" && value2) {
110
+ const [param1, state1] = nextParam(state);
111
+ const [param2, state2] = nextParam(state1);
112
+ let val1 = isDateField ? value : Number(value);
113
+ let val2 = isDateField ? value2 : Number(value2);
114
+ return [
115
+ `${field} BETWEEN ${param1} AND ${param2}`,
116
+ addValue(addValue(state2, val1), val2),
117
+ ];
118
+ }
119
+ const [paramName, newState] = nextParam(state);
120
+ const val = isDateField ? value : Number(value);
121
+ return [
122
+ `${field} ${operator} ${paramName}`,
123
+ addValue(newState, val),
124
+ ];
125
+ };
126
+ const inExpressionToSql = (field, values, state) => {
127
+ let currentState = state;
128
+ const cleanedValues = values.map((v) => cleanQuotedString(v));
129
+ const paramNames = [];
130
+ const schema = state.schemas.get(field.toLowerCase());
131
+ for (const value of cleanedValues) {
132
+ const [paramName, newState] = nextParam(currentState);
133
+ paramNames.push(paramName);
134
+ currentState = addValue(newState, (schema === null || schema === void 0 ? void 0 : schema.type) === "number" ? Number(value) : value);
135
+ }
136
+ return [
137
+ `${field} IN (${paramNames
138
+ .map((p) => p)
139
+ .join(", ")})`,
140
+ currentState,
141
+ ];
142
+ };
143
+ /**
144
+ * Convert a binary operation (AND/OR) to SQL
145
+ */
146
+ const binaryOpToSql = (operator, left, right, state) => {
147
+ const [leftText, leftState] = expressionToSql(left, state);
148
+ const [rightText, rightState] = expressionToSql(right, leftState);
149
+ return [`(${leftText} ${operator} ${rightText})`, rightState];
150
+ };
151
+ /**
152
+ * Convert a single expression to SQL
153
+ */
154
+ const expressionToSql = (expr, state) => {
155
+ var _a;
156
+ switch (expr.type) {
157
+ case "SEARCH_TERM":
158
+ return searchTermToSql(expr.value, state);
159
+ case "WILDCARD":
160
+ return wildcardPatternToSql(expr, state);
161
+ case "IN":
162
+ return inExpressionToSql(expr.field.value, expr.values.map((v) => v.value), state);
163
+ case "FIELD_VALUE":
164
+ return fieldValueToSql(expr.field.value, expr.value.value, state);
165
+ case "RANGE":
166
+ return rangeToSql(expr.field.value, expr.operator, expr.value.value, (_a = expr.value2) === null || _a === void 0 ? void 0 : _a.value, state);
167
+ case "AND":
168
+ return binaryOpToSql("AND", expr.left, expr.right, state);
169
+ case "OR":
170
+ return binaryOpToSql("OR", expr.left, expr.right, state);
171
+ case "NOT": {
172
+ const [sqlText, newState] = expressionToSql(expr.expression, state);
173
+ return [`NOT ${sqlText}`, newState];
174
+ }
175
+ }
176
+ };
177
+ /**
178
+ * Convert a SearchQuery to a SQL WHERE clause with specified search type
179
+ */
180
+ export const searchQueryToIlikeSql = (query, searchableColumns, schemas = [], options = {}) => {
181
+ const initialState = {
182
+ paramCounter: 1,
183
+ values: [],
184
+ searchableColumns,
185
+ schemas: new Map(schemas.map((s) => [s.name.toLowerCase(), s])),
186
+ language: options.language,
187
+ };
188
+ if (!query.expression) {
189
+ return { text: "1=1", values: [] };
190
+ }
191
+ const [text, finalState] = expressionToSql(query.expression, initialState);
192
+ return { text, values: finalState.values };
193
+ };
194
+ /**
195
+ * Convert a search string directly to SQL
196
+ */
197
+ export const searchStringToIlikeSql = (searchString, searchableColumns, schemas = [], options = {}) => {
198
+ const query = parseSearchInputQuery(searchString, schemas);
199
+ if (query.type === "SEARCH_QUERY_ERROR") {
200
+ throw new Error(`Parse error: ${query.errors[0].message}`);
201
+ }
202
+ return searchQueryToIlikeSql(query, searchableColumns, schemas, options);
203
+ };
@@ -0,0 +1,222 @@
1
+ import { parseSearchInputQuery, } from "./parser";
2
+ // Helper Functions
3
+ const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
+ // Helper to escape special characters for ParadeDB query syntax
5
+ const escapeParadeDBChars = (value) => {
6
+ const specialChars = [
7
+ "+",
8
+ "^",
9
+ "`",
10
+ ":",
11
+ "{",
12
+ "}",
13
+ '"',
14
+ "[",
15
+ "]",
16
+ "(",
17
+ ")",
18
+ "<",
19
+ ">",
20
+ "~",
21
+ "!",
22
+ "\\",
23
+ "*",
24
+ ];
25
+ return specialChars.reduce((escaped, char) => escaped.replace(new RegExp(escapeRegExp(char), "g"), `\\${char}`), value);
26
+ };
27
+ const stripQuotes = (value) => {
28
+ if (value.startsWith('"') && value.endsWith('"')) {
29
+ return value.slice(1, -1);
30
+ }
31
+ return value;
32
+ };
33
+ const cleanQuotedString = (value, stripOuterQuotes = true) => {
34
+ // First strip the outer quotes if requested
35
+ let cleaned = stripOuterQuotes ? stripQuotes(value) : value;
36
+ // Replace escaped quotes with regular quotes
37
+ cleaned = cleaned.replace(/\\"/g, '"');
38
+ // Clean up any remaining escape characters
39
+ cleaned = cleaned.replace(/\\\\/g, "\\");
40
+ return cleaned;
41
+ };
42
+ const prepareParadeDBString = (value, includeWildcard = false) => {
43
+ // First clean up the string
44
+ const cleaned = cleanQuotedString(value);
45
+ // For ParadeDB, we need to:
46
+ // 1. Escape special characters (except wildcards)
47
+ // 2. Wrap in quotes
48
+ // 3. Add wildcard if needed
49
+ const escaped = escapeParadeDBChars(cleaned);
50
+ const result = `"${escaped}"`;
51
+ return includeWildcard ? `${result}*` : result;
52
+ };
53
+ // Create a new parameter placeholder and update state
54
+ const nextParam = (state) => {
55
+ const paramName = `$${state.paramCounter}`;
56
+ const newState = {
57
+ ...state,
58
+ paramCounter: state.paramCounter + 1,
59
+ };
60
+ return [paramName, newState];
61
+ };
62
+ // Add a value to the state and return updated state
63
+ const addValue = (state, value) => ({
64
+ ...state,
65
+ values: [...state.values, value],
66
+ });
67
+ /**
68
+ * Convert a wildcard pattern to SQL
69
+ */
70
+ const wildcardPatternToSql = (expr, state) => {
71
+ if (expr.prefix === "") {
72
+ return ["1=1", state];
73
+ }
74
+ const [paramName, newState] = nextParam(state);
75
+ const cleanedPrefix = cleanQuotedString(expr.prefix);
76
+ const queryValue = prepareParadeDBString(cleanedPrefix, true);
77
+ const conditions = state.searchableColumns.map((column) => `${column} @@@ ${paramName}`);
78
+ const sql = conditions.length === 1
79
+ ? conditions[0]
80
+ : `(${conditions.join(" OR ")})`;
81
+ return [sql, addValue(newState, queryValue)];
82
+ };
83
+ /**
84
+ * Convert a search term to SQL conditions based on search type
85
+ */
86
+ const searchTermToSql = (value, state) => {
87
+ const [paramName, newState] = nextParam(state);
88
+ const hasWildcard = value.endsWith("*");
89
+ const cleanedValue = cleanQuotedString(value);
90
+ const baseValue = hasWildcard ? cleanedValue.slice(0, -1) : cleanedValue;
91
+ const queryValue = prepareParadeDBString(baseValue, hasWildcard);
92
+ const conditions = state.searchableColumns.map((column) => `${column} @@@ ${paramName}`);
93
+ const sql = conditions.length === 1
94
+ ? conditions[0]
95
+ : `(${conditions.join(" OR ")})`;
96
+ return [sql, addValue(newState, queryValue)];
97
+ };
98
+ /**
99
+ * Convert a field:value pair to SQL based on search type
100
+ */
101
+ const fieldValueToSql = (field, value, state) => {
102
+ const [paramName, newState] = nextParam(state);
103
+ const schema = state.schemas.get(field.toLowerCase());
104
+ const hasWildcard = value.endsWith("*");
105
+ const cleanedValue = cleanQuotedString(value);
106
+ const baseValue = hasWildcard ? cleanedValue.slice(0, -1) : cleanedValue;
107
+ switch (schema === null || schema === void 0 ? void 0 : schema.type) {
108
+ case "date": {
109
+ // Use parameter binding for dates
110
+ const [dateParam, dateState] = nextParam(state);
111
+ return [
112
+ `${field} @@@ '"' || ${dateParam} || '"'`,
113
+ addValue(dateState, baseValue),
114
+ ];
115
+ }
116
+ case "number":
117
+ return [`${field} @@@ '${baseValue}'`, newState];
118
+ default: {
119
+ const queryValue = prepareParadeDBString(baseValue, hasWildcard);
120
+ return [`${field} @@@ ${paramName}`, addValue(newState, queryValue)];
121
+ }
122
+ }
123
+ };
124
+ /**
125
+ * Convert a range expression to SQL
126
+ */
127
+ const rangeToSql = (field, operator, value, value2, state) => {
128
+ const schema = state.schemas.get(field.toLowerCase());
129
+ const isDateField = (schema === null || schema === void 0 ? void 0 : schema.type) === "date";
130
+ if (operator === "BETWEEN" && value2) {
131
+ const [param1, state1] = nextParam(state);
132
+ const [param2, state2] = nextParam(state1);
133
+ let val1 = isDateField ? value : Number(value);
134
+ let val2 = isDateField ? value2 : Number(value2);
135
+ return [
136
+ `${field} @@@ '[' || ${param1} || ' TO ' || ${param2} || ']'`,
137
+ addValue(addValue(state2, val1), val2),
138
+ ];
139
+ }
140
+ else {
141
+ const [paramName, newState] = nextParam(state);
142
+ const rangeOp = operator.replace(">=", ">=").replace("<=", "<=");
143
+ const val = isDateField ? value : Number(value);
144
+ return [
145
+ `${field} @@@ '${rangeOp}' || ${paramName}`,
146
+ addValue(newState, val),
147
+ ];
148
+ }
149
+ };
150
+ const inExpressionToSql = (field, values, state) => {
151
+ let currentState = state;
152
+ const cleanedValues = values.map((v) => cleanQuotedString(v));
153
+ const paramNames = [];
154
+ for (const value of cleanedValues) {
155
+ const [paramName, newState] = nextParam(currentState);
156
+ paramNames.push(paramName);
157
+ currentState = addValue(newState, value);
158
+ }
159
+ const concatExpr = paramNames.join(" || ' ' || ");
160
+ return [`${field} @@@ 'IN[' || ${concatExpr} || ']'`, currentState];
161
+ };
162
+ /**
163
+ * Convert a binary operation (AND/OR) to SQL
164
+ */
165
+ const binaryOpToSql = (operator, left, right, state) => {
166
+ const [leftText, leftState] = expressionToSql(left, state);
167
+ const [rightText, rightState] = expressionToSql(right, leftState);
168
+ return [`(${leftText} ${operator} ${rightText})`, rightState];
169
+ };
170
+ /**
171
+ * Convert a single expression to SQL
172
+ */
173
+ const expressionToSql = (expr, state) => {
174
+ var _a;
175
+ switch (expr.type) {
176
+ case "SEARCH_TERM":
177
+ return searchTermToSql(expr.value, state);
178
+ case "WILDCARD":
179
+ return wildcardPatternToSql(expr, state);
180
+ case "IN":
181
+ return inExpressionToSql(expr.field.value, expr.values.map((v) => v.value), state);
182
+ case "FIELD_VALUE":
183
+ return fieldValueToSql(expr.field.value, expr.value.value, state);
184
+ case "RANGE":
185
+ return rangeToSql(expr.field.value, expr.operator, expr.value.value, (_a = expr.value2) === null || _a === void 0 ? void 0 : _a.value, state);
186
+ case "AND":
187
+ return binaryOpToSql("AND", expr.left, expr.right, state);
188
+ case "OR":
189
+ return binaryOpToSql("OR", expr.left, expr.right, state);
190
+ case "NOT": {
191
+ const [sqlText, newState] = expressionToSql(expr.expression, state);
192
+ return [`NOT ${sqlText}`, newState];
193
+ }
194
+ }
195
+ };
196
+ /**
197
+ * Convert a SearchQuery to a SQL WHERE clause with specified search type
198
+ */
199
+ export const searchQueryToParadeDbSql = (query, searchableColumns, schemas = [], options = {}) => {
200
+ const initialState = {
201
+ paramCounter: 1,
202
+ values: [],
203
+ searchableColumns,
204
+ schemas: new Map(schemas.map((s) => [s.name.toLowerCase(), s])),
205
+ language: options.language,
206
+ };
207
+ if (!query.expression) {
208
+ return { text: "1=1", values: [] };
209
+ }
210
+ const [text, finalState] = expressionToSql(query.expression, initialState);
211
+ return { text, values: finalState.values };
212
+ };
213
+ /**
214
+ * Convert a search string directly to SQL
215
+ */
216
+ export const searchStringToParadeDbSql = (searchString, searchableColumns, schemas = [], options = {}) => {
217
+ const query = parseSearchInputQuery(searchString, schemas);
218
+ if (query.type === "SEARCH_QUERY_ERROR") {
219
+ throw new Error(`Parse error: ${query.errors[0].message}`);
220
+ }
221
+ return searchQueryToParadeDbSql(query, searchableColumns, schemas, options);
222
+ };
@@ -1,4 +1,7 @@
1
1
  import { parseSearchInputQuery, } from "./parser";
2
+ export { searchStringToIlikeSql, searchQueryToIlikeSql, } from "./search-query-to-ilike-sql";
3
+ export { searchQueryToTsVectorSql, searchStringToTsVectorSql } from "./search-query-to-tsvector-sql";
4
+ export { searchQueryToParadeDbSql, searchStringToParadeDbSql, } from "./search-query-to-paradedb-sql";
2
5
  // Constants
3
6
  const SPECIAL_CHARS = ["%", "_"];
4
7
  const ESCAPE_CHAR = "\\";