hlquery-node-client 1.0.5 → 1.0.7
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 +84 -45
- package/lib/Aliases.js +5 -0
- package/lib/Analytics.js +30 -0
- package/lib/Client.js +109 -61
- package/lib/Collections.js +36 -3
- package/lib/Documents.js +98 -6
- package/lib/Exceptions.js +11 -3
- package/lib/Modules.js +64 -0
- package/lib/Request.js +157 -15
- package/lib/Response.js +28 -2
- package/lib/Route.js +111 -0
- package/lib/Search.js +21 -9
- package/lib/System.js +38 -0
- package/lib/Users.js +51 -0
- package/lib/conformance.js +25 -19
- package/package.json +9 -20
- package/test/route-conformance.test.js +83 -0
- package/utils/Config.js +17 -2
- package/utils/Validator.js +12 -29
- package/examples/sam.js +0 -39
- package/index.js +0 -40
- package/lib/SAM.js +0 -174
- package/test/offline-smoke.js +0 -194
package/README.md
CHANGED
|
@@ -16,13 +16,13 @@
|
|
|
16
16
|
|
|
17
17
|
### What is the hlquery Node.js API?
|
|
18
18
|
|
|
19
|
-
The hlquery Node.js API is the official Node.js client for [hlquery](https://github.com/hlquery/hlquery). It wraps the REST interface in a modular service-style client with helpers for collections, documents, search,
|
|
19
|
+
The hlquery Node.js API is the official Node.js client for [hlquery](https://github.com/hlquery/hlquery). It wraps the REST interface in a modular service-style client with helpers for collections, documents, search, and SQL.
|
|
20
20
|
|
|
21
21
|
It is a good fit for backend services, scripts, dashboards, and apps that want hlquery integration without repeating request code.
|
|
22
22
|
|
|
23
23
|
### Why use it?
|
|
24
24
|
|
|
25
|
-
Use the Node.js client when you want hlquery calls to read like regular application code. The client is organized around familiar modules such as `client.collections()
|
|
25
|
+
Use the Node.js client when you want hlquery calls to read like regular application code. The client is organized around familiar modules such as `client.collections()` and `client.documents()`, with Redis-style dynamic route helpers for module APIs and custom endpoints.
|
|
26
26
|
|
|
27
27
|
It also keeps the repetitive parts in one place: authentication, request parameters, endpoint paths, and parsed responses are handled consistently across the client. Common hlquery workflows are covered by default, while raw request access is still available when you need a custom route.
|
|
28
28
|
|
|
@@ -49,12 +49,14 @@ const client = new Client(process.env.HLQ_BASE_URL || process.env.HLQUERY_BASE_U
|
|
|
49
49
|
});
|
|
50
50
|
|
|
51
51
|
const health = await client.system().health();
|
|
52
|
+
|
|
52
53
|
/* Print the HTTP status code from the health response. */
|
|
53
54
|
console.log('status:', health.getStatusCode());
|
|
54
55
|
|
|
55
56
|
const collections = await client.collections().list(0, 10);
|
|
57
|
+
|
|
56
58
|
/* Print the collection list response body. */
|
|
57
|
-
console.log(collections.
|
|
59
|
+
console.log(collections.body);
|
|
58
60
|
```
|
|
59
61
|
|
|
60
62
|
### Auth
|
|
@@ -86,6 +88,16 @@ const disconnect = await client.linksDisconnect('http://node-b:9200');
|
|
|
86
88
|
const flush = await client.flush();
|
|
87
89
|
```
|
|
88
90
|
|
|
91
|
+
Maintenance actions default to `POST`, and can explicitly use either server-supported method:
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
await client.updateCounters({ force: true });
|
|
95
|
+
await client.updateCounters({ force: true }, 'GET');
|
|
96
|
+
await client.repair({}, 'POST');
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The client also wraps readiness, startup, metrics, connection, storage, integrity, counter, user, key, module, and analytics routes through `client.system()`, `client.users()`, `client.keys()`, `client.modules()`, and `client.analytics()`.
|
|
100
|
+
|
|
89
101
|
Routes that do not have a dedicated wrapper can still be called through `executeRequest()`:
|
|
90
102
|
|
|
91
103
|
```javascript
|
|
@@ -93,45 +105,70 @@ const response = await client.executeRequest('GET', '/modules/<name>/<route>', n
|
|
|
93
105
|
q: 'example query'
|
|
94
106
|
});
|
|
95
107
|
|
|
96
|
-
|
|
97
|
-
console.log(status.getBody());
|
|
98
|
-
/* Print the health response body. */
|
|
99
|
-
console.log(health.getBody());
|
|
100
|
-
/* Print the runtime configuration response body. */
|
|
101
|
-
console.log(etc.getBody());
|
|
102
|
-
/* Print the configured cluster links response body. */
|
|
103
|
-
console.log(links.getBody());
|
|
104
|
-
/* Print the link ping response body. */
|
|
105
|
-
console.log(ping.getBody());
|
|
106
|
-
/* Print the link connect response body. */
|
|
107
|
-
console.log(connect.getBody());
|
|
108
|
-
/* Print the link disconnect response body. */
|
|
109
|
-
console.log(disconnect.getBody());
|
|
110
|
-
/* Print the flush response body. */
|
|
111
|
-
console.log(flush.getBody());
|
|
112
|
-
/* Print the custom route response body. */
|
|
113
|
-
console.log(response.getBody());
|
|
108
|
+
console.log(response.body);
|
|
114
109
|
```
|
|
115
110
|
|
|
116
|
-
|
|
111
|
+
For module routes and custom endpoints, the client also supports a Redis-style fluent API:
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const result = await client.module('<name>').route('<route>').get({
|
|
115
|
+
q: 'example query'
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
console.log(result.body);
|
|
119
|
+
|
|
120
|
+
const indexed = await client.module('<name>').route('index').post({
|
|
121
|
+
id: 'doc_1',
|
|
122
|
+
title: 'Example'
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const modules = await client.modules().list();
|
|
126
|
+
const syntax = await client.modules().syntax('<name>');
|
|
127
|
+
const raw = await client.route('etc').get();
|
|
128
|
+
|
|
129
|
+
console.log(indexed.body);
|
|
130
|
+
console.log(modules.body);
|
|
131
|
+
console.log(syntax.body);
|
|
132
|
+
console.log(raw.body);
|
|
133
|
+
```
|
|
117
134
|
|
|
118
|
-
|
|
135
|
+
### Collections, documents, and search
|
|
119
136
|
|
|
120
137
|
```javascript
|
|
121
|
-
const
|
|
138
|
+
const metadata = await client.collections().get('products');
|
|
139
|
+
const language = await client.collections().language('products');
|
|
122
140
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
126
|
-
|
|
141
|
+
// getFields() is a compatibility alias for collection metadata. The server has
|
|
142
|
+
// no /collections/{name}/fields route.
|
|
143
|
+
const metadataAgain = await client.collections().getFields('products');
|
|
144
|
+
|
|
145
|
+
const context = await client.documents().context('products', 'prod_1');
|
|
146
|
+
const facets = await client.documents().facetCounts('products', { facet_by: 'brand' });
|
|
147
|
+
const exported = await client.documents().export('products', { filter_by: 'active:true' });
|
|
148
|
+
const suggestions = await client.documents().maybe('products', { q: 'keybaord' });
|
|
149
|
+
|
|
150
|
+
await client.documents().updateByQuery('products', {
|
|
151
|
+
filter_by: 'active:false',
|
|
152
|
+
set: { archived: true }
|
|
127
153
|
});
|
|
154
|
+
await client.documents().deleteByQuery('products', { filter_by: 'expired:true' });
|
|
128
155
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
156
|
+
const searches = [{ collection: 'products', q: 'keyboard', query_by: 'title' }];
|
|
157
|
+
await client.searchApi().multiSearch(searches); // POST (default)
|
|
158
|
+
await client.searchApi().multiSearch(searches, 'GET');
|
|
159
|
+
await client.globalSearch({ collection: 'products', q: 'keyboard' });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Synonyms, overrides, and aliases expose separate `create()` (`POST`) and `update()` (`PUT`) helpers; `upsert()` uses the client's default upsert verb. Stopwords use their collection or global create/delete routes.
|
|
163
|
+
|
|
164
|
+
### Request safety and errors
|
|
165
|
+
|
|
166
|
+
`executeRequest()` accepts a relative path on the configured hlquery origin. Absolute cross-origin URLs are rejected with `RequestException` code `CROSS_ORIGIN_REQUEST`; this prevents credentials from being forwarded to another host. HTTP error bodies retain the server's `error`, optional `message`, numeric `code`, and stable `code_text` fields.
|
|
167
|
+
|
|
168
|
+
Run the offline route-contract suite with:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
npm test
|
|
135
172
|
```
|
|
136
173
|
|
|
137
174
|
### SQL
|
|
@@ -147,11 +184,11 @@ const books = await client.sqlSearch(
|
|
|
147
184
|
);
|
|
148
185
|
|
|
149
186
|
/* Print the SQL query response body. */
|
|
150
|
-
console.log(rows.
|
|
187
|
+
console.log(rows.body);
|
|
151
188
|
/* Print the SQL execution response body. */
|
|
152
|
-
console.log(execResult.
|
|
189
|
+
console.log(execResult.body);
|
|
153
190
|
/* Print the collection SQL search response body. */
|
|
154
|
-
console.log(books.
|
|
191
|
+
console.log(books.body);
|
|
155
192
|
```
|
|
156
193
|
|
|
157
194
|
### Contributing
|
|
@@ -160,17 +197,19 @@ We welcome contributions from the community! All contributions must be released
|
|
|
160
197
|
|
|
161
198
|
### How to Contribute
|
|
162
199
|
|
|
163
|
-
- Check existing [issues](https://github.com/hlquery/
|
|
164
|
-
- Contribute
|
|
165
|
-
-
|
|
166
|
-
-
|
|
200
|
+
- Check existing [Node.js API issues](https://github.com/hlquery/node-api/issues) or create new ones
|
|
201
|
+
- Contribute Node.js client changes to [hlquery/node-api](https://github.com/hlquery/node-api)
|
|
202
|
+
- Contribute shared server/API changes to [hlquery/hlquery](https://github.com/hlquery/hlquery)
|
|
203
|
+
- Test and report bugs against the Node.js client
|
|
204
|
+
- Improve Node.js-specific documentation and examples
|
|
167
205
|
|
|
168
206
|
### Community
|
|
169
207
|
|
|
170
|
-
-
|
|
171
|
-
-
|
|
172
|
-
-
|
|
208
|
+
- [Documentation](https://docs.hlquery.com)
|
|
209
|
+
- [X (Twitter)](https://x.com/hlquery)
|
|
210
|
+
- [Node.js API GitHub](https://github.com/hlquery/node-api)
|
|
211
|
+
- [hlquery GitHub](https://github.com/hlquery/hlquery)
|
|
173
212
|
|
|
174
213
|
### License
|
|
175
214
|
|
|
176
|
-
hlquery is licensed under the [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
|
|
215
|
+
The hlquery Node.js API is licensed under the [BSD 3-Clause License](https://opensource.org/licenses/BSD-3-Clause).
|
package/lib/Aliases.js
CHANGED
|
@@ -20,6 +20,11 @@ class Aliases {
|
|
|
20
20
|
return await this.request.execute('GET', '/aliases');
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
async listCollection(collectionName) {
|
|
24
|
+
Validator.validateCollectionName(collectionName);
|
|
25
|
+
return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/aliases`);
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
async get(name) {
|
|
24
29
|
Validator.validateCollectionName(name);
|
|
25
30
|
return await this.request.execute('GET', `/aliases/${encodeURIComponent(name)}`);
|
package/lib/Analytics.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hlquery Node.js Client - Analytics API
|
|
3
|
+
*
|
|
4
|
+
* Copyright (C) 2021-2026, Carlos F. Ferry <carlos.ferry@gmail.com>
|
|
5
|
+
*
|
|
6
|
+
* This file is part of hlquery, released under the BSD License version 3.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const Validator = require('../utils/Validator');
|
|
10
|
+
|
|
11
|
+
class Analytics {
|
|
12
|
+
constructor(request) {
|
|
13
|
+
this.request = request;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async click(payload) {
|
|
17
|
+
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
18
|
+
throw new Error('Analytics click payload must be an object');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
Validator.validateCollectionName(payload.collection);
|
|
22
|
+
|
|
23
|
+
const documentId = payload.doc_id !== undefined ? payload.doc_id : payload.document_id;
|
|
24
|
+
Validator.validateDocumentId(documentId);
|
|
25
|
+
|
|
26
|
+
return await this.request.execute('POST', '/analytics/click', payload);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = Analytics;
|
package/lib/Client.js
CHANGED
|
@@ -17,7 +17,10 @@ const Overrides = require('./Overrides');
|
|
|
17
17
|
const Synonyms = require('./Synonyms');
|
|
18
18
|
const Stopwords = require('./Stopwords');
|
|
19
19
|
const System = require('./System');
|
|
20
|
-
const
|
|
20
|
+
const Modules = require('./Modules');
|
|
21
|
+
const Users = require('./Users');
|
|
22
|
+
const Analytics = require('./Analytics');
|
|
23
|
+
const { RouteCommand, ModuleRouteCommand } = require('./Route');
|
|
21
24
|
const Config = require('../utils/Config');
|
|
22
25
|
|
|
23
26
|
/**
|
|
@@ -38,7 +41,8 @@ class Client {
|
|
|
38
41
|
baseUrl,
|
|
39
42
|
opts.timeout,
|
|
40
43
|
opts.token || null,
|
|
41
|
-
opts.auth_method || 'bearer'
|
|
44
|
+
opts.auth_method || 'bearer',
|
|
45
|
+
opts
|
|
42
46
|
);
|
|
43
47
|
|
|
44
48
|
this._collections = new Collections(this.request);
|
|
@@ -50,7 +54,9 @@ class Client {
|
|
|
50
54
|
this._synonyms = new Synonyms(this.request);
|
|
51
55
|
this._stopwords = new Stopwords(this.request);
|
|
52
56
|
this._system = new System(this.request);
|
|
53
|
-
this.
|
|
57
|
+
this._modules = new Modules(this.request);
|
|
58
|
+
this._users = new Users(this.request);
|
|
59
|
+
this._analytics = new Analytics(this.request);
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
/**
|
|
@@ -143,6 +149,10 @@ class Client {
|
|
|
143
149
|
return await this._system.bootStatus();
|
|
144
150
|
}
|
|
145
151
|
|
|
152
|
+
async ready() {
|
|
153
|
+
return await this._system.ready();
|
|
154
|
+
}
|
|
155
|
+
|
|
146
156
|
/**
|
|
147
157
|
* Get metrics.
|
|
148
158
|
*
|
|
@@ -161,6 +171,10 @@ class Client {
|
|
|
161
171
|
return await this._system.metricsJson();
|
|
162
172
|
}
|
|
163
173
|
|
|
174
|
+
async metricsHistory() {
|
|
175
|
+
return await this._system.metricsHistory();
|
|
176
|
+
}
|
|
177
|
+
|
|
164
178
|
/**
|
|
165
179
|
* Get live connections.
|
|
166
180
|
*
|
|
@@ -242,6 +256,22 @@ class Client {
|
|
|
242
256
|
return await this._system.storageStatus();
|
|
243
257
|
}
|
|
244
258
|
|
|
259
|
+
async searchConfig() {
|
|
260
|
+
return await this._system.searchConfig();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async updateCounters(params = {}, method = 'POST') {
|
|
264
|
+
return await this._system.updateCounters(params, method);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async debugCounters() {
|
|
268
|
+
return await this._system.debugCounters();
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async repair(params = {}, method = 'POST') {
|
|
272
|
+
return await this._system.repair(params, method);
|
|
273
|
+
}
|
|
274
|
+
|
|
245
275
|
/**
|
|
246
276
|
* Execute a top-level SQL query through GET /sql.
|
|
247
277
|
*
|
|
@@ -269,7 +299,7 @@ class Client {
|
|
|
269
299
|
* @returns {Promise<Response>}
|
|
270
300
|
*/
|
|
271
301
|
async clusterHealth() {
|
|
272
|
-
return await this.
|
|
302
|
+
return await this._system.health();
|
|
273
303
|
}
|
|
274
304
|
|
|
275
305
|
/**
|
|
@@ -278,7 +308,7 @@ class Client {
|
|
|
278
308
|
* @returns {Promise<Response>}
|
|
279
309
|
*/
|
|
280
310
|
async clusterStats() {
|
|
281
|
-
return await this.
|
|
311
|
+
return await this._system.stats();
|
|
282
312
|
}
|
|
283
313
|
|
|
284
314
|
/**
|
|
@@ -287,7 +317,7 @@ class Client {
|
|
|
287
317
|
* @returns {Promise<Response>}
|
|
288
318
|
*/
|
|
289
319
|
async clusterNodes() {
|
|
290
|
-
return await this.
|
|
320
|
+
return await this.links();
|
|
291
321
|
}
|
|
292
322
|
|
|
293
323
|
/**
|
|
@@ -383,6 +413,10 @@ class Client {
|
|
|
383
413
|
async getCollection(name) {
|
|
384
414
|
return await this._collections.get(name);
|
|
385
415
|
}
|
|
416
|
+
|
|
417
|
+
async getCollectionLanguage(name) {
|
|
418
|
+
return await this._collections.language(name);
|
|
419
|
+
}
|
|
386
420
|
|
|
387
421
|
/**
|
|
388
422
|
* Get collection fields (formatted)
|
|
@@ -503,6 +537,10 @@ class Client {
|
|
|
503
537
|
return await this._documents.get(collectionName, documentId);
|
|
504
538
|
}
|
|
505
539
|
|
|
540
|
+
async getDocumentContext(collectionName, documentId) {
|
|
541
|
+
return await this._documents.context(collectionName, documentId);
|
|
542
|
+
}
|
|
543
|
+
|
|
506
544
|
/**
|
|
507
545
|
* Copy a document to a new ID within the same collection.
|
|
508
546
|
*
|
|
@@ -548,6 +586,18 @@ class Client {
|
|
|
548
586
|
async facetCounts(collectionName, params = {}) {
|
|
549
587
|
return await this._documents.facetCounts(collectionName, params);
|
|
550
588
|
}
|
|
589
|
+
|
|
590
|
+
async maybeDocuments(collectionName, params = {}) {
|
|
591
|
+
return await this._documents.maybe(collectionName, params);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
async updateDocumentsByQuery(collectionName, body = {}) {
|
|
595
|
+
return await this._documents.updateByQuery(collectionName, body);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
async deleteDocumentsByQuery(collectionName, body = {}) {
|
|
599
|
+
return await this._documents.deleteByQuery(collectionName, body);
|
|
600
|
+
}
|
|
551
601
|
|
|
552
602
|
// ============================================================================
|
|
553
603
|
// SEARCH API
|
|
@@ -562,59 +612,6 @@ class Client {
|
|
|
562
612
|
return this._search;
|
|
563
613
|
}
|
|
564
614
|
|
|
565
|
-
/**
|
|
566
|
-
* Get SAM API instance
|
|
567
|
-
*
|
|
568
|
-
* @returns {SAM}
|
|
569
|
-
*/
|
|
570
|
-
sam() {
|
|
571
|
-
return this._sam;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
async samSearch(collectionName, query, params = {}) {
|
|
575
|
-
return await this._sam.search(collectionName, query, params);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
async samSearchAll(query, params = {}) {
|
|
579
|
-
return await this._sam.searchAll(query, params);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
async samRebuild(collectionName, params = {}) {
|
|
583
|
-
return await this._sam.rebuild(collectionName, params);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
async samStatus(collectionName = null, params = {}) {
|
|
587
|
-
return await this._sam.status(collectionName, params);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
async samDebug(collectionName = null, params = {}) {
|
|
591
|
-
return await this._sam.debug(collectionName, params);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
async samHistory(collectionName = null, limit = 100, params = {}) {
|
|
595
|
-
return await this._sam.history(collectionName, limit, params);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
async samPause(pauseUntilMs, params = {}) {
|
|
599
|
-
return await this._sam.pause(pauseUntilMs, params);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
async samClearPause(params = {}) {
|
|
603
|
-
return await this._sam.clearPause(params);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
async samDocuments(collectionName, offset = 0, limit = 20, params = {}) {
|
|
607
|
-
return await this._sam.listDocuments(collectionName, offset, limit, params);
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
async samDocument(collectionName, documentId, params = {}) {
|
|
611
|
-
return await this._sam.getDocument(collectionName, documentId, params);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
async samOpenDocument(collectionName, documentId, interactionQuery = null, params = {}) {
|
|
615
|
-
return await this._sam.openDocument(collectionName, documentId, interactionQuery, params);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
615
|
/**
|
|
619
616
|
* Get aliases API instance
|
|
620
617
|
*
|
|
@@ -668,6 +665,57 @@ class Client {
|
|
|
668
665
|
keys() {
|
|
669
666
|
return this._keys;
|
|
670
667
|
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Get runtime modules API instance
|
|
671
|
+
*
|
|
672
|
+
* @returns {Modules}
|
|
673
|
+
*/
|
|
674
|
+
modules() {
|
|
675
|
+
return this._modules;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Get users API instance.
|
|
680
|
+
*
|
|
681
|
+
* @returns {Users}
|
|
682
|
+
*/
|
|
683
|
+
users() {
|
|
684
|
+
return this._users;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Get analytics API instance.
|
|
689
|
+
*
|
|
690
|
+
* @returns {Analytics}
|
|
691
|
+
*/
|
|
692
|
+
analytics() {
|
|
693
|
+
return this._analytics;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
async analyticsClick(payload) {
|
|
697
|
+
return await this._analytics.click(payload);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* Build a Redis-style dynamic module command.
|
|
702
|
+
*
|
|
703
|
+
* @param {string} name
|
|
704
|
+
* @returns {ModuleRouteCommand}
|
|
705
|
+
*/
|
|
706
|
+
module(name) {
|
|
707
|
+
return new ModuleRouteCommand(this.request, name);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/**
|
|
711
|
+
* Build a Redis-style top-level route command.
|
|
712
|
+
*
|
|
713
|
+
* @param {string|Array<string>} route
|
|
714
|
+
* @returns {RouteCommand}
|
|
715
|
+
*/
|
|
716
|
+
route(route) {
|
|
717
|
+
return new RouteCommand(this.request, '/').route(route);
|
|
718
|
+
}
|
|
671
719
|
|
|
672
720
|
/**
|
|
673
721
|
* Search documents
|
|
@@ -722,8 +770,8 @@ class Client {
|
|
|
722
770
|
* @param {object} queryParams Query parameters
|
|
723
771
|
* @returns {Promise<Response>}
|
|
724
772
|
*/
|
|
725
|
-
async executeRequest(method, path, body = null, queryParams = {}) {
|
|
726
|
-
return await this.request.execute(method, path, body, queryParams);
|
|
773
|
+
async executeRequest(method, path, body = null, queryParams = {}, options = {}) {
|
|
774
|
+
return await this.request.execute(method, path, body, queryParams, options);
|
|
727
775
|
}
|
|
728
776
|
|
|
729
777
|
// ============================================================================
|
package/lib/Collections.js
CHANGED
|
@@ -42,6 +42,17 @@ class Collections {
|
|
|
42
42
|
Validator.validateCollectionName(name);
|
|
43
43
|
return await this.request.execute('GET', `/collections/${encodeURIComponent(name)}`);
|
|
44
44
|
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get detected collection language information.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} name Collection name
|
|
50
|
+
* @returns {Promise<Response>}
|
|
51
|
+
*/
|
|
52
|
+
async language(name) {
|
|
53
|
+
Validator.validateCollectionName(name);
|
|
54
|
+
return await this.request.execute('GET', `/collections/${encodeURIComponent(name)}/lang`);
|
|
55
|
+
}
|
|
45
56
|
|
|
46
57
|
/**
|
|
47
58
|
* Create a new collection
|
|
@@ -52,10 +63,14 @@ class Collections {
|
|
|
52
63
|
*/
|
|
53
64
|
async create(name, schema) {
|
|
54
65
|
Validator.validateCollectionName(name);
|
|
66
|
+
if (schema && schema.name !== undefined && schema.name !== name) {
|
|
67
|
+
throw new Error('Schema name must match the collection name argument');
|
|
68
|
+
}
|
|
69
|
+
|
|
55
70
|
// Merge schema with name at top level (server expects fields/searchable_fields at root)
|
|
56
71
|
const requestBody = {
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
...schema,
|
|
73
|
+
name: name
|
|
59
74
|
};
|
|
60
75
|
return await this.request.execute('POST', '/collections', requestBody);
|
|
61
76
|
}
|
|
@@ -99,6 +114,24 @@ class Collections {
|
|
|
99
114
|
const body = response.getBody();
|
|
100
115
|
const allFields = [];
|
|
101
116
|
const fieldTypes = {};
|
|
117
|
+
|
|
118
|
+
if (Array.isArray(body.fields)) {
|
|
119
|
+
for (const field of body.fields) {
|
|
120
|
+
const fieldName = typeof field === 'string' ? field : field.name;
|
|
121
|
+
if (!fieldName) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (!allFields.includes(fieldName)) {
|
|
125
|
+
allFields.push(fieldName);
|
|
126
|
+
}
|
|
127
|
+
if (!fieldTypes[fieldName]) {
|
|
128
|
+
fieldTypes[fieldName] = [];
|
|
129
|
+
}
|
|
130
|
+
if (field.type && !fieldTypes[fieldName].includes(field.type)) {
|
|
131
|
+
fieldTypes[fieldName].push(field.type);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
102
135
|
|
|
103
136
|
// Collect searchable fields
|
|
104
137
|
if (body.searchable_fields) {
|
|
@@ -142,7 +175,7 @@ class Collections {
|
|
|
142
175
|
// Format fields
|
|
143
176
|
const fields = allFields.map(field => ({
|
|
144
177
|
name: field,
|
|
145
|
-
type: fieldTypes[field].join(', ')
|
|
178
|
+
type: fieldTypes[field].join(', ') || 'unknown'
|
|
146
179
|
}));
|
|
147
180
|
|
|
148
181
|
return new Response(200, {
|