@lossless.org/client 0.1.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.
Files changed (255) hide show
  1. package/.smartconfig.json +37 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/classes.losslessorgclient.d.ts +32 -0
  5. package/dist_ts/classes.losslessorgclient.js +103 -0
  6. package/dist_ts/core/classes.error.d.ts +16 -0
  7. package/dist_ts/core/classes.error.js +29 -0
  8. package/dist_ts/core/classes.operation.d.ts +17 -0
  9. package/dist_ts/core/classes.operation.js +70 -0
  10. package/dist_ts/core/interfaces.d.ts +18 -0
  11. package/dist_ts/core/interfaces.js +2 -0
  12. package/dist_ts/index.d.ts +4 -0
  13. package/dist_ts/index.js +3 -0
  14. package/dist_ts/nosqldb/classes.atomicdelete.d.ts +26 -0
  15. package/dist_ts/nosqldb/classes.atomicdelete.js +44 -0
  16. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.d.ts +27 -0
  17. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.js +86 -0
  18. package/dist_ts/nosqldb/classes.atomicupdate.d.ts +26 -0
  19. package/dist_ts/nosqldb/classes.atomicupdate.js +55 -0
  20. package/dist_ts/nosqldb/classes.bootstraprecord.d.ts +14 -0
  21. package/dist_ts/nosqldb/classes.bootstraprecord.js +80 -0
  22. package/dist_ts/nosqldb/classes.bsonassertion.d.ts +2 -0
  23. package/dist_ts/nosqldb/classes.bsonassertion.js +29 -0
  24. package/dist_ts/nosqldb/classes.bsonobjectid.d.ts +2 -0
  25. package/dist_ts/nosqldb/classes.bsonobjectid.js +50 -0
  26. package/dist_ts/nosqldb/classes.bsonsnapshot.d.ts +3 -0
  27. package/dist_ts/nosqldb/classes.bsonsnapshot.js +126 -0
  28. package/dist_ts/nosqldb/classes.cached-document.d.ts +27 -0
  29. package/dist_ts/nosqldb/classes.cached-document.js +112 -0
  30. package/dist_ts/nosqldb/classes.collection.d.ts +302 -0
  31. package/dist_ts/nosqldb/classes.collection.js +1568 -0
  32. package/dist_ts/nosqldb/classes.collectionfactory.d.ts +30 -0
  33. package/dist_ts/nosqldb/classes.collectionfactory.js +65 -0
  34. package/dist_ts/nosqldb/classes.collectionlifecycle.d.ts +4 -0
  35. package/dist_ts/nosqldb/classes.collectionlifecycle.js +15 -0
  36. package/dist_ts/nosqldb/classes.collectionpreparation.d.ts +34 -0
  37. package/dist_ts/nosqldb/classes.collectionpreparation.js +134 -0
  38. package/dist_ts/nosqldb/classes.collectiontopology.d.ts +47 -0
  39. package/dist_ts/nosqldb/classes.collectiontopology.js +388 -0
  40. package/dist_ts/nosqldb/classes.connection.d.ts +18 -0
  41. package/dist_ts/nosqldb/classes.connection.js +23 -0
  42. package/dist_ts/nosqldb/classes.convenience.d.ts +1 -0
  43. package/dist_ts/nosqldb/classes.convenience.js +5 -0
  44. package/dist_ts/nosqldb/classes.cursor.d.ts +15 -0
  45. package/dist_ts/nosqldb/classes.cursor.js +82 -0
  46. package/dist_ts/nosqldb/classes.cursoroperation.d.ts +7 -0
  47. package/dist_ts/nosqldb/classes.cursoroperation.js +36 -0
  48. package/dist_ts/nosqldb/classes.db.d.ts +98 -0
  49. package/dist_ts/nosqldb/classes.db.js +434 -0
  50. package/dist_ts/nosqldb/classes.distributedcoordinator.d.ts +52 -0
  51. package/dist_ts/nosqldb/classes.distributedcoordinator.js +314 -0
  52. package/dist_ts/nosqldb/classes.doc.d.ts +722 -0
  53. package/dist_ts/nosqldb/classes.doc.js +2976 -0
  54. package/dist_ts/nosqldb/classes.easystore.d.ts +42 -0
  55. package/dist_ts/nosqldb/classes.easystore.js +199 -0
  56. package/dist_ts/nosqldb/classes.exactpersistence.d.ts +141 -0
  57. package/dist_ts/nosqldb/classes.exactpersistence.js +1110 -0
  58. package/dist_ts/nosqldb/classes.lucene.adapter.d.ts +173 -0
  59. package/dist_ts/nosqldb/classes.lucene.adapter.js +623 -0
  60. package/dist_ts/nosqldb/classes.mongoadministration.d.ts +73 -0
  61. package/dist_ts/nosqldb/classes.mongoadministration.js +497 -0
  62. package/dist_ts/nosqldb/classes.namespaceinspection.d.ts +42 -0
  63. package/dist_ts/nosqldb/classes.namespaceinspection.js +138 -0
  64. package/dist_ts/nosqldb/classes.operationbudget.d.ts +22 -0
  65. package/dist_ts/nosqldb/classes.operationbudget.js +65 -0
  66. package/dist_ts/nosqldb/classes.ordinarypersistence.d.ts +18 -0
  67. package/dist_ts/nosqldb/classes.ordinarypersistence.js +71 -0
  68. package/dist_ts/nosqldb/classes.persistence.d.ts +14 -0
  69. package/dist_ts/nosqldb/classes.persistence.js +34 -0
  70. package/dist_ts/nosqldb/classes.session.d.ts +28 -0
  71. package/dist_ts/nosqldb/classes.session.js +246 -0
  72. package/dist_ts/nosqldb/classes.storedinspection.d.ts +44 -0
  73. package/dist_ts/nosqldb/classes.storedinspection.js +100 -0
  74. package/dist_ts/nosqldb/classes.testdatabase.d.ts +56 -0
  75. package/dist_ts/nosqldb/classes.testdatabase.js +250 -0
  76. package/dist_ts/nosqldb/classes.watcher.d.ts +37 -0
  77. package/dist_ts/nosqldb/classes.watcher.js +98 -0
  78. package/dist_ts/nosqldb/index.d.ts +27 -0
  79. package/dist_ts/nosqldb/index.js +18 -0
  80. package/dist_ts/nosqldb/logging.d.ts +2 -0
  81. package/dist_ts/nosqldb/logging.js +3 -0
  82. package/dist_ts/nosqldb/plugins.d.ts +17 -0
  83. package/dist_ts/nosqldb/plugins.js +20 -0
  84. package/dist_ts/nosqldb/shim.d.ts +1 -0
  85. package/dist_ts/nosqldb/shim.js +8 -0
  86. package/dist_ts/nosqldb/testsupport.d.ts +3 -0
  87. package/dist_ts/nosqldb/testsupport.js +3 -0
  88. package/dist_ts/objectstorage/classes.bucket.d.ts +227 -0
  89. package/dist_ts/objectstorage/classes.bucket.js +939 -0
  90. package/dist_ts/objectstorage/classes.connection.d.ts +21 -0
  91. package/dist_ts/objectstorage/classes.connection.js +27 -0
  92. package/dist_ts/objectstorage/classes.directory.d.ts +124 -0
  93. package/dist_ts/objectstorage/classes.directory.js +335 -0
  94. package/dist_ts/objectstorage/classes.exactpathpurgeerror.d.ts +5 -0
  95. package/dist_ts/objectstorage/classes.exactpathpurgeerror.js +18 -0
  96. package/dist_ts/objectstorage/classes.exactuploaderror.d.ts +5 -0
  97. package/dist_ts/objectstorage/classes.exactuploaderror.js +19 -0
  98. package/dist_ts/objectstorage/classes.file.d.ts +91 -0
  99. package/dist_ts/objectstorage/classes.file.js +255 -0
  100. package/dist_ts/objectstorage/classes.listcursor.d.ts +41 -0
  101. package/dist_ts/objectstorage/classes.listcursor.js +67 -0
  102. package/dist_ts/objectstorage/classes.metadata.d.ts +57 -0
  103. package/dist_ts/objectstorage/classes.metadata.js +113 -0
  104. package/dist_ts/objectstorage/classes.smartbucket.d.ts +57 -0
  105. package/dist_ts/objectstorage/classes.smartbucket.js +357 -0
  106. package/dist_ts/objectstorage/classes.trash.d.ts +11 -0
  107. package/dist_ts/objectstorage/classes.trash.js +22 -0
  108. package/dist_ts/objectstorage/classes.watcher.d.ts +79 -0
  109. package/dist_ts/objectstorage/classes.watcher.js +249 -0
  110. package/dist_ts/objectstorage/helpers.d.ts +25 -0
  111. package/dist_ts/objectstorage/helpers.js +215 -0
  112. package/dist_ts/objectstorage/index.d.ts +13 -0
  113. package/dist_ts/objectstorage/index.js +13 -0
  114. package/dist_ts/objectstorage/interfaces.d.ts +276 -0
  115. package/dist_ts/objectstorage/interfaces.js +2 -0
  116. package/dist_ts/objectstorage/internal.exactpathpurge.operations.d.ts +11 -0
  117. package/dist_ts/objectstorage/internal.exactpathpurge.operations.js +1069 -0
  118. package/dist_ts/objectstorage/internal.exactupload.capability.d.ts +40 -0
  119. package/dist_ts/objectstorage/internal.exactupload.capability.js +382 -0
  120. package/dist_ts/objectstorage/internal.exactupload.operations.d.ts +11 -0
  121. package/dist_ts/objectstorage/internal.exactupload.operations.js +1104 -0
  122. package/dist_ts/objectstorage/internal.multipart.planner.d.ts +8 -0
  123. package/dist_ts/objectstorage/internal.multipart.planner.js +63 -0
  124. package/dist_ts/objectstorage/internal.multipart.stream.d.ts +17 -0
  125. package/dist_ts/objectstorage/internal.multipart.stream.js +440 -0
  126. package/dist_ts/objectstorage/internal.multipart.types.d.ts +82 -0
  127. package/dist_ts/objectstorage/internal.multipart.types.js +2 -0
  128. package/dist_ts/objectstorage/plugins.d.ts +17 -0
  129. package/dist_ts/objectstorage/plugins.js +23 -0
  130. package/dist_ts/plugins.d.ts +4 -0
  131. package/dist_ts/plugins.js +5 -0
  132. package/dist_ts/sqldb/classes.clickhouseconnection.d.ts +39 -0
  133. package/dist_ts/sqldb/classes.clickhouseconnection.js +225 -0
  134. package/dist_ts/sqldb/classes.sqlconnection.d.ts +50 -0
  135. package/dist_ts/sqldb/classes.sqlconnection.js +376 -0
  136. package/dist_ts/sqldb/classes.sqltable.d.ts +29 -0
  137. package/dist_ts/sqldb/classes.sqltable.js +70 -0
  138. package/dist_ts/sqldb/clickhouse/classes.metricwriter.d.ts +27 -0
  139. package/dist_ts/sqldb/clickhouse/classes.metricwriter.js +89 -0
  140. package/dist_ts/sqldb/clickhouse/index.d.ts +9 -0
  141. package/dist_ts/sqldb/clickhouse/index.js +13 -0
  142. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.clickhousetable.d.ts +88 -0
  143. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.clickhousetable.js +304 -0
  144. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.httpclient.d.ts +28 -0
  145. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.httpclient.js +46 -0
  146. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.querybuilder.d.ts +43 -0
  147. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.querybuilder.js +155 -0
  148. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.resultset.d.ts +13 -0
  149. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.resultset.js +36 -0
  150. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.smartclickhouse.d.ts +49 -0
  151. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.smartclickhouse.js +106 -0
  152. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.timedatatable.d.ts +51 -0
  153. package/dist_ts/sqldb/clickhouse/smartclickhouse.classes.timedatatable.js +96 -0
  154. package/dist_ts/sqldb/clickhouse/smartclickhouse.plugins.d.ts +5 -0
  155. package/dist_ts/sqldb/clickhouse/smartclickhouse.plugins.js +6 -0
  156. package/dist_ts/sqldb/clickhouse/smartclickhouse.types.d.ts +53 -0
  157. package/dist_ts/sqldb/clickhouse/smartclickhouse.types.js +41 -0
  158. package/dist_ts/sqldb/index.d.ts +6 -0
  159. package/dist_ts/sqldb/index.js +6 -0
  160. package/dist_ts/sqldb/interfaces.d.ts +53 -0
  161. package/dist_ts/sqldb/interfaces.js +19 -0
  162. package/dist_ts/sqldb/internal.sqlpool.d.ts +51 -0
  163. package/dist_ts/sqldb/internal.sqlpool.js +214 -0
  164. package/dist_ts/sqldb/plugins.clickhouse.d.ts +4 -0
  165. package/dist_ts/sqldb/plugins.clickhouse.js +5 -0
  166. package/dist_ts/sqldb/plugins.mariadb.d.ts +4 -0
  167. package/dist_ts/sqldb/plugins.mariadb.js +5 -0
  168. package/docs/source/smartbucket/license.md +19 -0
  169. package/docs/source/smartclickhouse/license.md +19 -0
  170. package/license.md +21 -0
  171. package/package.json +101 -0
  172. package/readme.md +157 -0
  173. package/ts/00_commitinfo_data.ts +8 -0
  174. package/ts/classes.losslessorgclient.ts +104 -0
  175. package/ts/core/classes.error.ts +31 -0
  176. package/ts/core/classes.operation.ts +60 -0
  177. package/ts/core/interfaces.ts +24 -0
  178. package/ts/index.ts +4 -0
  179. package/ts/nosqldb/classes.atomicdelete.ts +81 -0
  180. package/ts/nosqldb/classes.atomicfindoneandupdate.ts +152 -0
  181. package/ts/nosqldb/classes.atomicupdate.ts +99 -0
  182. package/ts/nosqldb/classes.bootstraprecord.ts +107 -0
  183. package/ts/nosqldb/classes.bsonassertion.ts +25 -0
  184. package/ts/nosqldb/classes.bsonobjectid.ts +45 -0
  185. package/ts/nosqldb/classes.bsonsnapshot.ts +105 -0
  186. package/ts/nosqldb/classes.cached-document.ts +67 -0
  187. package/ts/nosqldb/classes.collection.ts +2399 -0
  188. package/ts/nosqldb/classes.collectionfactory.ts +87 -0
  189. package/ts/nosqldb/classes.collectionlifecycle.ts +24 -0
  190. package/ts/nosqldb/classes.collectionpreparation.ts +164 -0
  191. package/ts/nosqldb/classes.collectiontopology.ts +645 -0
  192. package/ts/nosqldb/classes.connection.ts +31 -0
  193. package/ts/nosqldb/classes.convenience.ts +5 -0
  194. package/ts/nosqldb/classes.cursor.ts +88 -0
  195. package/ts/nosqldb/classes.cursoroperation.ts +38 -0
  196. package/ts/nosqldb/classes.db.ts +637 -0
  197. package/ts/nosqldb/classes.distributedcoordinator.ts +305 -0
  198. package/ts/nosqldb/classes.doc.ts +4928 -0
  199. package/ts/nosqldb/classes.easystore.ts +154 -0
  200. package/ts/nosqldb/classes.exactpersistence.ts +1974 -0
  201. package/ts/nosqldb/classes.lucene.adapter.ts +780 -0
  202. package/ts/nosqldb/classes.mongoadministration.ts +867 -0
  203. package/ts/nosqldb/classes.namespaceinspection.ts +199 -0
  204. package/ts/nosqldb/classes.operationbudget.ts +75 -0
  205. package/ts/nosqldb/classes.ordinarypersistence.ts +84 -0
  206. package/ts/nosqldb/classes.persistence.ts +62 -0
  207. package/ts/nosqldb/classes.session.ts +389 -0
  208. package/ts/nosqldb/classes.storedinspection.ts +151 -0
  209. package/ts/nosqldb/classes.testdatabase.ts +272 -0
  210. package/ts/nosqldb/classes.watcher.ts +96 -0
  211. package/ts/nosqldb/index.ts +51 -0
  212. package/ts/nosqldb/logging.ts +3 -0
  213. package/ts/nosqldb/plugins.ts +37 -0
  214. package/ts/nosqldb/shim.ts +6 -0
  215. package/ts/nosqldb/testsupport.ts +13 -0
  216. package/ts/objectstorage/classes.bucket.ts +1114 -0
  217. package/ts/objectstorage/classes.connection.ts +36 -0
  218. package/ts/objectstorage/classes.directory.ts +425 -0
  219. package/ts/objectstorage/classes.exactpathpurgeerror.ts +24 -0
  220. package/ts/objectstorage/classes.exactuploaderror.ts +25 -0
  221. package/ts/objectstorage/classes.file.ts +303 -0
  222. package/ts/objectstorage/classes.listcursor.ts +89 -0
  223. package/ts/objectstorage/classes.metadata.ts +134 -0
  224. package/ts/objectstorage/classes.smartbucket.ts +445 -0
  225. package/ts/objectstorage/classes.trash.ts +30 -0
  226. package/ts/objectstorage/classes.watcher.ts +289 -0
  227. package/ts/objectstorage/helpers.ts +254 -0
  228. package/ts/objectstorage/index.ts +13 -0
  229. package/ts/objectstorage/interfaces.ts +389 -0
  230. package/ts/objectstorage/internal.exactpathpurge.operations.ts +1936 -0
  231. package/ts/objectstorage/internal.exactupload.capability.ts +580 -0
  232. package/ts/objectstorage/internal.exactupload.operations.ts +1715 -0
  233. package/ts/objectstorage/internal.multipart.planner.ts +90 -0
  234. package/ts/objectstorage/internal.multipart.stream.ts +606 -0
  235. package/ts/objectstorage/internal.multipart.types.ts +106 -0
  236. package/ts/objectstorage/plugins.ts +35 -0
  237. package/ts/plugins.ts +4 -0
  238. package/ts/sqldb/classes.clickhouseconnection.ts +189 -0
  239. package/ts/sqldb/classes.sqlconnection.ts +350 -0
  240. package/ts/sqldb/classes.sqltable.ts +70 -0
  241. package/ts/sqldb/clickhouse/classes.metricwriter.ts +72 -0
  242. package/ts/sqldb/clickhouse/index.ts +16 -0
  243. package/ts/sqldb/clickhouse/smartclickhouse.classes.clickhousetable.ts +372 -0
  244. package/ts/sqldb/clickhouse/smartclickhouse.classes.httpclient.ts +52 -0
  245. package/ts/sqldb/clickhouse/smartclickhouse.classes.querybuilder.ts +120 -0
  246. package/ts/sqldb/clickhouse/smartclickhouse.classes.resultset.ts +44 -0
  247. package/ts/sqldb/clickhouse/smartclickhouse.classes.smartclickhouse.ts +123 -0
  248. package/ts/sqldb/clickhouse/smartclickhouse.classes.timedatatable.ts +114 -0
  249. package/ts/sqldb/clickhouse/smartclickhouse.plugins.ts +5 -0
  250. package/ts/sqldb/clickhouse/smartclickhouse.types.ts +134 -0
  251. package/ts/sqldb/index.ts +6 -0
  252. package/ts/sqldb/interfaces.ts +61 -0
  253. package/ts/sqldb/internal.sqlpool.ts +179 -0
  254. package/ts/sqldb/plugins.clickhouse.ts +4 -0
  255. package/ts/sqldb/plugins.mariadb.ts +4 -0
@@ -0,0 +1,780 @@
1
+ /**
2
+ * Lucene to MongoDB query adapter for SmartData
3
+ */
4
+ import * as plugins from './plugins.js';
5
+ import { logger } from './logging.js';
6
+
7
+ // Types
8
+ type NodeType =
9
+ | 'TERM'
10
+ | 'PHRASE'
11
+ | 'FIELD'
12
+ | 'AND'
13
+ | 'OR'
14
+ | 'NOT'
15
+ | 'RANGE'
16
+ | 'WILDCARD'
17
+ | 'FUZZY'
18
+ | 'GROUP';
19
+
20
+ interface QueryNode {
21
+ type: NodeType;
22
+ }
23
+
24
+ interface TermNode extends QueryNode {
25
+ type: 'TERM';
26
+ value: string;
27
+ boost?: number;
28
+ }
29
+
30
+ interface PhraseNode extends QueryNode {
31
+ type: 'PHRASE';
32
+ value: string;
33
+ proximity?: number;
34
+ }
35
+
36
+ interface FieldNode extends QueryNode {
37
+ type: 'FIELD';
38
+ field: string;
39
+ value: AnyQueryNode;
40
+ }
41
+
42
+ interface BooleanNode extends QueryNode {
43
+ type: 'AND' | 'OR' | 'NOT';
44
+ left: AnyQueryNode;
45
+ right: AnyQueryNode;
46
+ }
47
+
48
+ interface RangeNode extends QueryNode {
49
+ type: 'RANGE';
50
+ field: string;
51
+ lower: string;
52
+ upper: string;
53
+ includeLower: boolean;
54
+ includeUpper: boolean;
55
+ }
56
+
57
+ interface WildcardNode extends QueryNode {
58
+ type: 'WILDCARD';
59
+ value: string;
60
+ }
61
+
62
+ interface FuzzyNode extends QueryNode {
63
+ type: 'FUZZY';
64
+ value: string;
65
+ maxEdits: number;
66
+ }
67
+
68
+ interface GroupNode extends QueryNode {
69
+ type: 'GROUP';
70
+ value: AnyQueryNode;
71
+ }
72
+
73
+ type AnyQueryNode =
74
+ | TermNode
75
+ | PhraseNode
76
+ | FieldNode
77
+ | BooleanNode
78
+ | RangeNode
79
+ | WildcardNode
80
+ | FuzzyNode
81
+ | GroupNode;
82
+
83
+ /**
84
+ * Lucene query parser
85
+ */
86
+ export class LuceneParser {
87
+ private pos: number = 0;
88
+ private input: string = '';
89
+ private tokens: string[] = [];
90
+
91
+ constructor() {}
92
+
93
+ /**
94
+ * Parse a Lucene query string into an AST
95
+ */
96
+ parse(query: string): AnyQueryNode {
97
+ this.input = query.trim();
98
+ this.pos = 0;
99
+ this.tokens = this.tokenize(this.input);
100
+
101
+ return this.parseQuery();
102
+ }
103
+
104
+ /**
105
+ * Tokenize the input string into tokens
106
+ */
107
+ private tokenize(input: string): string[] {
108
+ const specialChars = /[()\[\]{}"~^:]/;
109
+ const operators = /AND|OR|NOT|TO/;
110
+
111
+ let tokens: string[] = [];
112
+ let current = '';
113
+ let inQuote = false;
114
+
115
+ for (let i = 0; i < input.length; i++) {
116
+ const char = input[i];
117
+
118
+ // Handle quoted strings
119
+ if (char === '"') {
120
+ if (inQuote) {
121
+ tokens.push(current + char);
122
+ current = '';
123
+ inQuote = false;
124
+ } else {
125
+ if (current) tokens.push(current);
126
+ current = char;
127
+ inQuote = true;
128
+ }
129
+ continue;
130
+ }
131
+
132
+ if (inQuote) {
133
+ current += char;
134
+ continue;
135
+ }
136
+
137
+ // Handle whitespace
138
+ if (char === ' ' || char === '\t' || char === '\n') {
139
+ if (current) {
140
+ tokens.push(current);
141
+ current = '';
142
+ }
143
+ continue;
144
+ }
145
+
146
+ // Handle special characters
147
+ if (specialChars.test(char)) {
148
+ if (current) {
149
+ tokens.push(current);
150
+ current = '';
151
+ }
152
+ tokens.push(char);
153
+ continue;
154
+ }
155
+
156
+ current += char;
157
+
158
+ // Check if current is an operator
159
+ if (operators.test(current) && (i + 1 === input.length || /\s/.test(input[i + 1]))) {
160
+ tokens.push(current);
161
+ current = '';
162
+ }
163
+ }
164
+
165
+ if (current) tokens.push(current);
166
+
167
+ return tokens;
168
+ }
169
+
170
+ /**
171
+ * Parse the main query expression
172
+ */
173
+ private parseQuery(): AnyQueryNode {
174
+ const left = this.parseBooleanOperand();
175
+
176
+ if (this.pos < this.tokens.length) {
177
+ const token = this.tokens[this.pos];
178
+
179
+ if (token === 'AND' || token === 'OR') {
180
+ this.pos++;
181
+ const right = this.parseQuery();
182
+ return {
183
+ type: token as 'AND' | 'OR',
184
+ left,
185
+ right,
186
+ };
187
+ } else if (token === 'NOT' || token === '-') {
188
+ this.pos++;
189
+ const right = this.parseQuery();
190
+ return {
191
+ type: 'NOT',
192
+ left,
193
+ right,
194
+ };
195
+ }
196
+ }
197
+
198
+ return left;
199
+ }
200
+
201
+ /**
202
+ * Parse boolean operands (terms, phrases, fields, groups)
203
+ */
204
+ private parseBooleanOperand(): AnyQueryNode {
205
+ if (this.pos >= this.tokens.length) {
206
+ throw new Error('Unexpected end of input');
207
+ }
208
+
209
+ const token = this.tokens[this.pos];
210
+
211
+ // Handle grouping with parentheses
212
+ if (token === '(') {
213
+ this.pos++;
214
+ const group = this.parseQuery();
215
+
216
+ if (this.pos < this.tokens.length && this.tokens[this.pos] === ')') {
217
+ this.pos++;
218
+ return { type: 'GROUP', value: group } as GroupNode;
219
+ } else {
220
+ throw new Error('Unclosed group');
221
+ }
222
+ }
223
+
224
+ // Handle fields (field:value)
225
+ if (this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1] === ':') {
226
+ const field = token;
227
+ this.pos += 2; // Skip field and colon
228
+
229
+ if (this.pos < this.tokens.length) {
230
+ const value = this.parseBooleanOperand();
231
+ return { type: 'FIELD', field, value } as FieldNode;
232
+ } else {
233
+ throw new Error('Expected value after field');
234
+ }
235
+ }
236
+
237
+ // Handle range queries
238
+ if (token === '[' || token === '{') {
239
+ return this.parseRange();
240
+ }
241
+
242
+ // Handle phrases ("term term")
243
+ if (token.startsWith('"') && token.endsWith('"')) {
244
+ const phrase = token.slice(1, -1);
245
+ this.pos++;
246
+
247
+ // Check for proximity operator
248
+ let proximity: number | undefined;
249
+ if (this.pos < this.tokens.length && this.tokens[this.pos] === '~') {
250
+ this.pos++;
251
+ if (this.pos < this.tokens.length && /^\d+$/.test(this.tokens[this.pos])) {
252
+ proximity = parseInt(this.tokens[this.pos], 10);
253
+ this.pos++;
254
+ } else {
255
+ throw new Error('Expected number after proximity operator');
256
+ }
257
+ }
258
+
259
+ return { type: 'PHRASE', value: phrase, proximity } as PhraseNode;
260
+ }
261
+
262
+ // Handle wildcards
263
+ if (token.includes('*') || token.includes('?')) {
264
+ this.pos++;
265
+ return { type: 'WILDCARD', value: token } as WildcardNode;
266
+ }
267
+
268
+ // Handle fuzzy searches
269
+ if (this.pos + 1 < this.tokens.length && this.tokens[this.pos + 1] === '~') {
270
+ const term = token;
271
+ this.pos += 2; // Skip term and tilde
272
+
273
+ let maxEdits = 2; // Default
274
+ if (this.pos < this.tokens.length && /^\d+$/.test(this.tokens[this.pos])) {
275
+ maxEdits = parseInt(this.tokens[this.pos], 10);
276
+ this.pos++;
277
+ }
278
+
279
+ return { type: 'FUZZY', value: term, maxEdits } as FuzzyNode;
280
+ }
281
+
282
+ // Simple term
283
+ this.pos++;
284
+ return { type: 'TERM', value: token } as TermNode;
285
+ }
286
+
287
+ /**
288
+ * Parse range queries
289
+ */
290
+ private parseRange(): RangeNode {
291
+ const includeLower = this.tokens[this.pos] === '[';
292
+ const includeUpper = this.tokens[this.pos + 4] === ']';
293
+
294
+ // Ensure tokens for lower, TO, upper, and closing bracket exist
295
+ if (this.pos + 4 >= this.tokens.length) {
296
+ throw new Error('Invalid range query syntax');
297
+ }
298
+ this.pos++; // Skip open bracket
299
+
300
+ const lower = this.tokens[this.pos];
301
+ this.pos++;
302
+
303
+ if (this.tokens[this.pos] !== 'TO') {
304
+ throw new Error('Expected TO in range query');
305
+ }
306
+ this.pos++;
307
+
308
+ const upper = this.tokens[this.pos];
309
+ this.pos++;
310
+
311
+ if (this.tokens[this.pos] !== (includeLower ? ']' : '}')) {
312
+ throw new Error('Invalid range query closing bracket');
313
+ }
314
+ this.pos++;
315
+
316
+ // For simplicity, assuming the field is handled separately
317
+ return {
318
+ type: 'RANGE',
319
+ field: '', // This will be filled by the field node
320
+ lower,
321
+ upper,
322
+ includeLower,
323
+ includeUpper,
324
+ };
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Transformer for Lucene AST to MongoDB query
330
+ * FIXED VERSION - proper MongoDB query structure
331
+ */
332
+ export class LuceneToMongoTransformer {
333
+ private defaultFields: string[];
334
+ constructor(defaultFields: string[] = []) {
335
+ this.defaultFields = defaultFields;
336
+ }
337
+ /**
338
+ * Escape special characters for use in RegExp patterns
339
+ */
340
+ private escapeRegex(input: string): string {
341
+ return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
342
+ }
343
+
344
+ /**
345
+ * Transform a Lucene AST node to a MongoDB query
346
+ */
347
+ transform(node: AnyQueryNode, searchFields?: string[]): any {
348
+ switch (node.type) {
349
+ case 'TERM':
350
+ return this.transformTerm(node, searchFields);
351
+ case 'PHRASE':
352
+ return this.transformPhrase(node, searchFields);
353
+ case 'FIELD':
354
+ return this.transformField(node);
355
+ case 'AND':
356
+ return this.transformAnd(node);
357
+ case 'OR':
358
+ return this.transformOr(node);
359
+ case 'NOT':
360
+ return this.transformNot(node);
361
+ case 'RANGE':
362
+ return this.transformRange(node);
363
+ case 'WILDCARD':
364
+ return this.transformWildcard(node, searchFields);
365
+ case 'FUZZY':
366
+ return this.transformFuzzy(node, searchFields);
367
+ case 'GROUP':
368
+ return this.transform(node.value, searchFields);
369
+ default:
370
+ throw new Error(`Unsupported node type: ${(node as any).type}`);
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Transform a term to MongoDB query
376
+ * FIXED: properly structured $or query for multiple fields
377
+ */
378
+ private transformTerm(node: TermNode, searchFields?: string[]): any {
379
+ // Build regex pattern, support wildcard (*) and fuzzy (?) if present
380
+ const term = node.value;
381
+ // Determine regex pattern: wildcard conversion or exact escape
382
+ let pattern: string;
383
+ if (term.includes('*') || term.includes('?')) {
384
+ pattern = this.luceneWildcardToRegex(term);
385
+ } else {
386
+ pattern = this.escapeRegex(term);
387
+ }
388
+ // Search across provided fields or default fields
389
+ const fields = searchFields && searchFields.length > 0 ? searchFields : this.defaultFields;
390
+ const orConditions = fields.map((field) => ({
391
+ [field]: { $regex: pattern, $options: 'i' },
392
+ }));
393
+ return { $or: orConditions };
394
+ }
395
+
396
+ /**
397
+ * Transform a phrase to MongoDB query
398
+ * FIXED: properly structured $or query for multiple fields
399
+ */
400
+ private transformPhrase(node: PhraseNode, searchFields?: string[]): any {
401
+ // Use regex across provided fields or default fields, respecting word boundaries
402
+ const parts = node.value.split(/\s+/).map((t) => this.escapeRegex(t));
403
+ const pattern = parts.join('\\s+');
404
+ const fields = searchFields && searchFields.length > 0 ? searchFields : this.defaultFields;
405
+ const orConditions = fields.map((field) => ({
406
+ [field]: { $regex: pattern, $options: 'i' },
407
+ }));
408
+ return { $or: orConditions };
409
+ }
410
+
411
+ /**
412
+ * Transform a field query to MongoDB query
413
+ */
414
+ private transformField(node: FieldNode): any {
415
+ // Handle special case for range queries on fields
416
+ if (node.value.type === 'RANGE') {
417
+ const rangeNode = node.value as RangeNode;
418
+ rangeNode.field = node.field;
419
+ return this.transformRange(rangeNode);
420
+ }
421
+
422
+ // Handle special case for wildcards on fields
423
+ if (node.value.type === 'WILDCARD') {
424
+ return {
425
+ [node.field]: {
426
+ $regex: this.luceneWildcardToRegex((node.value as WildcardNode).value),
427
+ $options: 'i',
428
+ },
429
+ };
430
+ }
431
+
432
+ // Handle special case for fuzzy searches on fields
433
+ if (node.value.type === 'FUZZY') {
434
+ return {
435
+ [node.field]: {
436
+ $regex: this.createFuzzyRegex((node.value as FuzzyNode).value),
437
+ $options: 'i',
438
+ },
439
+ };
440
+ }
441
+
442
+ // Special case for exact term matches on fields (supporting wildcard characters)
443
+ if (node.value.type === 'TERM') {
444
+ const val = (node.value as TermNode).value;
445
+ if (val.includes('*') || val.includes('?')) {
446
+ const regex = this.luceneWildcardToRegex(val);
447
+ return { [node.field]: { $regex: regex, $options: 'i' } };
448
+ }
449
+ return { [node.field]: { $regex: val, $options: 'i' } };
450
+ }
451
+
452
+ // Special case for phrase matches on fields
453
+ if (node.value.type === 'PHRASE') {
454
+ return {
455
+ [node.field]: {
456
+ $regex: `${(node.value as PhraseNode).value.replace(/\s+/g, '\\s+')}`,
457
+ $options: 'i',
458
+ },
459
+ };
460
+ }
461
+
462
+ // For other cases, we'll transform the value and apply it to the field
463
+ const transformedValue = this.transform(node.value);
464
+
465
+ // If the transformed value uses $text, we need to adapt it for the field
466
+ if (transformedValue.$text) {
467
+ return { [node.field]: { $regex: transformedValue.$text.$search, $options: 'i' } };
468
+ }
469
+
470
+ // Handle $or and $and cases
471
+ if (transformedValue.$or || transformedValue.$and) {
472
+ // This is a bit complex - we need to restructure the query to apply the field
473
+ // For now, simplify by just using a regex on the field
474
+ const term = this.extractTermFromBooleanQuery(transformedValue);
475
+ if (term) {
476
+ return { [node.field]: { $regex: term, $options: 'i' } };
477
+ }
478
+ }
479
+
480
+ return { [node.field]: transformedValue };
481
+ }
482
+
483
+ /**
484
+ * Extract a term from a boolean query (simplification)
485
+ */
486
+ private extractTermFromBooleanQuery(query: any): string | null {
487
+ if (query.$or && Array.isArray(query.$or) && query.$or.length > 0) {
488
+ const firstClause = query.$or[0];
489
+ for (const field in firstClause) {
490
+ if (firstClause[field].$regex) {
491
+ return firstClause[field].$regex;
492
+ }
493
+ }
494
+ }
495
+
496
+ if (query.$and && Array.isArray(query.$and) && query.$and.length > 0) {
497
+ const firstClause = query.$and[0];
498
+ for (const field in firstClause) {
499
+ if (firstClause[field].$regex) {
500
+ return firstClause[field].$regex;
501
+ }
502
+ }
503
+ }
504
+
505
+ return null;
506
+ }
507
+
508
+ /**
509
+ * Transform AND operator to MongoDB query
510
+ * FIXED: $and must be an array
511
+ */
512
+ private transformAnd(node: BooleanNode): any {
513
+ return { $and: [this.transform(node.left), this.transform(node.right)] };
514
+ }
515
+
516
+ /**
517
+ * Transform OR operator to MongoDB query
518
+ * FIXED: $or must be an array
519
+ */
520
+ private transformOr(node: BooleanNode): any {
521
+ return { $or: [this.transform(node.left), this.transform(node.right)] };
522
+ }
523
+
524
+ /**
525
+ * Transform NOT operator to MongoDB query
526
+ * FIXED: $and must be an array and $not usage
527
+ */
528
+ private transformNot(node: BooleanNode): any {
529
+ const leftQuery = this.transform(node.left);
530
+ const rightQuery = this.transform(node.right);
531
+
532
+ // Create a query that includes left but excludes right
533
+ if (rightQuery.$text) {
534
+ // For text searches, we need a different approach
535
+ // We'll use a negated regex instead
536
+ const searchTerm = rightQuery.$text.$search.replace(/"/g, '');
537
+
538
+ // Determine the fields to apply the negation to
539
+ const notConditions: any[] = [];
540
+
541
+ for (const field in leftQuery) {
542
+ if (field !== '$or' && field !== '$and') {
543
+ notConditions.push({
544
+ [field]: { $not: { $regex: searchTerm, $options: 'i' } },
545
+ });
546
+ }
547
+ }
548
+
549
+ // If left query has $or or $and, we need to handle it differently
550
+ if (leftQuery.$or) {
551
+ return {
552
+ $and: [leftQuery, { $nor: [{ $or: notConditions }] }],
553
+ };
554
+ } else {
555
+ // Simple case - just add $not to each field
556
+ return {
557
+ $and: [leftQuery, { $and: notConditions }],
558
+ };
559
+ }
560
+ } else {
561
+ // For other queries, we can use $not directly
562
+ // We need to handle different structures based on the rightQuery
563
+ let notQuery = {};
564
+
565
+ if (rightQuery.$or) {
566
+ notQuery = { $nor: rightQuery.$or };
567
+ } else if (rightQuery.$and) {
568
+ // Convert $and to $nor
569
+ notQuery = { $nor: rightQuery.$and };
570
+ } else {
571
+ // Simple field condition
572
+ for (const field in rightQuery) {
573
+ notQuery[field] = { $not: rightQuery[field] };
574
+ }
575
+ }
576
+
577
+ return { $and: [leftQuery, notQuery] };
578
+ }
579
+ }
580
+
581
+ /**
582
+ * Transform range query to MongoDB query
583
+ */
584
+ private transformRange(node: RangeNode): any {
585
+ const range: any = {};
586
+
587
+ if (node.lower !== '*') {
588
+ range[node.includeLower ? '$gte' : '$gt'] = this.parseValue(node.lower);
589
+ }
590
+
591
+ if (node.upper !== '*') {
592
+ range[node.includeUpper ? '$lte' : '$lt'] = this.parseValue(node.upper);
593
+ }
594
+
595
+ return { [node.field]: range };
596
+ }
597
+
598
+ /**
599
+ * Transform wildcard query to MongoDB query
600
+ * FIXED: properly structured for multiple fields
601
+ */
602
+ private transformWildcard(node: WildcardNode, searchFields?: string[]): any {
603
+ // Convert Lucene wildcards to MongoDB regex
604
+ const regex = this.luceneWildcardToRegex(node.value);
605
+
606
+ // If specific fields are provided, search wildcard across those fields
607
+ if (searchFields && searchFields.length > 0) {
608
+ const orConditions = searchFields.map((field) => ({
609
+ [field]: { $regex: regex, $options: 'i' },
610
+ }));
611
+
612
+ return { $or: orConditions };
613
+ }
614
+
615
+ // By default, apply to the default field
616
+ return { $regex: regex, $options: 'i' };
617
+ }
618
+
619
+ /**
620
+ * Transform fuzzy query to MongoDB query
621
+ * FIXED: properly structured for multiple fields
622
+ */
623
+ private transformFuzzy(node: FuzzyNode, searchFields?: string[]): any {
624
+ // MongoDB doesn't have built-in fuzzy search
625
+ // This is a very basic approach using regex
626
+ const regex = this.createFuzzyRegex(node.value);
627
+
628
+ // If specific fields are provided, search fuzzy term across those fields
629
+ if (searchFields && searchFields.length > 0) {
630
+ const orConditions = searchFields.map((field) => ({
631
+ [field]: { $regex: regex, $options: 'i' },
632
+ }));
633
+
634
+ return { $or: orConditions };
635
+ }
636
+
637
+ // By default, apply to the default field
638
+ return { $regex: regex, $options: 'i' };
639
+ }
640
+
641
+ /**
642
+ * Convert Lucene wildcards to MongoDB regex patterns
643
+ */
644
+ public luceneWildcardToRegex(wildcardPattern: string): string {
645
+ // Replace Lucene wildcards with regex equivalents
646
+ // * => .*
647
+ // ? => .
648
+ // Also escape regex special chars
649
+ return wildcardPattern
650
+ .replace(/([.+^${}()|\\])/g, '\\$1') // Escape regex special chars
651
+ .replace(/\*/g, '.*')
652
+ .replace(/\?/g, '.');
653
+ }
654
+
655
+ /**
656
+ * Create a simplified fuzzy search regex
657
+ */
658
+ private createFuzzyRegex(term: string): string {
659
+ // For a very simple approach, we allow some characters to be optional
660
+ let regex = '';
661
+ for (let i = 0; i < term.length; i++) {
662
+ // Make every other character optional (simplified fuzzy)
663
+ if (i % 2 === 1) {
664
+ regex += term[i] + '?';
665
+ } else {
666
+ regex += term[i];
667
+ }
668
+ }
669
+ return regex;
670
+ }
671
+
672
+ /**
673
+ * Parse string values to appropriate types (numbers, dates, etc.)
674
+ */
675
+ private parseValue(value: string): any {
676
+ // Try to parse as number
677
+ if (/^-?\d+$/.test(value)) {
678
+ return parseInt(value, 10);
679
+ }
680
+
681
+ if (/^-?\d+\.\d+$/.test(value)) {
682
+ return parseFloat(value);
683
+ }
684
+
685
+ // Try to parse as date (simplified)
686
+ const date = new Date(value);
687
+ if (!isNaN(date.getTime())) {
688
+ return date;
689
+ }
690
+
691
+ // Default to string
692
+ return value;
693
+ }
694
+ }
695
+
696
+ /**
697
+ * Main adapter class
698
+ */
699
+ export class SmartdataLuceneAdapter {
700
+ private parser: LuceneParser;
701
+ private transformer: LuceneToMongoTransformer;
702
+ private defaultSearchFields: string[] = [];
703
+
704
+ /**
705
+ * @param defaultSearchFields - Optional array of field names to search across when no field is specified
706
+ */
707
+ constructor(defaultSearchFields?: string[]) {
708
+ this.parser = new LuceneParser();
709
+ // Pass default searchable fields into transformer
710
+ this.transformer = new LuceneToMongoTransformer(defaultSearchFields || []);
711
+ if (defaultSearchFields) {
712
+ this.defaultSearchFields = defaultSearchFields;
713
+ }
714
+ }
715
+
716
+ /**
717
+ * Convert a Lucene query string to a MongoDB query object
718
+ * @param luceneQuery - The Lucene query string to convert
719
+ * @param searchFields - Optional array of field names to search across (overrides defaultSearchFields)
720
+ */
721
+ convert(luceneQuery: string, searchFields?: string[]): any {
722
+ try {
723
+ // For simple single-term queries (no field:, boolean, grouping), use simpler regex
724
+ if (
725
+ !luceneQuery.includes(':') &&
726
+ !luceneQuery.includes(' AND ') &&
727
+ !luceneQuery.includes(' OR ') &&
728
+ !luceneQuery.includes(' NOT ') &&
729
+ !luceneQuery.includes('(') &&
730
+ !luceneQuery.includes('[')
731
+ ) {
732
+ const fieldsToSearch = searchFields || this.defaultSearchFields;
733
+ if (fieldsToSearch && fieldsToSearch.length > 0) {
734
+ // Handle wildcard characters in query
735
+ let pattern = luceneQuery;
736
+ if (luceneQuery.includes('*') || luceneQuery.includes('?')) {
737
+ // Use transformer to convert wildcard pattern
738
+ pattern = this.transformer.luceneWildcardToRegex(luceneQuery);
739
+ }
740
+ return {
741
+ $or: fieldsToSearch.map((field) => ({
742
+ [field]: { $regex: pattern, $options: 'i' },
743
+ })),
744
+ };
745
+ }
746
+ }
747
+
748
+ // For more complex queries, use the full parser
749
+ // Parse the Lucene query into an AST
750
+ const ast = this.parser.parse(luceneQuery);
751
+
752
+ // Use provided searchFields, fall back to defaultSearchFields
753
+ const fieldsToSearch = searchFields || this.defaultSearchFields;
754
+
755
+ // Transform the AST to a MongoDB query
756
+ return this.transformWithFields(ast, fieldsToSearch);
757
+ } catch (error) {
758
+ logger.log('error', `Failed to convert Lucene query "${luceneQuery}":`, error);
759
+ throw new Error(`Failed to convert Lucene query: ${error}`);
760
+ }
761
+ }
762
+
763
+ /**
764
+ * Helper method to transform the AST with field information
765
+ */
766
+ private transformWithFields(node: AnyQueryNode, searchFields: string[]): any {
767
+ // Special case for term nodes without a specific field
768
+ if (
769
+ node.type === 'TERM' ||
770
+ node.type === 'PHRASE' ||
771
+ node.type === 'WILDCARD' ||
772
+ node.type === 'FUZZY'
773
+ ) {
774
+ return this.transformer.transform(node, searchFields);
775
+ }
776
+
777
+ // For other node types, use the standard transformation
778
+ return this.transformer.transform(node);
779
+ }
780
+ }