@atscript/moost-db 0.1.36 → 0.1.38
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/dist/index.cjs +53 -19
- package/dist/index.d.ts +13 -6
- package/dist/index.mjs +52 -18
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -26,7 +26,7 @@ const __atscript_typescript_utils = __toESM(require("@atscript/typescript/utils"
|
|
|
26
26
|
const __moostjs_event_http = __toESM(require("@moostjs/event-http"));
|
|
27
27
|
const moost = __toESM(require("moost"));
|
|
28
28
|
const __uniqu_url = __toESM(require("@uniqu/url"));
|
|
29
|
-
const
|
|
29
|
+
const __atscript_db = __toESM(require("@atscript/db"));
|
|
30
30
|
|
|
31
31
|
//#region packages/moost-db/src/decorators.ts
|
|
32
32
|
const READABLE_DEF = "__atscript_db_readable_def";
|
|
@@ -44,7 +44,7 @@ function transformValidationError(error, reply) {
|
|
|
44
44
|
statusCode: 400,
|
|
45
45
|
errors: error.errors
|
|
46
46
|
}));
|
|
47
|
-
else if (error instanceof
|
|
47
|
+
else if (error instanceof __atscript_db.DbError) {
|
|
48
48
|
const statusCode = dbErrorCodeToStatus[error.code] ?? 400;
|
|
49
49
|
reply(new __moostjs_event_http.HttpError(statusCode, {
|
|
50
50
|
message: error.message,
|
|
@@ -140,7 +140,7 @@ _define_property$1(SelectControlDto, "__is_atscript_annotated_type", true);
|
|
|
140
140
|
_define_property$1(SelectControlDto, "type", {});
|
|
141
141
|
_define_property$1(SelectControlDto, "metadata", new Map());
|
|
142
142
|
_define_property$1(SelectControlDto, "id", "SelectControlDto");
|
|
143
|
-
(0, __atscript_typescript_utils.defineAnnotatedType)("object", QueryControlsDto).prop("$skip", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$count", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type).prop("$sort", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript_utils.defineAnnotatedType)("union").item((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SelectControlDto).$type).item((0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$search", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$with", (0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationDto).$type).optional().$type);
|
|
143
|
+
(0, __atscript_typescript_utils.defineAnnotatedType)("object", QueryControlsDto).prop("$skip", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$count", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("boolean").tags("boolean").optional().$type).prop("$sort", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript_utils.defineAnnotatedType)("union").item((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SelectControlDto).$type).item((0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$search", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$vector", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$threshold", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$with", (0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationDto).$type).optional().$type);
|
|
144
144
|
(0, __atscript_typescript_utils.defineAnnotatedType)("object", PagesControlsDto).prop("$page", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").annotate("expect.pattern", {
|
|
145
145
|
pattern: "^\\d+$",
|
|
146
146
|
flags: "u",
|
|
@@ -149,7 +149,7 @@ _define_property$1(SelectControlDto, "id", "SelectControlDto");
|
|
|
149
149
|
pattern: "^\\d+$",
|
|
150
150
|
flags: "u",
|
|
151
151
|
message: "Expected positive number"
|
|
152
|
-
}, true).optional().$type).prop("$sort", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript_utils.defineAnnotatedType)("union").item((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SelectControlDto).$type).item((0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$search", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$with", (0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationDto).$type).optional().$type);
|
|
152
|
+
}, true).optional().$type).prop("$sort", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript_utils.defineAnnotatedType)("union").item((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SelectControlDto).$type).item((0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$search", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$index", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$vector", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$threshold", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").optional().$type).prop("$with", (0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationDto).$type).optional().$type);
|
|
153
153
|
(0, __atscript_typescript_utils.defineAnnotatedType)("object", GetOneControlsDto).prop("$select", (0, __atscript_typescript_utils.defineAnnotatedType)("union").item((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SelectControlDto).$type).item((0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$with", (0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationDto).$type).optional().$type);
|
|
154
154
|
(0, __atscript_typescript_utils.defineAnnotatedType)("object", WithRelationDto).prop("name", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).prop("filter", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithFilterDto).optional().$type).prop("controls", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationControlsDto).optional().$type).prop("insights", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithFilterDto).optional().$type);
|
|
155
155
|
(0, __atscript_typescript_utils.defineAnnotatedType)("object", WithRelationControlsDto).prop("$skip", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", (0, __atscript_typescript_utils.defineAnnotatedType)().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$sort", (0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SortControlDto).optional().$type).prop("$select", (0, __atscript_typescript_utils.defineAnnotatedType)("union").item((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(SelectControlDto).$type).item((0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().designType("string").tags("string").$type).$type).optional().$type).prop("$with", (0, __atscript_typescript_utils.defineAnnotatedType)("array").of((0, __atscript_typescript_utils.defineAnnotatedType)().refTo(WithRelationDto).$type).optional().$type);
|
|
@@ -205,7 +205,10 @@ var AsDbReadableController = class {
|
|
|
205
205
|
return undefined;
|
|
206
206
|
}
|
|
207
207
|
validateInsights(insights) {
|
|
208
|
-
for (const key of insights
|
|
208
|
+
for (const [key] of insights) {
|
|
209
|
+
if (key === "*") continue;
|
|
210
|
+
if (!this.readable.flatMap.has(key)) return `Unknown field "${key}"`;
|
|
211
|
+
}
|
|
209
212
|
return undefined;
|
|
210
213
|
}
|
|
211
214
|
validateParsed(parsed, type) {
|
|
@@ -230,6 +233,13 @@ var AsDbReadableController = class {
|
|
|
230
233
|
return undefined;
|
|
231
234
|
}
|
|
232
235
|
/**
|
|
236
|
+
* Compute an embedding vector from a search term.
|
|
237
|
+
* Override in subclass to integrate with your embedding provider (OpenAI, etc.).
|
|
238
|
+
* Called when `$vector` is present in query controls.
|
|
239
|
+
*/ computeEmbedding(_search, _fieldName) {
|
|
240
|
+
throw new __moostjs_event_http.HttpError(501, "Vector search requires computeEmbedding() to be implemented");
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
233
243
|
* Transform filter before querying. Override to add tenant filtering, etc.
|
|
234
244
|
*/ transformFilter(filter) {
|
|
235
245
|
return filter;
|
|
@@ -284,9 +294,23 @@ var AsDbReadableController = class {
|
|
|
284
294
|
* **GET /query** — returns an array of records or a count.
|
|
285
295
|
*/ async query(url) {
|
|
286
296
|
const parsed = this.parseQueryString(url);
|
|
297
|
+
const controls = parsed.controls;
|
|
298
|
+
const groupBy = controls.$groupBy;
|
|
299
|
+
if (groupBy?.length) {
|
|
300
|
+
if (controls.$with?.length) return new __moostjs_event_http.HttpError(400, "Cannot combine $with and $groupBy in the same query");
|
|
301
|
+
if (parsed.insights) {
|
|
302
|
+
const insightsError = this.validateInsights(parsed.insights);
|
|
303
|
+
if (insightsError) return new __moostjs_event_http.HttpError(400, insightsError);
|
|
304
|
+
}
|
|
305
|
+
const filter$1 = this.transformFilter(parsed.filter);
|
|
306
|
+
return this.readable.aggregate({
|
|
307
|
+
filter: filter$1,
|
|
308
|
+
controls,
|
|
309
|
+
insights: parsed.insights
|
|
310
|
+
});
|
|
311
|
+
}
|
|
287
312
|
const error = this.validateParsed(parsed, "query");
|
|
288
313
|
if (error) return error;
|
|
289
|
-
const controls = parsed.controls;
|
|
290
314
|
const filter = this.transformFilter(parsed.filter);
|
|
291
315
|
const select = this.transformProjection(controls.$select);
|
|
292
316
|
if (controls.$count) return this.readable.count({
|
|
@@ -298,22 +322,24 @@ var AsDbReadableController = class {
|
|
|
298
322
|
});
|
|
299
323
|
const searchTerm = controls.$search;
|
|
300
324
|
const indexName = controls.$index;
|
|
301
|
-
|
|
325
|
+
const vectorField = controls.$vector;
|
|
326
|
+
const threshold = controls.$threshold ? Number(controls.$threshold) : undefined;
|
|
327
|
+
const queryObj = {
|
|
302
328
|
filter,
|
|
303
329
|
controls: {
|
|
304
330
|
...controls,
|
|
305
331
|
$select: select,
|
|
306
|
-
$limit: controls.$limit || 1e3
|
|
332
|
+
$limit: controls.$limit || 1e3,
|
|
333
|
+
$threshold: threshold
|
|
307
334
|
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
});
|
|
335
|
+
};
|
|
336
|
+
if (vectorField !== undefined && searchTerm) {
|
|
337
|
+
const vector = await this.computeEmbedding(searchTerm, vectorField || undefined);
|
|
338
|
+
if (vectorField) return this.readable.vectorSearch(vectorField, vector, queryObj);
|
|
339
|
+
return this.readable.vectorSearch(vector, queryObj);
|
|
340
|
+
}
|
|
341
|
+
if (searchTerm && this.readable.isSearchable()) return this.readable.search(searchTerm, queryObj, indexName);
|
|
342
|
+
return this.readable.findMany(queryObj);
|
|
317
343
|
}
|
|
318
344
|
/**
|
|
319
345
|
* **GET /pages** — returns paginated records with metadata.
|
|
@@ -329,17 +355,24 @@ var AsDbReadableController = class {
|
|
|
329
355
|
const select = this.transformProjection(controls.$select);
|
|
330
356
|
const searchTerm = controls.$search;
|
|
331
357
|
const indexName = controls.$index;
|
|
358
|
+
const vectorField = controls.$vector;
|
|
359
|
+
const threshold = controls.$threshold ? Number(controls.$threshold) : undefined;
|
|
332
360
|
const query = {
|
|
333
361
|
filter,
|
|
334
362
|
controls: {
|
|
335
363
|
...controls,
|
|
336
364
|
$select: select,
|
|
337
365
|
$skip: skip,
|
|
338
|
-
$limit: size
|
|
366
|
+
$limit: size,
|
|
367
|
+
$threshold: threshold
|
|
339
368
|
}
|
|
340
369
|
};
|
|
341
370
|
let result;
|
|
342
|
-
if (
|
|
371
|
+
if (vectorField !== undefined && searchTerm) {
|
|
372
|
+
const vector = await this.computeEmbedding(searchTerm, vectorField || undefined);
|
|
373
|
+
if (vectorField) result = await this.readable.vectorSearchWithCount(vectorField, vector, query);
|
|
374
|
+
else result = await this.readable.vectorSearchWithCount(vector, query);
|
|
375
|
+
} else if (searchTerm && this.readable.isSearchable()) result = await this.readable.searchWithCount(searchTerm, query, indexName);
|
|
343
376
|
else result = await this.readable.findManyWithCount(query);
|
|
344
377
|
return {
|
|
345
378
|
data: result.data,
|
|
@@ -382,6 +415,7 @@ else result = await this.readable.findManyWithCount(query);
|
|
|
382
415
|
*/ meta() {
|
|
383
416
|
return {
|
|
384
417
|
searchable: this.readable.isSearchable(),
|
|
418
|
+
vectorSearchable: this.readable.isVectorSearchable(),
|
|
385
419
|
searchIndexes: this._searchIndexes,
|
|
386
420
|
type: this._serializedType
|
|
387
421
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as _atscript_utils_db from '@atscript/utils-db';
|
|
3
|
-
import { AtscriptDbReadable, Uniquery, FilterExpr, UniqueryControls, AtscriptDbTable } from '@atscript/utils-db';
|
|
4
|
-
import * as _uniqu_url from '@uniqu/url';
|
|
1
|
+
import * as _atscript_typescript_utils from '@atscript/typescript/utils';
|
|
5
2
|
import { TAtscriptAnnotatedType, TAtscriptDataType, Validator } from '@atscript/typescript/utils';
|
|
3
|
+
import * as _atscript_db from '@atscript/db';
|
|
4
|
+
import { AtscriptDbReadable, Uniquery, FilterExpr, UniqueryControls, AtscriptDbTable } from '@atscript/db';
|
|
5
|
+
import * as _uniqu_url from '@uniqu/url';
|
|
6
6
|
import { HttpError } from '@moostjs/event-http';
|
|
7
7
|
import * as moost from 'moost';
|
|
8
8
|
import { TConsoleBase, Moost } from 'moost';
|
|
@@ -37,6 +37,12 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
37
37
|
protected validateControls(controls: Record<string, unknown>, type: 'query' | 'pages' | 'getOne'): string | undefined;
|
|
38
38
|
protected validateInsights(insights: Map<string, unknown>): string | undefined;
|
|
39
39
|
protected validateParsed(parsed: Uniquery, type: 'query' | 'pages' | 'getOne'): HttpError | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Compute an embedding vector from a search term.
|
|
42
|
+
* Override in subclass to integrate with your embedding provider (OpenAI, etc.).
|
|
43
|
+
* Called when `$vector` is present in query controls.
|
|
44
|
+
*/
|
|
45
|
+
protected computeEmbedding(_search: string, _fieldName?: string): Promise<number[]>;
|
|
40
46
|
/**
|
|
41
47
|
* Transform filter before querying. Override to add tenant filtering, etc.
|
|
42
48
|
*/
|
|
@@ -80,8 +86,9 @@ declare class AsDbReadableController<T extends TAtscriptAnnotatedType = TAtscrip
|
|
|
80
86
|
*/
|
|
81
87
|
meta(): {
|
|
82
88
|
searchable: boolean;
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
vectorSearchable: boolean;
|
|
90
|
+
searchIndexes: _atscript_db.TSearchIndexInfo[];
|
|
91
|
+
type: _atscript_typescript_utils.TSerializedAnnotatedType;
|
|
85
92
|
};
|
|
86
93
|
}
|
|
87
94
|
|
package/dist/index.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import { ValidatorError, defineAnnotatedType, serializeAnnotatedType, throwFeatu
|
|
|
2
2
|
import { Body, Delete, Get, HttpError, Patch, Post, Put, Query, Url } from "@moostjs/event-http";
|
|
3
3
|
import { ApplyDecorators, Controller, Inherit, Inject, Intercept, Moost, Param, Provide, TInterceptorPriority, defineInterceptor } from "moost";
|
|
4
4
|
import { parseUrl } from "@uniqu/url";
|
|
5
|
-
import { DbError } from "@atscript/
|
|
5
|
+
import { DbError } from "@atscript/db";
|
|
6
6
|
|
|
7
7
|
//#region packages/moost-db/src/decorators.ts
|
|
8
8
|
const READABLE_DEF = "__atscript_db_readable_def";
|
|
@@ -116,7 +116,7 @@ _define_property$1(SelectControlDto, "__is_atscript_annotated_type", true);
|
|
|
116
116
|
_define_property$1(SelectControlDto, "type", {});
|
|
117
117
|
_define_property$1(SelectControlDto, "metadata", new Map());
|
|
118
118
|
_define_property$1(SelectControlDto, "id", "SelectControlDto");
|
|
119
|
-
defineAnnotatedType("object", QueryControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$count", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
119
|
+
defineAnnotatedType("object", QueryControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$count", defineAnnotatedType().designType("boolean").tags("boolean").optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$vector", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$threshold", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
120
120
|
defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedType().designType("string").tags("string").annotate("expect.pattern", {
|
|
121
121
|
pattern: "^\\d+$",
|
|
122
122
|
flags: "u",
|
|
@@ -125,7 +125,7 @@ defineAnnotatedType("object", PagesControlsDto).prop("$page", defineAnnotatedTyp
|
|
|
125
125
|
pattern: "^\\d+$",
|
|
126
126
|
flags: "u",
|
|
127
127
|
message: "Expected positive number"
|
|
128
|
-
}, true).optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
128
|
+
}, true).optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$search", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$index", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$vector", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$threshold", defineAnnotatedType().designType("string").tags("string").optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
129
129
|
defineAnnotatedType("object", GetOneControlsDto).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
130
130
|
defineAnnotatedType("object", WithRelationDto).prop("name", defineAnnotatedType().designType("string").tags("string").$type).prop("filter", defineAnnotatedType().refTo(WithFilterDto).optional().$type).prop("controls", defineAnnotatedType().refTo(WithRelationControlsDto).optional().$type).prop("insights", defineAnnotatedType().refTo(WithFilterDto).optional().$type);
|
|
131
131
|
defineAnnotatedType("object", WithRelationControlsDto).prop("$skip", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$limit", defineAnnotatedType().designType("number").tags("positive", "int", "number").annotate("expect.int", true).annotate("expect.min", { minValue: 0 }).optional().$type).prop("$sort", defineAnnotatedType().refTo(SortControlDto).optional().$type).prop("$select", defineAnnotatedType("union").item(defineAnnotatedType().refTo(SelectControlDto).$type).item(defineAnnotatedType("array").of(defineAnnotatedType().designType("string").tags("string").$type).$type).optional().$type).prop("$with", defineAnnotatedType("array").of(defineAnnotatedType().refTo(WithRelationDto).$type).optional().$type);
|
|
@@ -181,7 +181,10 @@ var AsDbReadableController = class {
|
|
|
181
181
|
return undefined;
|
|
182
182
|
}
|
|
183
183
|
validateInsights(insights) {
|
|
184
|
-
for (const key of insights
|
|
184
|
+
for (const [key] of insights) {
|
|
185
|
+
if (key === "*") continue;
|
|
186
|
+
if (!this.readable.flatMap.has(key)) return `Unknown field "${key}"`;
|
|
187
|
+
}
|
|
185
188
|
return undefined;
|
|
186
189
|
}
|
|
187
190
|
validateParsed(parsed, type) {
|
|
@@ -206,6 +209,13 @@ var AsDbReadableController = class {
|
|
|
206
209
|
return undefined;
|
|
207
210
|
}
|
|
208
211
|
/**
|
|
212
|
+
* Compute an embedding vector from a search term.
|
|
213
|
+
* Override in subclass to integrate with your embedding provider (OpenAI, etc.).
|
|
214
|
+
* Called when `$vector` is present in query controls.
|
|
215
|
+
*/ computeEmbedding(_search, _fieldName) {
|
|
216
|
+
throw new HttpError(501, "Vector search requires computeEmbedding() to be implemented");
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
209
219
|
* Transform filter before querying. Override to add tenant filtering, etc.
|
|
210
220
|
*/ transformFilter(filter) {
|
|
211
221
|
return filter;
|
|
@@ -260,9 +270,23 @@ var AsDbReadableController = class {
|
|
|
260
270
|
* **GET /query** — returns an array of records or a count.
|
|
261
271
|
*/ async query(url) {
|
|
262
272
|
const parsed = this.parseQueryString(url);
|
|
273
|
+
const controls = parsed.controls;
|
|
274
|
+
const groupBy = controls.$groupBy;
|
|
275
|
+
if (groupBy?.length) {
|
|
276
|
+
if (controls.$with?.length) return new HttpError(400, "Cannot combine $with and $groupBy in the same query");
|
|
277
|
+
if (parsed.insights) {
|
|
278
|
+
const insightsError = this.validateInsights(parsed.insights);
|
|
279
|
+
if (insightsError) return new HttpError(400, insightsError);
|
|
280
|
+
}
|
|
281
|
+
const filter$1 = this.transformFilter(parsed.filter);
|
|
282
|
+
return this.readable.aggregate({
|
|
283
|
+
filter: filter$1,
|
|
284
|
+
controls,
|
|
285
|
+
insights: parsed.insights
|
|
286
|
+
});
|
|
287
|
+
}
|
|
263
288
|
const error = this.validateParsed(parsed, "query");
|
|
264
289
|
if (error) return error;
|
|
265
|
-
const controls = parsed.controls;
|
|
266
290
|
const filter = this.transformFilter(parsed.filter);
|
|
267
291
|
const select = this.transformProjection(controls.$select);
|
|
268
292
|
if (controls.$count) return this.readable.count({
|
|
@@ -274,22 +298,24 @@ var AsDbReadableController = class {
|
|
|
274
298
|
});
|
|
275
299
|
const searchTerm = controls.$search;
|
|
276
300
|
const indexName = controls.$index;
|
|
277
|
-
|
|
301
|
+
const vectorField = controls.$vector;
|
|
302
|
+
const threshold = controls.$threshold ? Number(controls.$threshold) : undefined;
|
|
303
|
+
const queryObj = {
|
|
278
304
|
filter,
|
|
279
305
|
controls: {
|
|
280
306
|
...controls,
|
|
281
307
|
$select: select,
|
|
282
|
-
$limit: controls.$limit || 1e3
|
|
308
|
+
$limit: controls.$limit || 1e3,
|
|
309
|
+
$threshold: threshold
|
|
283
310
|
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
});
|
|
311
|
+
};
|
|
312
|
+
if (vectorField !== undefined && searchTerm) {
|
|
313
|
+
const vector = await this.computeEmbedding(searchTerm, vectorField || undefined);
|
|
314
|
+
if (vectorField) return this.readable.vectorSearch(vectorField, vector, queryObj);
|
|
315
|
+
return this.readable.vectorSearch(vector, queryObj);
|
|
316
|
+
}
|
|
317
|
+
if (searchTerm && this.readable.isSearchable()) return this.readable.search(searchTerm, queryObj, indexName);
|
|
318
|
+
return this.readable.findMany(queryObj);
|
|
293
319
|
}
|
|
294
320
|
/**
|
|
295
321
|
* **GET /pages** — returns paginated records with metadata.
|
|
@@ -305,17 +331,24 @@ var AsDbReadableController = class {
|
|
|
305
331
|
const select = this.transformProjection(controls.$select);
|
|
306
332
|
const searchTerm = controls.$search;
|
|
307
333
|
const indexName = controls.$index;
|
|
334
|
+
const vectorField = controls.$vector;
|
|
335
|
+
const threshold = controls.$threshold ? Number(controls.$threshold) : undefined;
|
|
308
336
|
const query = {
|
|
309
337
|
filter,
|
|
310
338
|
controls: {
|
|
311
339
|
...controls,
|
|
312
340
|
$select: select,
|
|
313
341
|
$skip: skip,
|
|
314
|
-
$limit: size
|
|
342
|
+
$limit: size,
|
|
343
|
+
$threshold: threshold
|
|
315
344
|
}
|
|
316
345
|
};
|
|
317
346
|
let result;
|
|
318
|
-
if (
|
|
347
|
+
if (vectorField !== undefined && searchTerm) {
|
|
348
|
+
const vector = await this.computeEmbedding(searchTerm, vectorField || undefined);
|
|
349
|
+
if (vectorField) result = await this.readable.vectorSearchWithCount(vectorField, vector, query);
|
|
350
|
+
else result = await this.readable.vectorSearchWithCount(vector, query);
|
|
351
|
+
} else if (searchTerm && this.readable.isSearchable()) result = await this.readable.searchWithCount(searchTerm, query, indexName);
|
|
319
352
|
else result = await this.readable.findManyWithCount(query);
|
|
320
353
|
return {
|
|
321
354
|
data: result.data,
|
|
@@ -358,6 +391,7 @@ else result = await this.readable.findManyWithCount(query);
|
|
|
358
391
|
*/ meta() {
|
|
359
392
|
return {
|
|
360
393
|
searchable: this.readable.isSearchable(),
|
|
394
|
+
vectorSearchable: this.readable.isVectorSearchable(),
|
|
361
395
|
searchIndexes: this._searchIndexes,
|
|
362
396
|
type: this._serializedType
|
|
363
397
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/moost-db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.38",
|
|
4
4
|
"description": "Generic database controller for Moost with Atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -35,20 +35,20 @@
|
|
|
35
35
|
"./package.json": "./package.json"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@uniqu/url": "^0.
|
|
38
|
+
"@uniqu/url": "^0.1.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@moostjs/event-http": "^0.6.2",
|
|
42
42
|
"moost": "^0.6.2",
|
|
43
43
|
"vitest": "3.2.4",
|
|
44
|
-
"@atscript/core": "^0.1.
|
|
44
|
+
"@atscript/core": "^0.1.38"
|
|
45
45
|
},
|
|
46
46
|
"peerDependencies": {
|
|
47
47
|
"@moostjs/event-http": "^0.6.2",
|
|
48
48
|
"moost": "^0.6.2",
|
|
49
|
-
"@uniqu/core": "^0.
|
|
50
|
-
"@atscript/
|
|
51
|
-
"@atscript/typescript": "^0.1.
|
|
49
|
+
"@uniqu/core": "^0.1.2",
|
|
50
|
+
"@atscript/db": "^0.1.38",
|
|
51
|
+
"@atscript/typescript": "^0.1.38"
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
54
|
"pub": "pnpm publish --access public",
|