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/.github/workflows/ci.yml +37 -0
- package/LICENSE +29 -0
- package/README.md +316 -0
- package/example.js +636 -0
- package/examples/basic_usage.js +35 -0
- package/examples/collections.js +40 -0
- package/examples/csv.js +39 -0
- package/examples/documents.js +54 -0
- package/examples/flush.js +128 -0
- package/examples/keys.js +58 -0
- package/examples/pdf.js +40 -0
- package/examples/search.js +101 -0
- package/examples/vector.js +465 -0
- package/index.js +39 -0
- package/lib/Aliases.js +48 -0
- package/lib/Client.js +623 -0
- package/lib/Collections.js +159 -0
- package/lib/Documents.js +244 -0
- package/lib/Exceptions.js +101 -0
- package/lib/Keys.js +85 -0
- package/lib/Overrides.js +53 -0
- package/lib/Request.js +147 -0
- package/lib/Response.js +54 -0
- package/lib/Search.js +350 -0
- package/lib/Stopwords.js +60 -0
- package/lib/Synonyms.js +85 -0
- package/lib/System.js +110 -0
- package/lib/conformance.js +74 -0
- package/package.json +42 -0
- package/test/offline-smoke.js +86 -0
- package/utils/Auth.js +34 -0
- package/utils/CSVParser.js +160 -0
- package/utils/Config.js +57 -0
- package/utils/PDFParser.js +196 -0
- package/utils/Validator.js +143 -0
- package/utils/ranker.js +51 -0
package/lib/Request.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hlquery Node.js Client - HTTP Request Handler
|
|
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 https = require('https');
|
|
10
|
+
const http = require('http');
|
|
11
|
+
const { URL } = require('url');
|
|
12
|
+
const { RequestException, AuthenticationException, DemoModeException } = require('./Exceptions');
|
|
13
|
+
const Response = require('./Response');
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* HTTP request handler
|
|
17
|
+
*/
|
|
18
|
+
class Request {
|
|
19
|
+
constructor(baseUrl, timeout = 30000, authToken = null, authMethod = 'bearer') {
|
|
20
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
|
21
|
+
this.timeout = timeout;
|
|
22
|
+
this.authToken = authToken;
|
|
23
|
+
this.authMethod = authMethod;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setAuthToken(token, method = 'bearer') {
|
|
27
|
+
this.authToken = token;
|
|
28
|
+
this.authMethod = method;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
clearAuth() {
|
|
32
|
+
this.authToken = null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Make HTTP request
|
|
37
|
+
*
|
|
38
|
+
* @param {string} method HTTP method
|
|
39
|
+
* @param {string} path API path
|
|
40
|
+
* @param {object|string|null} body Request body
|
|
41
|
+
* @param {object} queryParams Query parameters
|
|
42
|
+
* @returns {Promise<Response>}
|
|
43
|
+
* @throws {RequestException}
|
|
44
|
+
*/
|
|
45
|
+
async execute(method, path, body = null, queryParams = {}) {
|
|
46
|
+
const url = new URL(this.baseUrl + path);
|
|
47
|
+
|
|
48
|
+
// Add query parameters
|
|
49
|
+
Object.keys(queryParams).forEach(key => {
|
|
50
|
+
if (queryParams[key] !== undefined && queryParams[key] !== null) {
|
|
51
|
+
url.searchParams.append(key, queryParams[key]);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const headers = {
|
|
56
|
+
'Content-Type': 'application/json',
|
|
57
|
+
'Accept': 'application/json'
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Prepare body string if present
|
|
61
|
+
let bodyStr = null;
|
|
62
|
+
if (body !== null) {
|
|
63
|
+
bodyStr = typeof body === 'string' ? body : JSON.stringify(body);
|
|
64
|
+
headers['Content-Length'] = Buffer.byteLength(bodyStr);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add authentication header if token is set
|
|
68
|
+
if (this.authToken !== null) {
|
|
69
|
+
if (this.authMethod === 'api-key') {
|
|
70
|
+
headers['X-API-Key'] = this.authToken;
|
|
71
|
+
} else {
|
|
72
|
+
headers['Authorization'] = `Bearer ${this.authToken}`;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const options = {
|
|
77
|
+
method: method,
|
|
78
|
+
headers: headers,
|
|
79
|
+
timeout: this.timeout
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const client = url.protocol === 'https:' ? https : http;
|
|
83
|
+
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const req = client.request(url, options, (res) => {
|
|
86
|
+
const responseHeaders = {};
|
|
87
|
+
Object.keys(res.headers).forEach(key => {
|
|
88
|
+
responseHeaders[key.toLowerCase()] = res.headers[key];
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
let data = '';
|
|
92
|
+
|
|
93
|
+
res.on('data', (chunk) => {
|
|
94
|
+
data += chunk;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
res.on('end', () => {
|
|
98
|
+
let decoded;
|
|
99
|
+
try {
|
|
100
|
+
decoded = data ? JSON.parse(data) : null;
|
|
101
|
+
} catch (e) {
|
|
102
|
+
// If JSON parsing fails, return raw response
|
|
103
|
+
decoded = data;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Check for demo mode errors
|
|
107
|
+
if (res.statusCode === 403 && decoded && typeof decoded === 'object') {
|
|
108
|
+
const errorMsg = decoded.error || decoded.message || '';
|
|
109
|
+
if (errorMsg.includes('Demo mode is enabled') ||
|
|
110
|
+
errorMsg.includes('Operation not allowed') ||
|
|
111
|
+
errorMsg.includes('Write operations are not allowed')) {
|
|
112
|
+
return reject(new DemoModeException(
|
|
113
|
+
`Demo mode is enabled on the server. Write operations are blocked. Only search and read operations are permitted. Server message: ${decoded.message || decoded.error}`
|
|
114
|
+
));
|
|
115
|
+
}
|
|
116
|
+
// Check if server rejected token because auth is disabled
|
|
117
|
+
if (errorMsg.includes('Authentication is disabled') ||
|
|
118
|
+
errorMsg.includes('Tokens are not accepted when authentication is disabled')) {
|
|
119
|
+
return reject(new AuthenticationException(
|
|
120
|
+
`Authentication is disabled on the server. Remove the token from your client configuration. Server message: ${decoded.message || decoded.error}`
|
|
121
|
+
));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
resolve(new Response(res.statusCode, decoded, responseHeaders));
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
req.on('error', (error) => {
|
|
130
|
+
reject(new RequestException(`Request failed: ${error.message}`, 0));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
req.on('timeout', () => {
|
|
134
|
+
req.destroy();
|
|
135
|
+
reject(new RequestException('Request timeout', 0));
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
if (bodyStr !== null) {
|
|
139
|
+
req.write(bodyStr);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
req.end();
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = Request;
|
package/lib/Response.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hlquery Node.js Client - Response Handler
|
|
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
|
+
/**
|
|
10
|
+
* Response wrapper for API responses
|
|
11
|
+
*/
|
|
12
|
+
class Response {
|
|
13
|
+
constructor(statusCode, body, headers = {}) {
|
|
14
|
+
this.statusCode = statusCode;
|
|
15
|
+
this.body = body;
|
|
16
|
+
this.headers = headers;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getStatusCode() {
|
|
20
|
+
return this.statusCode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
getBody() {
|
|
24
|
+
return this.body;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getHeaders() {
|
|
28
|
+
return this.headers;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
isSuccess() {
|
|
32
|
+
return this.statusCode >= 200 && this.statusCode < 300;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
isError() {
|
|
36
|
+
return this.statusCode >= 400;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getError() {
|
|
40
|
+
if (this.isError() && typeof this.body === 'object' && this.body !== null) {
|
|
41
|
+
return this.body.error || this.body.message || 'Unknown error';
|
|
42
|
+
}
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
toArray() {
|
|
47
|
+
return {
|
|
48
|
+
status: this.statusCode,
|
|
49
|
+
body: this.body
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
module.exports = Response;
|
package/lib/Search.js
ADDED
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hlquery Node.js Client - Search 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
|
+
/**
|
|
12
|
+
* Search API operations
|
|
13
|
+
*/
|
|
14
|
+
class Search {
|
|
15
|
+
constructor(request, collections) {
|
|
16
|
+
this.request = request;
|
|
17
|
+
this.collections = collections;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Search documents
|
|
22
|
+
*
|
|
23
|
+
* @param {string} collectionName
|
|
24
|
+
* @param {object} params Search parameters
|
|
25
|
+
* @returns {Promise<Response>}
|
|
26
|
+
*/
|
|
27
|
+
async search(collectionName, params = {}) {
|
|
28
|
+
return await this._searchCollection(collectionName, params, false);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Execute a collection-bound SQL SELECT through the search endpoint.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} collectionName
|
|
35
|
+
* @param {string} sql
|
|
36
|
+
* @param {object} params
|
|
37
|
+
* @returns {Promise<Response>}
|
|
38
|
+
*/
|
|
39
|
+
async sql(collectionName, sql, params = {}) {
|
|
40
|
+
Validator.validateCollectionName(collectionName);
|
|
41
|
+
|
|
42
|
+
if (typeof sql !== 'string' || sql.trim() === '') {
|
|
43
|
+
throw new Error('SQL query must be a non-empty string');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (params === null || typeof params !== 'object' || Array.isArray(params)) {
|
|
47
|
+
throw new Error('SQL params must be an object');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const queryParams = { ...params, sql };
|
|
51
|
+
|
|
52
|
+
return await this.request.execute(
|
|
53
|
+
'GET',
|
|
54
|
+
`/collections/${encodeURIComponent(collectionName)}/documents/search`,
|
|
55
|
+
null,
|
|
56
|
+
queryParams
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Search documents through the legacy /documents/search route.
|
|
62
|
+
*
|
|
63
|
+
* @param {string} collectionName
|
|
64
|
+
* @param {object} params
|
|
65
|
+
* @returns {Promise<Response>}
|
|
66
|
+
*/
|
|
67
|
+
async searchLegacy(collectionName, params = {}) {
|
|
68
|
+
return await this._searchCollection(collectionName, params, true);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async _searchCollection(collectionName, params = {}, useLegacyPath = false) {
|
|
72
|
+
Validator.validateCollectionName(collectionName);
|
|
73
|
+
Validator.validateSearchParams(params);
|
|
74
|
+
|
|
75
|
+
const queryParams = {};
|
|
76
|
+
|
|
77
|
+
// Handle structured query object (Elasticsearch-like)
|
|
78
|
+
if (params.query) {
|
|
79
|
+
if (params.query.q) {
|
|
80
|
+
queryParams.q = params.query.q;
|
|
81
|
+
}
|
|
82
|
+
if (params.query.query_by) {
|
|
83
|
+
queryParams.query_by = Array.isArray(params.query.query_by)
|
|
84
|
+
? params.query.query_by.join(',')
|
|
85
|
+
: params.query.query_by;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Direct query parameters
|
|
90
|
+
if (params.q) {
|
|
91
|
+
queryParams.q = params.q;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Fields to search in
|
|
95
|
+
if (params.query_by) {
|
|
96
|
+
queryParams.query_by = Array.isArray(params.query_by)
|
|
97
|
+
? params.query_by.join(',')
|
|
98
|
+
: params.query_by;
|
|
99
|
+
} else if (params.q && params.q !== '') {
|
|
100
|
+
// Auto-detect searchable fields
|
|
101
|
+
const collection = await this.collections.get(collectionName);
|
|
102
|
+
if (collection.getStatusCode() === 200) {
|
|
103
|
+
const body = collection.getBody();
|
|
104
|
+
if (body.searchable_fields && body.searchable_fields.length > 0) {
|
|
105
|
+
queryParams.query_by = body.searchable_fields.join(',');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Pagination
|
|
111
|
+
if (params.from !== undefined) {
|
|
112
|
+
queryParams.offset = params.from;
|
|
113
|
+
} else if (params.offset !== undefined) {
|
|
114
|
+
queryParams.offset = params.offset;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (params.size !== undefined) {
|
|
118
|
+
queryParams.limit = params.size;
|
|
119
|
+
} else if (params.limit !== undefined) {
|
|
120
|
+
queryParams.limit = params.limit;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Page-based pagination
|
|
124
|
+
if (params.page !== undefined) {
|
|
125
|
+
queryParams.page = params.page;
|
|
126
|
+
}
|
|
127
|
+
if (params.per_page !== undefined) {
|
|
128
|
+
queryParams.per_page = params.per_page;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Filter
|
|
132
|
+
if (params.filter_by) {
|
|
133
|
+
queryParams.filter_by = params.filter_by;
|
|
134
|
+
} else if (params.filter) {
|
|
135
|
+
queryParams.filter_by = typeof params.filter === 'object'
|
|
136
|
+
? JSON.stringify(params.filter)
|
|
137
|
+
: params.filter;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Sort
|
|
141
|
+
if (params.sort) {
|
|
142
|
+
if (Array.isArray(params.sort)) {
|
|
143
|
+
const sortFields = [];
|
|
144
|
+
for (const sortItem of params.sort) {
|
|
145
|
+
if (typeof sortItem === 'object') {
|
|
146
|
+
for (const [field, order] of Object.entries(sortItem)) {
|
|
147
|
+
sortFields.push(order === 'desc' ? `-${field}` : field);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
sortFields.push(sortItem);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
queryParams.sort_by = sortFields.join(',');
|
|
154
|
+
} else {
|
|
155
|
+
queryParams.sort_by = params.sort;
|
|
156
|
+
}
|
|
157
|
+
} else if (params.sort_by) {
|
|
158
|
+
queryParams.sort_by = Array.isArray(params.sort_by)
|
|
159
|
+
? params.sort_by.join(',')
|
|
160
|
+
: params.sort_by;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Facets
|
|
164
|
+
if (params.facet_by) {
|
|
165
|
+
queryParams.facet_by = Array.isArray(params.facet_by)
|
|
166
|
+
? params.facet_by.join(',')
|
|
167
|
+
: params.facet_by;
|
|
168
|
+
} else if (params.facets) {
|
|
169
|
+
queryParams.facet_by = Array.isArray(params.facets)
|
|
170
|
+
? params.facets.join(',')
|
|
171
|
+
: params.facets;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Additional search parameters
|
|
175
|
+
if (params.typo_tolerance !== undefined) {
|
|
176
|
+
queryParams.typo_tolerance = params.typo_tolerance;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (params.num_typos !== undefined) {
|
|
180
|
+
queryParams.num_typos = params.num_typos;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Highlighting parameters
|
|
184
|
+
if (params.highlight !== undefined) {
|
|
185
|
+
queryParams.highlight = params.highlight === true || params.highlight === 'true' ? 'true' : 'false';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (params.highlight_fields !== undefined) {
|
|
189
|
+
queryParams.highlight_fields = Array.isArray(params.highlight_fields)
|
|
190
|
+
? params.highlight_fields.join(',')
|
|
191
|
+
: params.highlight_fields;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (params.highlight_full_fields !== undefined) {
|
|
195
|
+
queryParams.highlight_full_fields = Array.isArray(params.highlight_full_fields)
|
|
196
|
+
? params.highlight_full_fields.join(',')
|
|
197
|
+
: params.highlight_full_fields;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Determine HTTP method
|
|
201
|
+
const method = params.body ? 'POST' : 'GET';
|
|
202
|
+
const body = params.body || null;
|
|
203
|
+
|
|
204
|
+
const path = useLegacyPath
|
|
205
|
+
? `/collections/${encodeURIComponent(collectionName)}/documents/search`
|
|
206
|
+
: `/collections/${encodeURIComponent(collectionName)}/search`;
|
|
207
|
+
|
|
208
|
+
return await this.request.execute(method, path, body, queryParams);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Multi-search across multiple collections
|
|
213
|
+
*
|
|
214
|
+
* @param {Array} searches Array of search requests
|
|
215
|
+
* @returns {Promise<Response>}
|
|
216
|
+
*/
|
|
217
|
+
async multiSearch(searches) {
|
|
218
|
+
return await this.request.execute('POST', '/multi_search', { searches: searches });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Global search across collections.
|
|
223
|
+
*
|
|
224
|
+
* @param {object} params
|
|
225
|
+
* @returns {Promise<Response>}
|
|
226
|
+
*/
|
|
227
|
+
async globalSearch(params = {}) {
|
|
228
|
+
Validator.validateSearchParams(params);
|
|
229
|
+
|
|
230
|
+
const method = params.body ? 'POST' : 'GET';
|
|
231
|
+
const body = params.body || (method === 'POST' ? params : null);
|
|
232
|
+
const queryParams = method === 'GET' ? params : {};
|
|
233
|
+
|
|
234
|
+
return await this.request.execute(method, '/search', body, queryParams);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Vector search
|
|
239
|
+
*
|
|
240
|
+
* @param {string} collectionName
|
|
241
|
+
* @param {object} params Vector search parameters:
|
|
242
|
+
* - vector_query: Array of floats or JSON string
|
|
243
|
+
* - vectorQuery: Array/object alias for vector_query
|
|
244
|
+
* - vector: Array of floats or JSON string (alias for vector_query)
|
|
245
|
+
* - embedding: Array of floats or JSON string (alias for vector_query)
|
|
246
|
+
* - field_name/field/fieldName: Field name to search in (default: 'embedding')
|
|
247
|
+
* - limit/topk/top_k/k/per_page: Number of results
|
|
248
|
+
* - threshold: Similarity threshold (default: 0.0)
|
|
249
|
+
* - normalize: Normalize vectors (default: true)
|
|
250
|
+
* - output_fields/outputFields, include_vector/includeVector, include_distance/includeDistance
|
|
251
|
+
* - radius/max_distance, range_filter/min_distance
|
|
252
|
+
* - query_params/queryParams/params, filter/filter_by/filterBy
|
|
253
|
+
* @returns {Promise<Response>}
|
|
254
|
+
*/
|
|
255
|
+
async vectorSearch(collectionName, params = {}) {
|
|
256
|
+
Validator.validateCollectionName(collectionName);
|
|
257
|
+
|
|
258
|
+
const queryParams = {};
|
|
259
|
+
let forcePost = false;
|
|
260
|
+
|
|
261
|
+
// Handle vector query
|
|
262
|
+
if (params.vector_query) {
|
|
263
|
+
queryParams.vector_query = Array.isArray(params.vector_query)
|
|
264
|
+
? JSON.stringify(params.vector_query)
|
|
265
|
+
: params.vector_query;
|
|
266
|
+
} else if (params.vectorQuery) {
|
|
267
|
+
queryParams.vector_query = Array.isArray(params.vectorQuery) || typeof params.vectorQuery === 'object'
|
|
268
|
+
? JSON.stringify(params.vectorQuery)
|
|
269
|
+
: params.vectorQuery;
|
|
270
|
+
} else if (params.vector) {
|
|
271
|
+
queryParams.vector_query = Array.isArray(params.vector)
|
|
272
|
+
? JSON.stringify(params.vector)
|
|
273
|
+
: params.vector;
|
|
274
|
+
} else if (params.embedding) {
|
|
275
|
+
queryParams.vector_query = Array.isArray(params.embedding)
|
|
276
|
+
? JSON.stringify(params.embedding)
|
|
277
|
+
: params.embedding;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (params.field_name || params.field || params.fieldName) {
|
|
281
|
+
queryParams.field_name = params.field_name || params.field || params.fieldName;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (params.limit !== undefined || params.topk !== undefined || params.top_k !== undefined || params.topK !== undefined ||
|
|
285
|
+
params.k !== undefined || params.per_page !== undefined) {
|
|
286
|
+
queryParams.limit = params.limit ?? params.topk ?? params.top_k ?? params.topK ?? params.k ?? params.per_page;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (params.threshold !== undefined) {
|
|
290
|
+
queryParams.threshold = params.threshold;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (params.normalize !== undefined) {
|
|
294
|
+
queryParams.normalize = params.normalize ? 'true' : 'false';
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (params.output_fields !== undefined || params.outputFields !== undefined) {
|
|
298
|
+
const outputFields = params.output_fields ?? params.outputFields;
|
|
299
|
+
queryParams.output_fields = Array.isArray(outputFields)
|
|
300
|
+
? outputFields.join(',')
|
|
301
|
+
: outputFields;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (params.include_vector !== undefined || params.includeVector !== undefined) {
|
|
305
|
+
const includeVector = params.include_vector ?? params.includeVector;
|
|
306
|
+
queryParams.include_vector = includeVector ? 'true' : 'false';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (params.include_distance !== undefined || params.includeDistance !== undefined) {
|
|
310
|
+
const includeDistance = params.include_distance ?? params.includeDistance;
|
|
311
|
+
queryParams.include_distance = includeDistance ? 'true' : 'false';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (params.filter_by !== undefined) {
|
|
315
|
+
queryParams.filter_by = params.filter_by;
|
|
316
|
+
} else if (params.filterBy !== undefined) {
|
|
317
|
+
queryParams.filter_by = params.filterBy;
|
|
318
|
+
} else if (params.filter !== undefined) {
|
|
319
|
+
queryParams.filter_by = typeof params.filter === 'object'
|
|
320
|
+
? JSON.stringify(params.filter)
|
|
321
|
+
: params.filter;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (params.radius !== undefined) {
|
|
325
|
+
queryParams.radius = params.radius;
|
|
326
|
+
}
|
|
327
|
+
if (params.max_distance !== undefined || params.maxDistance !== undefined) {
|
|
328
|
+
queryParams.max_distance = params.max_distance ?? params.maxDistance;
|
|
329
|
+
}
|
|
330
|
+
if (params.range_filter !== undefined || params.rangeFilter !== undefined) {
|
|
331
|
+
queryParams.range_filter = params.range_filter ?? params.rangeFilter;
|
|
332
|
+
}
|
|
333
|
+
if (params.min_distance !== undefined || params.minDistance !== undefined) {
|
|
334
|
+
queryParams.min_distance = params.min_distance ?? params.minDistance;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (params.query_params !== undefined || params.queryParams !== undefined || params.params !== undefined ||
|
|
338
|
+
params.vector_queries !== undefined || params.vectorQueries !== undefined || params.vectorQuery !== undefined) {
|
|
339
|
+
forcePost = true;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const path = `/collections/${encodeURIComponent(collectionName)}/vector_search`;
|
|
343
|
+
const method = params.body || forcePost ? 'POST' : 'GET';
|
|
344
|
+
const body = params.body || (forcePost ? params : null);
|
|
345
|
+
|
|
346
|
+
return await this.request.execute(method, path, body, queryParams);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
module.exports = Search;
|
package/lib/Stopwords.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hlquery Node.js Client - Stopwords 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
|
+
const { ValidationException } = require('./Exceptions');
|
|
11
|
+
|
|
12
|
+
function validateWord(word, fieldName = 'word') {
|
|
13
|
+
if (!word || typeof word !== 'string' || word.trim() === '') {
|
|
14
|
+
throw new ValidationException(`${fieldName} must be a non-empty string`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Stopwords API operations
|
|
20
|
+
*/
|
|
21
|
+
class Stopwords {
|
|
22
|
+
constructor(request) {
|
|
23
|
+
this.request = request;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async list(collectionName) {
|
|
27
|
+
Validator.validateCollectionName(collectionName);
|
|
28
|
+
return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/stopwords`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async create(collectionName, params) {
|
|
32
|
+
Validator.validateCollectionName(collectionName);
|
|
33
|
+
return await this.request.execute('POST', `/collections/${encodeURIComponent(collectionName)}/stopwords`, params);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async delete(collectionName, word) {
|
|
37
|
+
Validator.validateCollectionName(collectionName);
|
|
38
|
+
validateWord(word);
|
|
39
|
+
return await this.request.execute('DELETE', `/collections/${encodeURIComponent(collectionName)}/stopwords/${encodeURIComponent(word)}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async listAll() {
|
|
43
|
+
return await this.request.execute('GET', '/stopwords');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async listGlobal() {
|
|
47
|
+
return await this.request.execute('GET', '/stopwords/global');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async createGlobal(params) {
|
|
51
|
+
return await this.request.execute('POST', '/stopwords/global', params);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async deleteGlobal(word) {
|
|
55
|
+
validateWord(word);
|
|
56
|
+
return await this.request.execute('DELETE', `/stopwords/global/${encodeURIComponent(word)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = Stopwords;
|
package/lib/Synonyms.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hlquery Node.js Client - Synonyms 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
|
+
/**
|
|
12
|
+
* Synonyms API operations
|
|
13
|
+
*/
|
|
14
|
+
class Synonyms {
|
|
15
|
+
constructor(request) {
|
|
16
|
+
this.request = request;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async list(collectionName) {
|
|
20
|
+
Validator.validateCollectionName(collectionName);
|
|
21
|
+
return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/synonyms`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async get(collectionName, id) {
|
|
25
|
+
Validator.validateCollectionName(collectionName);
|
|
26
|
+
Validator.validateDocumentId(id);
|
|
27
|
+
return await this.request.execute('GET', `/collections/${encodeURIComponent(collectionName)}/synonyms/${encodeURIComponent(id)}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async create(collectionName, id, synonym) {
|
|
31
|
+
Validator.validateCollectionName(collectionName);
|
|
32
|
+
Validator.validateDocumentId(id);
|
|
33
|
+
return await this.request.execute('POST', `/collections/${encodeURIComponent(collectionName)}/synonyms/${encodeURIComponent(id)}`, synonym);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async update(collectionName, id, synonym) {
|
|
37
|
+
Validator.validateCollectionName(collectionName);
|
|
38
|
+
Validator.validateDocumentId(id);
|
|
39
|
+
return await this.request.execute('PUT', `/collections/${encodeURIComponent(collectionName)}/synonyms/${encodeURIComponent(id)}`, synonym);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async upsert(collectionName, id, synonym) {
|
|
43
|
+
return await this.create(collectionName, id, synonym);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async delete(collectionName, id) {
|
|
47
|
+
Validator.validateCollectionName(collectionName);
|
|
48
|
+
Validator.validateDocumentId(id);
|
|
49
|
+
return await this.request.execute('DELETE', `/collections/${encodeURIComponent(collectionName)}/synonyms/${encodeURIComponent(id)}`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async listAll() {
|
|
53
|
+
return await this.request.execute('GET', '/synonyms');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async listGlobal() {
|
|
57
|
+
return await this.request.execute('GET', '/synonyms/global');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async getGlobal(id) {
|
|
61
|
+
Validator.validateDocumentId(id);
|
|
62
|
+
return await this.request.execute('GET', `/synonyms/global/${encodeURIComponent(id)}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async createGlobal(id, synonym) {
|
|
66
|
+
Validator.validateDocumentId(id);
|
|
67
|
+
return await this.request.execute('POST', `/synonyms/global/${encodeURIComponent(id)}`, synonym);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async updateGlobal(id, synonym) {
|
|
71
|
+
Validator.validateDocumentId(id);
|
|
72
|
+
return await this.request.execute('PUT', `/synonyms/global/${encodeURIComponent(id)}`, synonym);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async upsertGlobal(id, synonym) {
|
|
76
|
+
return await this.createGlobal(id, synonym);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async deleteGlobal(id) {
|
|
80
|
+
Validator.validateDocumentId(id);
|
|
81
|
+
return await this.request.execute('DELETE', `/synonyms/global/${encodeURIComponent(id)}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = Synonyms;
|