hlquery-node-client 1.0.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/lib/Client.js ADDED
@@ -0,0 +1,623 @@
1
+ /**
2
+ * hlquery Node.js Client - Main Client Class
3
+ * Elasticsearch-like API client for hlquery
4
+ *
5
+ * Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
6
+ *
7
+ * This file is part of hlquery, released under the BSD License version 3.
8
+ */
9
+
10
+ const Request = require('./Request');
11
+ const Collections = require('./Collections');
12
+ const Documents = require('./Documents');
13
+ const Search = require('./Search');
14
+ const Keys = require('./Keys');
15
+ const Aliases = require('./Aliases');
16
+ const Overrides = require('./Overrides');
17
+ const Synonyms = require('./Synonyms');
18
+ const Stopwords = require('./Stopwords');
19
+ const System = require('./System');
20
+ const Config = require('../utils/Config');
21
+
22
+ /**
23
+ * Main hlquery client class
24
+ */
25
+ class Client {
26
+ constructor(baseUrl = null, options = {}) {
27
+ const opts = Config.mergeDefaults(options);
28
+
29
+ baseUrl = baseUrl || opts.base_url;
30
+ baseUrl = Config.normalizeUrl(baseUrl);
31
+
32
+ if (!Config.isValidUrl(baseUrl)) {
33
+ throw new Error(`Invalid base URL: ${baseUrl}`);
34
+ }
35
+
36
+ this.request = new Request(
37
+ baseUrl,
38
+ opts.timeout,
39
+ opts.token || null,
40
+ opts.auth_method || 'bearer'
41
+ );
42
+
43
+ this._collections = new Collections(this.request);
44
+ this._documents = new Documents(this.request);
45
+ this._search = new Search(this.request, this._collections);
46
+ this._keys = new Keys(this.request);
47
+ this._aliases = new Aliases(this.request);
48
+ this._overrides = new Overrides(this.request);
49
+ this._synonyms = new Synonyms(this.request);
50
+ this._stopwords = new Stopwords(this.request);
51
+ this._system = new System(this.request);
52
+ }
53
+
54
+ /**
55
+ * Set authentication token
56
+ *
57
+ * @param {string} token
58
+ * @param {string} method 'bearer' or 'api-key'
59
+ * @returns {this}
60
+ */
61
+ setAuthToken(token, method = 'bearer') {
62
+ this.request.setAuthToken(token, method);
63
+ return this;
64
+ }
65
+
66
+ /**
67
+ * Clear authentication
68
+ *
69
+ * @returns {this}
70
+ */
71
+ clearAuth() {
72
+ this.request.clearAuth();
73
+ return this;
74
+ }
75
+
76
+ // ============================================================================
77
+ // CLUSTER & NODE APIs
78
+ // ============================================================================
79
+
80
+ /**
81
+ * Health check
82
+ *
83
+ * @returns {Promise<Response>}
84
+ */
85
+ async health() {
86
+ return await this._system.health();
87
+ }
88
+
89
+ /**
90
+ * Get server stats
91
+ *
92
+ * @returns {Promise<Response>}
93
+ */
94
+ async stats() {
95
+ return await this._system.stats();
96
+ }
97
+
98
+ /**
99
+ * Get protocol codes for API communication
100
+ * Returns HTTP status codes and protocol information
101
+ *
102
+ * @returns {Promise<Response>}
103
+ */
104
+ async etc() {
105
+ return await this._system.etc();
106
+ }
107
+
108
+ /**
109
+ * Get server info
110
+ *
111
+ * @returns {Promise<Response>}
112
+ */
113
+ async info() {
114
+ return await this._system.info();
115
+ }
116
+
117
+ /**
118
+ * Get server status.
119
+ *
120
+ * @returns {Promise<Response>}
121
+ */
122
+ async status() {
123
+ return await this._system.status();
124
+ }
125
+
126
+ /**
127
+ * Get startup readiness state.
128
+ *
129
+ * @returns {Promise<Response>}
130
+ */
131
+ async startup() {
132
+ return await this._system.startup();
133
+ }
134
+
135
+ /**
136
+ * Get boot status readiness alias.
137
+ *
138
+ * @returns {Promise<Response>}
139
+ */
140
+ async bootStatus() {
141
+ return await this._system.bootStatus();
142
+ }
143
+
144
+ /**
145
+ * Get metrics.
146
+ *
147
+ * @returns {Promise<Response>}
148
+ */
149
+ async metrics() {
150
+ return await this._system.metrics();
151
+ }
152
+
153
+ /**
154
+ * Get JSON metrics alias.
155
+ *
156
+ * @returns {Promise<Response>}
157
+ */
158
+ async metricsJson() {
159
+ return await this._system.metricsJson();
160
+ }
161
+
162
+ /**
163
+ * Get live connections.
164
+ *
165
+ * @returns {Promise<Response>}
166
+ */
167
+ async connections() {
168
+ return await this._system.connections();
169
+ }
170
+
171
+ /**
172
+ * Get RocksDB stats.
173
+ *
174
+ * @returns {Promise<Response>}
175
+ */
176
+ async rocksdb() {
177
+ return await this._system.rocksdb();
178
+ }
179
+
180
+ /**
181
+ * Get internal RocksDB stats alias.
182
+ *
183
+ * @returns {Promise<Response>}
184
+ */
185
+ async rocksdbInternal() {
186
+ return await this._system.rocksdbInternal();
187
+ }
188
+
189
+ /**
190
+ * Get document count across collections.
191
+ *
192
+ * @returns {Promise<Response>}
193
+ */
194
+ async docTotal() {
195
+ return await this._system.docTotal();
196
+ }
197
+
198
+ /**
199
+ * Ping server.
200
+ *
201
+ * @returns {Promise<Response>}
202
+ */
203
+ async ping() {
204
+ return await this._system.ping();
205
+ }
206
+
207
+ /**
208
+ * Run integrity checks.
209
+ *
210
+ * @returns {Promise<Response>}
211
+ */
212
+ async integrity() {
213
+ return await this._system.integrity();
214
+ }
215
+
216
+ /**
217
+ * Run consistency checks.
218
+ *
219
+ * @returns {Promise<Response>}
220
+ */
221
+ async consistency() {
222
+ return await this._system.consistency();
223
+ }
224
+
225
+ /**
226
+ * Run self-check endpoint.
227
+ *
228
+ * @returns {Promise<Response>}
229
+ */
230
+ async selfCheck() {
231
+ return await this._system.selfCheck();
232
+ }
233
+
234
+ /**
235
+ * Get storage status.
236
+ *
237
+ * @returns {Promise<Response>}
238
+ */
239
+ async storageStatus() {
240
+ return await this._system.storageStatus();
241
+ }
242
+
243
+ /**
244
+ * Execute a top-level SQL query through GET /sql.
245
+ *
246
+ * @param {string} sql
247
+ * @param {object} params
248
+ * @returns {Promise<Response>}
249
+ */
250
+ async sql(sql, params = {}) {
251
+ return await this._system.sql(sql, params);
252
+ }
253
+
254
+ /**
255
+ * Execute a top-level SQL statement through POST /sql.
256
+ *
257
+ * @param {string} sql
258
+ * @returns {Promise<Response>}
259
+ */
260
+ async execSql(sql) {
261
+ return await this._system.execSql(sql);
262
+ }
263
+
264
+ /**
265
+ * Get cluster health status
266
+ *
267
+ * @returns {Promise<Response>}
268
+ */
269
+ async clusterHealth() {
270
+ return await this.request.execute('GET', '/cluster/health');
271
+ }
272
+
273
+ /**
274
+ * Get cluster statistics
275
+ *
276
+ * @returns {Promise<Response>}
277
+ */
278
+ async clusterStats() {
279
+ return await this.request.execute('GET', '/cluster/stats');
280
+ }
281
+
282
+ /**
283
+ * Get list of nodes in the cluster
284
+ *
285
+ * @returns {Promise<Response>}
286
+ */
287
+ async clusterNodes() {
288
+ return await this.request.execute('GET', '/cluster/nodes');
289
+ }
290
+
291
+ /**
292
+ * List configured cluster links
293
+ *
294
+ * @returns {Promise<Response>}
295
+ */
296
+ async links() {
297
+ return await this.request.execute('GET', '/links');
298
+ }
299
+
300
+ /**
301
+ * Ping configured cluster links
302
+ *
303
+ * @returns {Promise<Response>}
304
+ */
305
+ async linksPing() {
306
+ return await this.request.execute('GET', '/links/ping');
307
+ }
308
+
309
+ /**
310
+ * Add a cluster link (in-memory only)
311
+ *
312
+ * @param {string} endpointOrHost
313
+ * @param {number|null} port
314
+ * @returns {Promise<Response>}
315
+ */
316
+ async linksConnect(endpointOrHost, port = null) {
317
+ const body = (port === null) ? { endpoint: endpointOrHost } : { host: endpointOrHost, port };
318
+ return await this.request.execute('POST', '/links/connect', body);
319
+ }
320
+
321
+ /**
322
+ * Remove a cluster link (in-memory only)
323
+ *
324
+ * @param {string} endpointOrHost
325
+ * @param {number|null} port
326
+ * @returns {Promise<Response>}
327
+ */
328
+ async linksDisconnect(endpointOrHost, port = null) {
329
+ const body = (port === null) ? { endpoint: endpointOrHost } : { host: endpointOrHost, port };
330
+ return await this.request.execute('POST', '/links/disconnect', body);
331
+ }
332
+
333
+ /**
334
+ * Flush all data to disk
335
+ *
336
+ * @returns {Promise<Response>}
337
+ */
338
+ async flush() {
339
+ return await this.request.execute('POST', '/flush');
340
+ }
341
+
342
+ // ============================================================================
343
+ // COLLECTIONS API
344
+ // ============================================================================
345
+
346
+ /**
347
+ * Get collections API instance
348
+ *
349
+ * @returns {Collections}
350
+ */
351
+ collections() {
352
+ return this._collections;
353
+ }
354
+
355
+ /**
356
+ * List all collections
357
+ *
358
+ * @param {number} offset
359
+ * @param {number} limit
360
+ * @returns {Promise<Response>}
361
+ */
362
+ async listCollections(offset = 0, limit = 10) {
363
+ return await this._collections.list(offset, limit);
364
+ }
365
+
366
+ /**
367
+ * List collections across all configured nodes
368
+ *
369
+ * @returns {Promise<Response>}
370
+ */
371
+ async listCollectionsDistributed() {
372
+ return await this.request.execute('GET', '/collections/distributed');
373
+ }
374
+
375
+ /**
376
+ * Get collection details
377
+ *
378
+ * @param {string} name
379
+ * @returns {Promise<Response>}
380
+ */
381
+ async getCollection(name) {
382
+ return await this._collections.get(name);
383
+ }
384
+
385
+ /**
386
+ * Get collection fields (formatted)
387
+ *
388
+ * @param {string} name
389
+ * @returns {Promise<Response>}
390
+ */
391
+ async getCollectionFields(name) {
392
+ return await this._collections.getFields(name);
393
+ }
394
+
395
+ // ============================================================================
396
+ // DOCUMENTS API
397
+ // ============================================================================
398
+
399
+ /**
400
+ * Get documents API instance
401
+ *
402
+ * @returns {Documents}
403
+ */
404
+ documents() {
405
+ return this._documents;
406
+ }
407
+
408
+ /**
409
+ * List documents in a collection
410
+ *
411
+ * @param {string} collectionName
412
+ * @param {object} params
413
+ * @returns {Promise<Response>}
414
+ */
415
+ async listDocuments(collectionName, params = {}) {
416
+ return await this._documents.list(collectionName, params);
417
+ }
418
+
419
+ /**
420
+ * Get a document by ID
421
+ *
422
+ * @param {string} collectionName
423
+ * @param {string} documentId
424
+ * @returns {Promise<Response>}
425
+ */
426
+ async getDocument(collectionName, documentId) {
427
+ return await this._documents.get(collectionName, documentId);
428
+ }
429
+
430
+ /**
431
+ * Export documents from a collection.
432
+ *
433
+ * @param {string} collectionName
434
+ * @param {object} params
435
+ * @returns {Promise<Response>}
436
+ */
437
+ async exportDocuments(collectionName, params = {}) {
438
+ return await this._documents.export(collectionName, params);
439
+ }
440
+
441
+ /**
442
+ * Compute facet counts for a collection.
443
+ *
444
+ * @param {string} collectionName
445
+ * @param {object} params
446
+ * @returns {Promise<Response>}
447
+ */
448
+ async facetCounts(collectionName, params = {}) {
449
+ return await this._documents.facetCounts(collectionName, params);
450
+ }
451
+
452
+ // ============================================================================
453
+ // SEARCH API
454
+ // ============================================================================
455
+
456
+ /**
457
+ * Get search API instance
458
+ *
459
+ * @returns {Search}
460
+ */
461
+ searchApi() {
462
+ return this._search;
463
+ }
464
+
465
+ /**
466
+ * Get aliases API instance
467
+ *
468
+ * @returns {Aliases}
469
+ */
470
+ aliases() {
471
+ return this._aliases;
472
+ }
473
+
474
+ /**
475
+ * Get overrides API instance
476
+ *
477
+ * @returns {Overrides}
478
+ */
479
+ overrides() {
480
+ return this._overrides;
481
+ }
482
+
483
+ /**
484
+ * Get synonyms API instance
485
+ *
486
+ * @returns {Synonyms}
487
+ */
488
+ synonyms() {
489
+ return this._synonyms;
490
+ }
491
+
492
+ /**
493
+ * Get stopwords API instance
494
+ *
495
+ * @returns {Stopwords}
496
+ */
497
+ stopwords() {
498
+ return this._stopwords;
499
+ }
500
+
501
+ /**
502
+ * Get system API instance
503
+ *
504
+ * @returns {System}
505
+ */
506
+ system() {
507
+ return this._system;
508
+ }
509
+
510
+ /**
511
+ * Get API keys instance
512
+ *
513
+ * @returns {Keys}
514
+ */
515
+ keys() {
516
+ return this._keys;
517
+ }
518
+
519
+ /**
520
+ * Search documents
521
+ *
522
+ * @param {string} collectionName
523
+ * @param {object} params
524
+ * @returns {Promise<Response>}
525
+ */
526
+ async search(collectionName, params = {}) {
527
+ return await this._search.search(collectionName, params);
528
+ }
529
+
530
+ /**
531
+ * Execute a collection-bound SQL SELECT query.
532
+ *
533
+ * @param {string} collectionName
534
+ * @param {string} sql
535
+ * @param {object} params
536
+ * @returns {Promise<Response>}
537
+ */
538
+ async sqlSearch(collectionName, sql, params = {}) {
539
+ return await this._search.sql(collectionName, sql, params);
540
+ }
541
+
542
+ /**
543
+ * Vector search
544
+ *
545
+ * @param {string} collectionName
546
+ * @param {object} params
547
+ * @returns {Promise<Response>}
548
+ */
549
+ async vectorSearch(collectionName, params = {}) {
550
+ return await this._search.vectorSearch(collectionName, params);
551
+ }
552
+
553
+ /**
554
+ * Global search across collections.
555
+ *
556
+ * @param {object} params
557
+ * @returns {Promise<Response>}
558
+ */
559
+ async globalSearch(params = {}) {
560
+ return await this._search.globalSearch(params);
561
+ }
562
+
563
+ /**
564
+ * Execute arbitrary HTTP request
565
+ *
566
+ * @param {string} method HTTP method
567
+ * @param {string} path API path
568
+ * @param {object|string|null} body Request body
569
+ * @param {object} queryParams Query parameters
570
+ * @returns {Promise<Response>}
571
+ */
572
+ async executeRequest(method, path, body = null, queryParams = {}) {
573
+ return await this.request.execute(method, path, body, queryParams);
574
+ }
575
+
576
+ // ============================================================================
577
+ // ELASTICSEARCH-LIKE ALIASES
578
+ // ============================================================================
579
+
580
+ /**
581
+ * Alias for listCollections() - Elasticsearch compatibility
582
+ *
583
+ * @param {object} params
584
+ * @returns {Promise<Response>}
585
+ */
586
+ async indices(params = {}) {
587
+ return await this.listCollections(
588
+ params.offset || 0,
589
+ params.limit || 10
590
+ );
591
+ }
592
+
593
+ /**
594
+ * Alias for getCollection() or getDocument() - Elasticsearch compatibility
595
+ *
596
+ * @param {object} params
597
+ * @returns {Promise<Response>}
598
+ */
599
+ async get(params) {
600
+ if (params.index && params.id) {
601
+ return await this.getDocument(params.index, params.id);
602
+ } else if (params.index) {
603
+ return await this.getCollection(params.index);
604
+ }
605
+ throw new Error('Invalid parameters for get()');
606
+ }
607
+
608
+ /**
609
+ * Alias for listCollections() - Elasticsearch cat API
610
+ *
611
+ * @param {string} type
612
+ * @param {object} params
613
+ * @returns {Promise<Response>}
614
+ */
615
+ async cat(type = 'indices', params = {}) {
616
+ if (type === 'indices') {
617
+ return await this.indices(params);
618
+ }
619
+ throw new Error(`Unsupported cat type: ${type}`);
620
+ }
621
+ }
622
+
623
+ module.exports = Client;