@an5/orm 1.3.2 → 1.3.4
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 +74 -11
- package/dist/cleanup.d.ts +1 -0
- package/dist/cleanup.js +177 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +147 -0
- package/dist/generator/src/attributes.d.ts +57 -0
- package/dist/generator/src/attributes.js +225 -0
- package/dist/generator/src/code-generator.js +49 -16
- package/dist/generator/src/config.d.ts +2 -2
- package/dist/generator/src/config.js +4 -3
- package/dist/generator/src/dialect.d.ts +52 -1
- package/dist/generator/src/dialect.js +136 -3
- package/dist/generator/src/dotnet-generator.js +67 -23
- package/dist/generator/src/golang-generator.d.ts +8 -0
- package/dist/generator/src/golang-generator.js +86 -21
- package/dist/generator/src/java-generator.d.ts +8 -0
- package/dist/generator/src/java-generator.js +108 -3
- package/dist/generator/src/kotlin-generator.js +51 -0
- package/dist/generator/src/parser.d.ts +1 -0
- package/dist/generator/src/parser.js +90 -35
- package/dist/generator/src/pull-schema.d.ts +54 -0
- package/dist/generator/src/pull-schema.js +116 -0
- package/dist/generator/src/push-apply.d.ts +4 -2
- package/dist/generator/src/push-apply.js +53 -4
- package/dist/generator/src/push-schema.d.ts +21 -9
- package/dist/generator/src/push-schema.js +132 -32
- package/dist/generator/src/python-generator.js +35 -22
- package/dist/generator/src/rust-generator.js +79 -74
- package/dist/generator/src/swift-generator.js +54 -3
- package/dist/generator/src/types.d.ts +10 -0
- package/dist/generator/src/view-order.d.ts +33 -0
- package/dist/generator/src/view-order.js +123 -0
- package/dist/migrate.d.ts +12 -0
- package/dist/migrate.js +442 -0
- package/dist/migration-core.d.ts +32 -3
- package/dist/migration-core.js +213 -43
- package/dist/provider-support.d.ts +0 -1
- package/dist/provider-support.js +0 -1
- package/dist/pull.d.ts +1 -0
- package/dist/pull.js +425 -0
- package/dist/push.d.ts +1 -0
- package/dist/push.js +93 -0
- package/dist/seed.d.ts +1 -0
- package/dist/seed.js +33 -0
- package/generator/src/attributes.ts +231 -0
- package/generator/src/code-generator.ts +47 -16
- package/generator/src/config.ts +5 -3
- package/generator/src/dialect.ts +188 -5
- package/generator/src/dotnet-generator.ts +68 -24
- package/generator/src/golang-generator.ts +89 -22
- package/generator/src/java-generator.ts +109 -3
- package/generator/src/kotlin-generator.ts +51 -0
- package/generator/src/parser.ts +102 -30
- package/generator/src/pull-schema.ts +127 -0
- package/generator/src/push-apply.ts +62 -5
- package/generator/src/push-schema.ts +160 -32
- package/generator/src/python-generator.ts +33 -20
- package/generator/src/rust-generator.ts +79 -73
- package/generator/src/swift-generator.ts +55 -4
- package/generator/src/types.ts +10 -0
- package/generator/src/view-order.ts +133 -0
- package/package.json +19 -14
|
@@ -133,26 +133,27 @@ namespace An5Orm
|
|
|
133
133
|
content += ` public string ${this.capitalize(f.name)} { get; set; } // "asc" or "desc"\n`;
|
|
134
134
|
}
|
|
135
135
|
content += ` }\n\n`;
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
// Create / update / delete inputs describe writes; a view has none.
|
|
137
|
+
if (!model.isView) {
|
|
138
|
+
content += ` /// <summary>Typed data for creating a new ${name} record.</summary>\n`;
|
|
139
|
+
content += ` public class ${name}CreateInput\n {\n`;
|
|
140
|
+
for (const f of model.fields) {
|
|
141
|
+
if (!f.isId) {
|
|
142
|
+
const ct = this.mapType(f, f.isOptional || f.hasDefault);
|
|
143
|
+
content += ` public ${ct} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
144
|
+
}
|
|
143
145
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
content += ` public ${ut} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
146
|
+
content += ` }\n\n`;
|
|
147
|
+
content += ` /// <summary>Typed data for updating an existing ${name} record.</summary>\n`;
|
|
148
|
+
content += ` public class ${name}UpdateInput\n {\n`;
|
|
149
|
+
for (const f of model.fields) {
|
|
150
|
+
if (!f.isId) {
|
|
151
|
+
const ut = this.mapType(f, true); // always nullable for update
|
|
152
|
+
content += ` public ${ut} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
153
|
+
}
|
|
153
154
|
}
|
|
155
|
+
content += ` }\n\n`;
|
|
154
156
|
}
|
|
155
|
-
content += ` }\n\n`;
|
|
156
157
|
// FindManyArgs
|
|
157
158
|
content += ` /// <summary>ORM-style args for ${name}.FindMany().</summary>\n`;
|
|
158
159
|
content += ` public class ${name}FindManyArgs\n {\n`;
|
|
@@ -175,10 +176,12 @@ namespace An5Orm
|
|
|
175
176
|
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
176
177
|
content += ` }\n\n`;
|
|
177
178
|
// DeleteManyArgs
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
if (!model.isView) {
|
|
180
|
+
content += ` /// <summary>ORM-style args for ${name}.DeleteMany().</summary>\n`;
|
|
181
|
+
content += ` public class ${name}DeleteManyArgs\n {\n`;
|
|
182
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
183
|
+
content += ` }\n\n`;
|
|
184
|
+
}
|
|
182
185
|
// CountArgs
|
|
183
186
|
content += ` /// <summary>ORM-style args for ${name}.Count().</summary>\n`;
|
|
184
187
|
content += ` public class ${name}CountArgs\n {\n`;
|
|
@@ -477,8 +480,9 @@ namespace An5Orm
|
|
|
477
480
|
`;
|
|
478
481
|
for (const model of models) {
|
|
479
482
|
const name = this.capitalize(model.name);
|
|
480
|
-
|
|
481
|
-
content += ` public
|
|
483
|
+
const client = model.isView ? 'ViewClient' : 'TableClient';
|
|
484
|
+
content += ` public ${client}<${model.name}> ${name}s => new ${client}<${model.name}>(ConnectionString, "${(0, types_1.dottedTableName)(model)}");\n`;
|
|
485
|
+
content += ` public ${client}<${model.name}> ${name} => ${name}s;\n`;
|
|
482
486
|
}
|
|
483
487
|
content += ` }
|
|
484
488
|
|
|
@@ -1172,6 +1176,46 @@ namespace An5Orm
|
|
|
1172
1176
|
return false;
|
|
1173
1177
|
}
|
|
1174
1178
|
}
|
|
1179
|
+
|
|
1180
|
+
/// <summary>
|
|
1181
|
+
/// Read-only client for a database view.
|
|
1182
|
+
///
|
|
1183
|
+
/// It exposes the same read methods as <see cref="TableClient{T}"/> and no
|
|
1184
|
+
/// write methods, so attempting to write a view fails at compile time rather
|
|
1185
|
+
/// than in the database. The view's rows still map onto the generated entity
|
|
1186
|
+
/// class, which is why the queries delegate instead of being duplicated.
|
|
1187
|
+
/// </summary>
|
|
1188
|
+
public class ViewClient<T> where T : new()
|
|
1189
|
+
{
|
|
1190
|
+
private readonly TableClient<T> _table;
|
|
1191
|
+
|
|
1192
|
+
public ViewClient(string connectionString, string viewName)
|
|
1193
|
+
{
|
|
1194
|
+
_table = new TableClient<T>(connectionString, viewName);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
public string ConnectionString => _table.ConnectionString;
|
|
1198
|
+
public string ViewName => _table.TableName;
|
|
1199
|
+
public An5Dialect Dialect => _table.Dialect;
|
|
1200
|
+
|
|
1201
|
+
public List<T> QueryRaw(string query, Dictionary<string, object> parameters = null)
|
|
1202
|
+
=> _table.QueryRaw(query, parameters);
|
|
1203
|
+
|
|
1204
|
+
public List<T> FindMany(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
1205
|
+
=> _table.FindMany(whereClause, parameters);
|
|
1206
|
+
|
|
1207
|
+
public T FindFirst(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
1208
|
+
=> _table.FindFirst(whereClause, parameters);
|
|
1209
|
+
|
|
1210
|
+
public T FindUnique(object id)
|
|
1211
|
+
=> _table.FindUnique(id);
|
|
1212
|
+
|
|
1213
|
+
public int Count(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
1214
|
+
=> _table.Count(whereClause, parameters);
|
|
1215
|
+
|
|
1216
|
+
public List<T> VectorSearch(List<double> vector, int take = 10, string whereClause = null, Dictionary<string, object> parameters = null, string vectorField = "Embedding", string distanceMetric = "cosine")
|
|
1217
|
+
=> _table.VectorSearch(vector, take, whereClause, parameters, vectorField, distanceMetric);
|
|
1218
|
+
}
|
|
1175
1219
|
}
|
|
1176
1220
|
`;
|
|
1177
1221
|
fs_1.default.writeFileSync(path_1.default.join(this.outputDir, 'An5DbContext.cs'), content);
|
|
@@ -22,6 +22,14 @@ export declare class GolangGenerator {
|
|
|
22
22
|
private generateBaseOrmTypes;
|
|
23
23
|
private generateDbContext;
|
|
24
24
|
private generateTableClientStruct;
|
|
25
|
+
/**
|
|
26
|
+
* The read-only client used for views.
|
|
27
|
+
*
|
|
28
|
+
* Go has no way to hide methods from an embedded value, so wrapping rather
|
|
29
|
+
* than embedding is what makes a write to a view a compile error. The read
|
|
30
|
+
* methods delegate, which keeps a view's rows scanning exactly like a table's.
|
|
31
|
+
*/
|
|
32
|
+
private generateViewClientStruct;
|
|
25
33
|
private generateQueryAndScan;
|
|
26
34
|
/**
|
|
27
35
|
* The vector codec the generated client needs on both sides of a query.
|
|
@@ -182,6 +182,7 @@ func GetDefaultConnectionString() string {
|
|
|
182
182
|
content += this.generateBaseOrmTypes();
|
|
183
183
|
content += this.generateDbContext(models);
|
|
184
184
|
content += this.generateTableClientStruct();
|
|
185
|
+
content += this.generateViewClientStruct();
|
|
185
186
|
content += this.generateQueryAndScan();
|
|
186
187
|
content += this.generateWhereBuilder();
|
|
187
188
|
content += this.generateCrudMethods();
|
|
@@ -311,8 +312,9 @@ func SortOrderPtr(s SortOrder) *SortOrder { return &s }
|
|
|
311
312
|
let content = `// An5DbContext represents the main ORM database context.\ntype An5DbContext struct {\n\tDB *sql.DB\n\tDialect Dialect\n`;
|
|
312
313
|
for (const model of models) {
|
|
313
314
|
const name = this.capitalize(model.name);
|
|
314
|
-
|
|
315
|
-
content += `\t${name}
|
|
315
|
+
const client = model.isView ? 'ViewClient' : 'TableClient';
|
|
316
|
+
content += `\t${name}s *${client}[${model.name}]\n`;
|
|
317
|
+
content += `\t${name} *${client}[${model.name}]\n`;
|
|
316
318
|
}
|
|
317
319
|
content += `}
|
|
318
320
|
|
|
@@ -328,7 +330,8 @@ func NewAn5DbContextWithConnStr(db *sql.DB, connStr string) *An5DbContext {
|
|
|
328
330
|
`;
|
|
329
331
|
for (const model of models) {
|
|
330
332
|
const name = this.capitalize(model.name);
|
|
331
|
-
|
|
333
|
+
const client = model.isView ? 'NewViewClient' : 'NewTableClient';
|
|
334
|
+
content += `\tclient${name} := ${client}[${model.name}](db, "${(0, types_1.dottedTableName)(model)}", d)\n`;
|
|
332
335
|
content += `\tctx.${name}s = client${name}\n`;
|
|
333
336
|
content += `\tctx.${name} = client${name}\n`;
|
|
334
337
|
}
|
|
@@ -380,6 +383,65 @@ func (c *TableClient[T]) ph(n int) string {
|
|
|
380
383
|
\treturn "?"
|
|
381
384
|
}
|
|
382
385
|
|
|
386
|
+
`;
|
|
387
|
+
}
|
|
388
|
+
// ─── ViewClient ────────────────────────────────────────────────────────────
|
|
389
|
+
/**
|
|
390
|
+
* The read-only client used for views.
|
|
391
|
+
*
|
|
392
|
+
* Go has no way to hide methods from an embedded value, so wrapping rather
|
|
393
|
+
* than embedding is what makes a write to a view a compile error. The read
|
|
394
|
+
* methods delegate, which keeps a view's rows scanning exactly like a table's.
|
|
395
|
+
*/
|
|
396
|
+
generateViewClientStruct() {
|
|
397
|
+
return `// ViewClient provides read-only access to a database view.
|
|
398
|
+
type ViewClient[T any] struct {
|
|
399
|
+
\tDB *sql.DB
|
|
400
|
+
\tDialect Dialect
|
|
401
|
+
\tViewName string
|
|
402
|
+
\ttable *TableClient[T]
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// NewViewClient creates a typed read-only client for a view.
|
|
406
|
+
func NewViewClient[T any](db *sql.DB, viewName string, dialect Dialect) *ViewClient[T] {
|
|
407
|
+
\treturn &ViewClient[T]{
|
|
408
|
+
\t\tDB: db,
|
|
409
|
+
\t\tDialect: dialect,
|
|
410
|
+
\t\tViewName: viewName,
|
|
411
|
+
\t\ttable: NewTableClient[T](db, viewName, dialect),
|
|
412
|
+
\t}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// FindMany queries the view with ORM-style args.
|
|
416
|
+
func (c *ViewClient[T]) FindMany(ctx context.Context, args interface{}) ([]T, error) {
|
|
417
|
+
\treturn c.table.FindMany(ctx, args)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// FindFirst returns the first matching row of the view.
|
|
421
|
+
func (c *ViewClient[T]) FindFirst(ctx context.Context, args interface{}) (*T, error) {
|
|
422
|
+
\treturn c.table.FindFirst(ctx, args)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// FindUnique returns one row of the view by primary key.
|
|
426
|
+
func (c *ViewClient[T]) FindUnique(ctx context.Context, args interface{}) (*T, error) {
|
|
427
|
+
\treturn c.table.FindUnique(ctx, args)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Count returns how many rows of the view match.
|
|
431
|
+
func (c *ViewClient[T]) Count(ctx context.Context, args interface{}) (int64, error) {
|
|
432
|
+
\treturn c.table.Count(ctx, args)
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// QueryRaw runs a raw SELECT against the view.
|
|
436
|
+
func (c *ViewClient[T]) QueryRaw(ctx context.Context, query string, args ...interface{}) ([]T, error) {
|
|
437
|
+
\treturn c.table.QueryRaw(ctx, query, args...)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// VectorSearch searches a vector column of the view.
|
|
441
|
+
func (c *ViewClient[T]) VectorSearch(ctx context.Context, vector []float64, opts ...VectorSearchOptions) ([]T, error) {
|
|
442
|
+
\treturn c.table.VectorSearch(ctx, vector, opts...)
|
|
443
|
+
}
|
|
444
|
+
|
|
383
445
|
`;
|
|
384
446
|
}
|
|
385
447
|
// ─── queryAndScan ──────────────────────────────────────────────────────────
|
|
@@ -1309,24 +1371,25 @@ var _ = time.Time{}
|
|
|
1309
1371
|
content += `\t${this.capitalize(field.name)} *SortOrder\n`;
|
|
1310
1372
|
}
|
|
1311
1373
|
content += `}\n\n`;
|
|
1312
|
-
// CreateInput
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1374
|
+
// CreateInput / UpdateInput / DeleteManyArgs describe writes; a view has none.
|
|
1375
|
+
if (!model.isView) {
|
|
1376
|
+
content += `// ${name}CreateInput holds data for creating a new ${name} record.\ntype ${name}CreateInput struct {\n`;
|
|
1377
|
+
for (const field of model.fields) {
|
|
1378
|
+
if (!field.isId) {
|
|
1379
|
+
const goType = this.mapGoType(field, field.isOptional || field.hasDefault);
|
|
1380
|
+
content += `\t${this.capitalize(field.name)} ${goType}\n`;
|
|
1381
|
+
}
|
|
1318
1382
|
}
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
content += `\t${this.capitalize(field.name)} ${goType}\n`;
|
|
1383
|
+
content += `}\n\n`;
|
|
1384
|
+
content += `// ${name}UpdateInput holds data for updating an existing ${name} record.\ntype ${name}UpdateInput struct {\n`;
|
|
1385
|
+
for (const field of model.fields) {
|
|
1386
|
+
if (!field.isId) {
|
|
1387
|
+
const goType = this.mapGoType(field, true); // always pointer so we can detect zero
|
|
1388
|
+
content += `\t${this.capitalize(field.name)} ${goType}\n`;
|
|
1389
|
+
}
|
|
1327
1390
|
}
|
|
1391
|
+
content += `}\n\n`;
|
|
1328
1392
|
}
|
|
1329
|
-
content += `}\n\n`;
|
|
1330
1393
|
// FindManyArgs
|
|
1331
1394
|
content += `// ${name}FindManyArgs provides ORM-style args for FindMany queries on ${name}.\ntype ${name}FindManyArgs struct {\n`;
|
|
1332
1395
|
content += `\tWhere *${name}WhereInput\n`;
|
|
@@ -1346,9 +1409,11 @@ var _ = time.Time{}
|
|
|
1346
1409
|
content += `\tWhere *${name}WhereInput\n`;
|
|
1347
1410
|
content += `}\n\n`;
|
|
1348
1411
|
// DeleteManyArgs
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1412
|
+
if (!model.isView) {
|
|
1413
|
+
content += `// ${name}DeleteManyArgs provides ORM-style args for DeleteMany queries on ${name}.\ntype ${name}DeleteManyArgs struct {\n`;
|
|
1414
|
+
content += `\tWhere *${name}WhereInput\n`;
|
|
1415
|
+
content += `}\n\n`;
|
|
1416
|
+
}
|
|
1352
1417
|
// CountArgs
|
|
1353
1418
|
content += `// ${name}CountArgs provides ORM-style args for Count queries on ${name}.\ntype ${name}CountArgs struct {\n`;
|
|
1354
1419
|
content += `\tWhere *${name}WhereInput\n`;
|
|
@@ -51,6 +51,14 @@ export declare class JavaGenerator {
|
|
|
51
51
|
private generateMetadata;
|
|
52
52
|
private generateConfig;
|
|
53
53
|
private modelClientSource;
|
|
54
|
+
/**
|
|
55
|
+
* The typed client used for views.
|
|
56
|
+
*
|
|
57
|
+
* <p>It repeats {@link ModelClient}'s read signatures and stops there: with no
|
|
58
|
+
* {@code create}/{@code update}/{@code delete} to call, writing a view is a
|
|
59
|
+
* compile error rather than a runtime refusal.
|
|
60
|
+
*/
|
|
61
|
+
private viewClientSource;
|
|
54
62
|
private generateDbContext;
|
|
55
63
|
private tableName;
|
|
56
64
|
}
|
|
@@ -1224,10 +1224,105 @@ public final class ModelClient<T> {
|
|
|
1224
1224
|
return value instanceof Number ? ((Number) value).intValue() : 0;
|
|
1225
1225
|
}
|
|
1226
1226
|
}
|
|
1227
|
+
`;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* The typed client used for views.
|
|
1231
|
+
*
|
|
1232
|
+
* <p>It repeats {@link ModelClient}'s read signatures and stops there: with no
|
|
1233
|
+
* {@code create}/{@code update}/{@code delete} to call, writing a view is a
|
|
1234
|
+
* compile error rather than a runtime refusal.
|
|
1235
|
+
*/
|
|
1236
|
+
viewClientSource() {
|
|
1237
|
+
return `// This file is auto-generated. Do not edit directly.
|
|
1238
|
+
package an5.client;
|
|
1239
|
+
|
|
1240
|
+
import java.sql.SQLException;
|
|
1241
|
+
import java.util.ArrayList;
|
|
1242
|
+
import java.util.List;
|
|
1243
|
+
import java.util.Map;
|
|
1244
|
+
import java.util.function.Function;
|
|
1245
|
+
|
|
1246
|
+
import an5.adapters.An5Aggregate;
|
|
1247
|
+
import an5.adapters.An5GroupBy;
|
|
1248
|
+
import an5.adapters.An5Query;
|
|
1249
|
+
import an5.adapters.An5ViewClient;
|
|
1250
|
+
|
|
1251
|
+
/** A read-only, typed client for one database view. */
|
|
1252
|
+
public final class ViewClient<T> {
|
|
1253
|
+
|
|
1254
|
+
private final An5ViewClient view;
|
|
1255
|
+
private final Function<Map<String, Object>, T> fromRow;
|
|
1256
|
+
|
|
1257
|
+
ViewClient(An5ViewClient view, Function<Map<String, Object>, T> fromRow) {
|
|
1258
|
+
this.view = view;
|
|
1259
|
+
this.fromRow = fromRow;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/** The view name this client reads. */
|
|
1263
|
+
public String view() {
|
|
1264
|
+
return view.viewName();
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
public List<T> findMany() throws SQLException {
|
|
1268
|
+
return findMany(new An5Query());
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
public List<T> findMany(An5Query query) throws SQLException {
|
|
1272
|
+
return map(view.findMany(query));
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
public T findFirst(An5Query query) throws SQLException {
|
|
1276
|
+
return one(view.findFirst(query));
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
public T findFirst(Map<String, Object> where) throws SQLException {
|
|
1280
|
+
return one(view.findFirst(where));
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
public T findUnique(Map<String, Object> where) throws SQLException {
|
|
1284
|
+
return one(view.findUnique(where));
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
public long count(Map<String, Object> where) throws SQLException {
|
|
1288
|
+
return view.count(where);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
public Map<String, Object> aggregate(An5Aggregate aggregate) throws SQLException {
|
|
1292
|
+
return view.aggregate(aggregate);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
public List<Map<String, Object>> groupBy(An5GroupBy group) throws SQLException {
|
|
1296
|
+
return view.groupBy(group);
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
public List<T> vectorSearch(
|
|
1300
|
+
double[] vector, Integer take, Map<String, Object> where, String vectorField, String metric)
|
|
1301
|
+
throws SQLException {
|
|
1302
|
+
return map(view.vectorSearch(vector, take, where, vectorField, metric));
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
public List<T> vectorSearch(double[] vector, Integer take) throws SQLException {
|
|
1306
|
+
return vectorSearch(vector, take, null, "embedding", "cosine");
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
private List<T> map(List<Map<String, Object>> rows) {
|
|
1310
|
+
List<T> mapped = new ArrayList<T>();
|
|
1311
|
+
for (Map<String, Object> row : rows) {
|
|
1312
|
+
mapped.add(fromRow.apply(row));
|
|
1313
|
+
}
|
|
1314
|
+
return mapped;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
private T one(Map<String, Object> row) {
|
|
1318
|
+
return row == null ? null : fromRow.apply(row);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1227
1321
|
`;
|
|
1228
1322
|
}
|
|
1229
1323
|
generateDbContext(models) {
|
|
1230
1324
|
fs_1.default.writeFileSync(path_1.default.join(this.outputDir, 'ModelClient.java'), this.modelClientSource());
|
|
1325
|
+
fs_1.default.writeFileSync(path_1.default.join(this.outputDir, 'ViewClient.java'), this.viewClientSource());
|
|
1231
1326
|
let content = `// This file is auto-generated. Do not edit directly.
|
|
1232
1327
|
package an5.client;
|
|
1233
1328
|
|
|
@@ -1259,7 +1354,7 @@ public class An5DbContext implements AutoCloseable {
|
|
|
1259
1354
|
if (declared.has(property))
|
|
1260
1355
|
continue;
|
|
1261
1356
|
declared.add(property);
|
|
1262
|
-
content += ` private final ModelClient<${model.name}> ${property}Client;\n`;
|
|
1357
|
+
content += ` private final ${model.isView ? 'ViewClient' : 'ModelClient'}<${model.name}> ${property}Client;\n`;
|
|
1263
1358
|
}
|
|
1264
1359
|
content += `
|
|
1265
1360
|
/** Opens a context, registering this schema with the adapter. */
|
|
@@ -1273,6 +1368,11 @@ public class An5DbContext implements AutoCloseable {
|
|
|
1273
1368
|
if (assigned.has(property))
|
|
1274
1369
|
continue;
|
|
1275
1370
|
assigned.add(property);
|
|
1371
|
+
if (model.isView) {
|
|
1372
|
+
content += ` this.${property}Client = new ViewClient<${model.name}>(\n`;
|
|
1373
|
+
content += ` adapter.view("${model.name}"), ${model.name}::fromRow);\n`;
|
|
1374
|
+
continue;
|
|
1375
|
+
}
|
|
1276
1376
|
content += ` this.${property}Client = new ModelClient<${model.name}>(\n`;
|
|
1277
1377
|
content += ` adapter.table("${model.name}"), ${model.name}::fromRow, ${model.name}::toValues);\n`;
|
|
1278
1378
|
}
|
|
@@ -1289,6 +1389,11 @@ public class An5DbContext implements AutoCloseable {
|
|
|
1289
1389
|
if (wrapped.has(property))
|
|
1290
1390
|
continue;
|
|
1291
1391
|
wrapped.add(property);
|
|
1392
|
+
if (model.isView) {
|
|
1393
|
+
content += ` this.${property}Client = new ViewClient<${model.name}>(\n`;
|
|
1394
|
+
content += ` adapter.view("${model.name}"), ${model.name}::fromRow);\n`;
|
|
1395
|
+
continue;
|
|
1396
|
+
}
|
|
1292
1397
|
content += ` this.${property}Client = new ModelClient<${model.name}>(\n`;
|
|
1293
1398
|
content += ` adapter.table("${model.name}"), ${model.name}::fromRow, ${model.name}::toValues);\n`;
|
|
1294
1399
|
}
|
|
@@ -1312,8 +1417,8 @@ public class An5DbContext implements AutoCloseable {
|
|
|
1312
1417
|
exposed.add(property);
|
|
1313
1418
|
const accessor = `get${this.capitalize(property)}`;
|
|
1314
1419
|
content += `
|
|
1315
|
-
/** Queries for \`${model.name}\`. */
|
|
1316
|
-
public ModelClient<${model.name}> ${accessor}() {
|
|
1420
|
+
/** ${model.isView ? 'Reads' : 'Queries'} for \`${model.name}\`. */
|
|
1421
|
+
public ${model.isView ? 'ViewClient' : 'ModelClient'}<${model.name}> ${accessor}() {
|
|
1317
1422
|
return this.${property}Client;
|
|
1318
1423
|
}
|
|
1319
1424
|
`;
|
|
@@ -663,6 +663,47 @@ class ModelClient<T : Any>(
|
|
|
663
663
|
): List<T> = delegate.vectorSearch(vector, take, filter, vectorField, metric).map(read)
|
|
664
664
|
}
|
|
665
665
|
|
|
666
|
+
/**
|
|
667
|
+
* A read-only, typed client for one view.
|
|
668
|
+
*
|
|
669
|
+
* It mirrors [ModelClient]'s read methods and stops there, so a write to a view does
|
|
670
|
+
* not compile. The runtime's read-only client does the actual work.
|
|
671
|
+
*/
|
|
672
|
+
class ViewClient<T : Any>(
|
|
673
|
+
private val delegate: an5.adapters.ViewClient,
|
|
674
|
+
private val read: (Row) -> T,
|
|
675
|
+
) {
|
|
676
|
+
|
|
677
|
+
/** The view this client reads. */
|
|
678
|
+
val name: String get() = delegate.name
|
|
679
|
+
|
|
680
|
+
fun findMany(): List<T> = delegate.findMany().map(read)
|
|
681
|
+
|
|
682
|
+
fun findMany(block: QueryBuilder.() -> Unit): List<T> = delegate.findMany(block).map(read)
|
|
683
|
+
|
|
684
|
+
fun findFirst(filter: Where): T? = delegate.findFirst(filter)?.let(read)
|
|
685
|
+
|
|
686
|
+
fun findUnique(filter: Where): T? = delegate.findUnique(filter)?.let(read)
|
|
687
|
+
|
|
688
|
+
fun count(filter: Where? = null): Long = delegate.count(filter)
|
|
689
|
+
|
|
690
|
+
/** One row of aggregate values, keyed \`_count\`, \`_sum_<field>\` and so on. */
|
|
691
|
+
fun aggregate(block: AggregateBuilder.() -> Unit): Row = delegate.aggregate(block)
|
|
692
|
+
|
|
693
|
+
/** One row per group, each carrying the group's count and aggregates. */
|
|
694
|
+
fun groupBy(vararg by: String, block: AggregateBuilder.() -> Unit = {}): List<Row> =
|
|
695
|
+
delegate.groupBy(*by, block = block)
|
|
696
|
+
|
|
697
|
+
/** The rows nearest [vector]. */
|
|
698
|
+
fun vectorSearch(
|
|
699
|
+
vector: DoubleArray,
|
|
700
|
+
take: Int = 10,
|
|
701
|
+
filter: Where? = null,
|
|
702
|
+
vectorField: String = "embedding",
|
|
703
|
+
metric: DistanceMetric = DistanceMetric.COSINE,
|
|
704
|
+
): List<T> = delegate.vectorSearch(vector, take, filter, vectorField, metric).map(read)
|
|
705
|
+
}
|
|
706
|
+
|
|
666
707
|
/**
|
|
667
708
|
* The AN5 entry point: one typed client per model, over one connection.
|
|
668
709
|
*
|
|
@@ -687,6 +728,16 @@ class An5Db internal constructor(private val an5: An5) : AutoCloseable {
|
|
|
687
728
|
if (seen.has(property))
|
|
688
729
|
continue;
|
|
689
730
|
seen.add(property);
|
|
731
|
+
if (model.isView) {
|
|
732
|
+
content += ` /** Reads for \`${model.name}\`. */
|
|
733
|
+
`;
|
|
734
|
+
content += ` val ${property}: ViewClient<${model.name}> =
|
|
735
|
+
`;
|
|
736
|
+
content += ` ViewClient(an5.view("${model.name}"), ${model.name}::fromRow)
|
|
737
|
+
|
|
738
|
+
`;
|
|
739
|
+
continue;
|
|
740
|
+
}
|
|
690
741
|
content += ` /** Queries for \`${model.name}\`. */
|
|
691
742
|
`;
|
|
692
743
|
content += ` val ${property}: ModelClient<${model.name}> =
|