@an5/orm 1.0.7 → 1.0.9
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 +97 -83
- package/dist/an5Metadata.d.ts +10 -0
- package/dist/an5Metadata.js +93 -0
- package/dist/an5Orm.d.ts +95 -3
- package/dist/an5Orm.js +808 -512
- package/dist/config.d.ts +2 -0
- package/dist/config.js +24 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +16 -0
- package/dist/generator/src/code-generator.d.ts +1 -0
- package/dist/generator/src/code-generator.js +44 -20
- package/dist/generator/src/dotnet-generator.d.ts +2 -0
- package/dist/generator/src/dotnet-generator.js +332 -6
- package/dist/generator/src/index.js +18 -2
- package/dist/generator/src/metadata-generator.d.ts +4 -1
- package/dist/generator/src/metadata-generator.js +22 -8
- package/dist/generator/src/python-generator.d.ts +10 -0
- package/dist/generator/src/python-generator.js +242 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +23 -8
- package/dist/logger.d.ts +53 -0
- package/dist/logger.js +117 -0
- package/dist/metadata.d.ts +24 -0
- package/dist/metadata.js +15 -0
- package/dist/migration-core.d.ts +87 -0
- package/dist/migration-core.js +692 -0
- package/dist/sql-utils.d.ts +31 -0
- package/dist/sql-utils.js +290 -0
- package/generator/src/code-generator.ts +45 -20
- package/generator/src/dotnet-generator.ts +344 -6
- package/generator/src/golang-generator.ts +1242 -0
- package/generator/src/index.ts +8 -1
- package/generator/src/metadata-generator.ts +29 -12
- package/generator/src/python-generator.ts +314 -4
- package/package.json +12 -10
- package/dist/cleanup.d.ts +0 -2
- package/dist/cleanup.js +0 -120
- package/dist/cli.js +0 -64
- package/dist/migrate.d.ts +0 -11
- package/dist/migrate.js +0 -301
- package/dist/pull.d.ts +0 -2
- package/dist/pull.js +0 -339
- package/dist/push.d.ts +0 -2
- package/dist/push.js +0 -281
- package/dist/seed.d.ts +0 -2
- package/dist/seed.js +0 -401
|
@@ -15,13 +15,187 @@ export class DotnetGenerator {
|
|
|
15
15
|
this.generateEntityClass(model);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
// 2. Generate
|
|
18
|
+
// 2. Generate per-model ORM types (WhereInput, OrderByInput, FindManyArgs, etc.)
|
|
19
|
+
this.generateOrmTypes(models);
|
|
20
|
+
|
|
21
|
+
// 3. Generate DbContext / Client metadata file
|
|
19
22
|
this.generateDbContext(models);
|
|
20
23
|
|
|
21
|
-
//
|
|
24
|
+
// 4. Generate configuration helper class
|
|
22
25
|
this.generateConfigClass();
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
private getCsFilterType(fieldType: string, isOptional: boolean): string {
|
|
29
|
+
const lower = fieldType.toLowerCase();
|
|
30
|
+
if (['datetime', 'datetime2', 'smalldatetime', 'date', 'datetimeoffset'].includes(lower)) return 'DateTimeFilter';
|
|
31
|
+
if (['bool', 'boolean', 'bit'].includes(lower)) return 'BoolFilter';
|
|
32
|
+
if (['int', 'integer', 'smallint', 'tinyint', 'number'].includes(lower)) return 'IntFilter';
|
|
33
|
+
if (['bigint', 'long', 'float', 'real', 'double', 'decimal', 'numeric', 'money', 'smallmoney'].includes(lower)) return 'NumberFilter';
|
|
34
|
+
return 'StringFilter';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
private generateOrmTypes(models: Model[]) {
|
|
38
|
+
let content = `// This file is auto-generated. Do not edit directly.
|
|
39
|
+
using System;
|
|
40
|
+
using System.Collections.Generic;
|
|
41
|
+
|
|
42
|
+
namespace An5Orm
|
|
43
|
+
{
|
|
44
|
+
// ── Base Filter Types ──────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
/// <summary>Type-safe filter for string fields.</summary>
|
|
47
|
+
public class StringFilter
|
|
48
|
+
{
|
|
49
|
+
public new string Equals { get; set; }
|
|
50
|
+
public string Not { get; set; }
|
|
51
|
+
public List<string> In { get; set; }
|
|
52
|
+
public List<string> NotIn { get; set; }
|
|
53
|
+
public string Contains { get; set; }
|
|
54
|
+
public string StartsWith { get; set; }
|
|
55
|
+
public string EndsWith { get; set; }
|
|
56
|
+
public string Gt { get; set; }
|
|
57
|
+
public string Gte { get; set; }
|
|
58
|
+
public string Lt { get; set; }
|
|
59
|
+
public string Lte { get; set; }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// <summary>Type-safe filter for integer fields.</summary>
|
|
63
|
+
public class IntFilter
|
|
64
|
+
{
|
|
65
|
+
public new int? Equals { get; set; }
|
|
66
|
+
public int? Not { get; set; }
|
|
67
|
+
public List<int> In { get; set; }
|
|
68
|
+
public List<int> NotIn { get; set; }
|
|
69
|
+
public int? Gt { get; set; }
|
|
70
|
+
public int? Gte { get; set; }
|
|
71
|
+
public int? Lt { get; set; }
|
|
72
|
+
public int? Lte { get; set; }
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/// <summary>Type-safe filter for numeric/float fields.</summary>
|
|
76
|
+
public class NumberFilter
|
|
77
|
+
{
|
|
78
|
+
public new double? Equals { get; set; }
|
|
79
|
+
public double? Not { get; set; }
|
|
80
|
+
public List<double> In { get; set; }
|
|
81
|
+
public List<double> NotIn { get; set; }
|
|
82
|
+
public double? Gt { get; set; }
|
|
83
|
+
public double? Gte { get; set; }
|
|
84
|
+
public double? Lt { get; set; }
|
|
85
|
+
public double? Lte { get; set; }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// <summary>Type-safe filter for boolean fields.</summary>
|
|
89
|
+
public class BoolFilter
|
|
90
|
+
{
|
|
91
|
+
public new bool? Equals { get; set; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/// <summary>Type-safe filter for DateTime fields.</summary>
|
|
95
|
+
public class DateTimeFilter
|
|
96
|
+
{
|
|
97
|
+
public new DateTime? Equals { get; set; }
|
|
98
|
+
public DateTime? Not { get; set; }
|
|
99
|
+
public List<DateTime> In { get; set; }
|
|
100
|
+
public List<DateTime> NotIn { get; set; }
|
|
101
|
+
public DateTime? Gt { get; set; }
|
|
102
|
+
public DateTime? Gte { get; set; }
|
|
103
|
+
public DateTime? Lt { get; set; }
|
|
104
|
+
public DateTime? Lte { get; set; }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
`;
|
|
108
|
+
|
|
109
|
+
for (const model of models) {
|
|
110
|
+
const name = this.capitalize(model.name);
|
|
111
|
+
const csType = (f: { type: string; isOptional: boolean }) => this.mapType(f.type, f.isOptional);
|
|
112
|
+
|
|
113
|
+
content += ` // ── ${name} ORM Types ──────────────────────────────────────────────────────\n\n`;
|
|
114
|
+
|
|
115
|
+
// WhereInput
|
|
116
|
+
content += ` /// <summary>Type-safe WHERE filter for ${name} queries.</summary>\n`;
|
|
117
|
+
content += ` public class ${name}WhereInput\n {\n`;
|
|
118
|
+
content += ` public List<${name}WhereInput> AND { get; set; }\n`;
|
|
119
|
+
content += ` public List<${name}WhereInput> OR { get; set; }\n`;
|
|
120
|
+
content += ` public ${name}WhereInput NOT { get; set; }\n`;
|
|
121
|
+
for (const f of model.fields) {
|
|
122
|
+
const ft = this.getCsFilterType(f.type, f.isOptional);
|
|
123
|
+
content += ` public ${ft} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
124
|
+
}
|
|
125
|
+
content += ` }\n\n`;
|
|
126
|
+
|
|
127
|
+
// OrderByInput
|
|
128
|
+
content += ` /// <summary>Type-safe ORDER BY for ${name} queries.</summary>\n`;
|
|
129
|
+
content += ` public class ${name}OrderByInput\n {\n`;
|
|
130
|
+
for (const f of model.fields) {
|
|
131
|
+
content += ` public string ${this.capitalize(f.name)} { get; set; } // "asc" or "desc"\n`;
|
|
132
|
+
}
|
|
133
|
+
content += ` }\n\n`;
|
|
134
|
+
|
|
135
|
+
// CreateInput
|
|
136
|
+
content += ` /// <summary>Typed data for creating a new ${name} record.</summary>\n`;
|
|
137
|
+
content += ` public class ${name}CreateInput\n {\n`;
|
|
138
|
+
for (const f of model.fields) {
|
|
139
|
+
if (!f.isId) {
|
|
140
|
+
const ct = this.mapType(f.type, f.isOptional || f.hasDefault);
|
|
141
|
+
content += ` public ${ct} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
content += ` }\n\n`;
|
|
145
|
+
|
|
146
|
+
// UpdateInput
|
|
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.type, true); // always nullable for update
|
|
152
|
+
content += ` public ${ut} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
content += ` }\n\n`;
|
|
156
|
+
|
|
157
|
+
// FindManyArgs
|
|
158
|
+
content += ` /// <summary>ORM-style args for ${name}.FindMany().</summary>\n`;
|
|
159
|
+
content += ` public class ${name}FindManyArgs\n {\n`;
|
|
160
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
161
|
+
content += ` public ${name}OrderByInput OrderBy { get; set; }\n`;
|
|
162
|
+
content += ` public int? Take { get; set; }\n`;
|
|
163
|
+
content += ` public int? Skip { get; set; }\n`;
|
|
164
|
+
content += ` public List<string> Select { get; set; }\n`;
|
|
165
|
+
content += ` }\n\n`;
|
|
166
|
+
|
|
167
|
+
// FindFirstArgs
|
|
168
|
+
content += ` /// <summary>ORM-style args for ${name}.FindFirst().</summary>\n`;
|
|
169
|
+
content += ` public class ${name}FindFirstArgs\n {\n`;
|
|
170
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
171
|
+
content += ` public ${name}OrderByInput OrderBy { get; set; }\n`;
|
|
172
|
+
content += ` public List<string> Select { get; set; }\n`;
|
|
173
|
+
content += ` }\n\n`;
|
|
174
|
+
|
|
175
|
+
// FindUniqueArgs
|
|
176
|
+
content += ` /// <summary>ORM-style args for ${name}.FindUnique().</summary>\n`;
|
|
177
|
+
content += ` public class ${name}FindUniqueArgs\n {\n`;
|
|
178
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
179
|
+
content += ` }\n\n`;
|
|
180
|
+
|
|
181
|
+
// DeleteManyArgs
|
|
182
|
+
content += ` /// <summary>ORM-style args for ${name}.DeleteMany().</summary>\n`;
|
|
183
|
+
content += ` public class ${name}DeleteManyArgs\n {\n`;
|
|
184
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
185
|
+
content += ` }\n\n`;
|
|
186
|
+
|
|
187
|
+
// CountArgs
|
|
188
|
+
content += ` /// <summary>ORM-style args for ${name}.Count().</summary>\n`;
|
|
189
|
+
content += ` public class ${name}CountArgs\n {\n`;
|
|
190
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
191
|
+
content += ` }\n\n`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
content += `}\n`;
|
|
195
|
+
|
|
196
|
+
fs.writeFileSync(path.join(this.outputDir, 'An5OrmTypes.cs'), content);
|
|
197
|
+
}
|
|
198
|
+
|
|
25
199
|
private generateConfigClass() {
|
|
26
200
|
const content = `// This file is auto-generated. Do not edit directly.
|
|
27
201
|
using System;
|
|
@@ -115,6 +289,7 @@ using System;
|
|
|
115
289
|
using System.Collections.Generic;
|
|
116
290
|
using System.Data;
|
|
117
291
|
using System.Reflection;
|
|
292
|
+
using System.Text.Json;
|
|
118
293
|
using Microsoft.Data.SqlClient;
|
|
119
294
|
using An5Orm.Entities;
|
|
120
295
|
|
|
@@ -166,7 +341,9 @@ namespace An5Orm
|
|
|
166
341
|
`;
|
|
167
342
|
|
|
168
343
|
for (const model of models) {
|
|
169
|
-
|
|
344
|
+
const name = this.capitalize(model.name);
|
|
345
|
+
content += ` public TableClient<${model.name}> ${name}s => new TableClient<${model.name}>(ConnectionString, "${model.schemaName}.${model.tableName}");\n`;
|
|
346
|
+
content += ` public TableClient<${model.name}> ${name} => ${name}s;\n`;
|
|
170
347
|
}
|
|
171
348
|
|
|
172
349
|
content += ` }
|
|
@@ -231,6 +408,51 @@ namespace An5Orm
|
|
|
231
408
|
return cmd;
|
|
232
409
|
}
|
|
233
410
|
|
|
411
|
+
public List<T> QueryRaw(string query, Dictionary<string, object> parameters = null)
|
|
412
|
+
{
|
|
413
|
+
var list = new List<T>();
|
|
414
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
415
|
+
try
|
|
416
|
+
{
|
|
417
|
+
using (var cmd = CreateCommand(conn, query))
|
|
418
|
+
{
|
|
419
|
+
if (parameters != null)
|
|
420
|
+
{
|
|
421
|
+
foreach (var kvp in parameters)
|
|
422
|
+
{
|
|
423
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
using (var reader = cmd.ExecuteReader())
|
|
428
|
+
{
|
|
429
|
+
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
430
|
+
while (reader.Read())
|
|
431
|
+
{
|
|
432
|
+
var item = new T();
|
|
433
|
+
foreach (var prop in properties)
|
|
434
|
+
{
|
|
435
|
+
if (HasColumn(reader, prop.Name))
|
|
436
|
+
{
|
|
437
|
+
var val = reader[prop.Name];
|
|
438
|
+
if (val != DBNull.Value)
|
|
439
|
+
{
|
|
440
|
+
prop.SetValue(item, val);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
list.Add(item);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
finally
|
|
450
|
+
{
|
|
451
|
+
if (!isTx) conn.Dispose();
|
|
452
|
+
}
|
|
453
|
+
return list;
|
|
454
|
+
}
|
|
455
|
+
|
|
234
456
|
public List<T> FindMany(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
235
457
|
{
|
|
236
458
|
var list = new List<T>();
|
|
@@ -439,15 +661,132 @@ namespace An5Orm
|
|
|
439
661
|
}
|
|
440
662
|
}
|
|
441
663
|
|
|
664
|
+
public int Count(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
665
|
+
{
|
|
666
|
+
string query = $"SELECT COUNT(*) FROM {TableName}";
|
|
667
|
+
if (!string.IsNullOrEmpty(whereClause)) query += $" WHERE {whereClause}";
|
|
668
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
669
|
+
try
|
|
670
|
+
{
|
|
671
|
+
using (var cmd = CreateCommand(conn, query))
|
|
672
|
+
{
|
|
673
|
+
if (parameters != null)
|
|
674
|
+
{
|
|
675
|
+
foreach (var kvp in parameters)
|
|
676
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
677
|
+
}
|
|
678
|
+
var res = cmd.ExecuteScalar();
|
|
679
|
+
return res != null && res != DBNull.Value ? Convert.ToInt32(res) : 0;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
finally { if (!isTx) conn.Dispose(); }
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
public int CreateMany(IEnumerable<T> entities)
|
|
686
|
+
{
|
|
687
|
+
int count = 0;
|
|
688
|
+
foreach (var entity in entities)
|
|
689
|
+
{
|
|
690
|
+
Create(entity);
|
|
691
|
+
count++;
|
|
692
|
+
}
|
|
693
|
+
return count;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
public int UpdateMany(string whereClause, Dictionary<string, object> updateData, Dictionary<string, object> parameters = null)
|
|
697
|
+
{
|
|
698
|
+
if (updateData == null || updateData.Count == 0) return 0;
|
|
699
|
+
var sets = new List<string>();
|
|
700
|
+
var sqlParams = new List<SqlParameter>();
|
|
701
|
+
int pIndex = 0;
|
|
702
|
+
foreach (var kvp in updateData)
|
|
703
|
+
{
|
|
704
|
+
string paramName = "@u_" + pIndex++;
|
|
705
|
+
sets.Add($"{kvp.Key} = {paramName}");
|
|
706
|
+
sqlParams.Add(new SqlParameter(paramName, kvp.Value ?? DBNull.Value));
|
|
707
|
+
}
|
|
708
|
+
string query = $"UPDATE {TableName} SET {string.Join(", ", sets)}";
|
|
709
|
+
if (!string.IsNullOrEmpty(whereClause)) query += $" WHERE {whereClause}";
|
|
710
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
711
|
+
try
|
|
712
|
+
{
|
|
713
|
+
using (var cmd = CreateCommand(conn, query))
|
|
714
|
+
{
|
|
715
|
+
cmd.Parameters.AddRange(sqlParams.ToArray());
|
|
716
|
+
if (parameters != null)
|
|
717
|
+
{
|
|
718
|
+
foreach (var kvp in parameters)
|
|
719
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
720
|
+
}
|
|
721
|
+
return cmd.ExecuteNonQuery();
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
finally { if (!isTx) conn.Dispose(); }
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
public int DeleteMany(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
728
|
+
{
|
|
729
|
+
string query = $"DELETE FROM {TableName}";
|
|
730
|
+
if (!string.IsNullOrEmpty(whereClause)) query += $" WHERE {whereClause}";
|
|
731
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
732
|
+
try
|
|
733
|
+
{
|
|
734
|
+
using (var cmd = CreateCommand(conn, query))
|
|
735
|
+
{
|
|
736
|
+
if (parameters != null)
|
|
737
|
+
{
|
|
738
|
+
foreach (var kvp in parameters)
|
|
739
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
740
|
+
}
|
|
741
|
+
return cmd.ExecuteNonQuery();
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
finally { if (!isTx) conn.Dispose(); }
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
public T Upsert(T entity, string idColumnName = "Id")
|
|
748
|
+
{
|
|
749
|
+
var prop = typeof(T).GetProperty(idColumnName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
750
|
+
if (prop == null) throw new InvalidOperationException($"Property '{idColumnName}' not found on entity.");
|
|
751
|
+
var idVal = prop.GetValue(entity);
|
|
752
|
+
var existing = idVal != null ? FindUnique(idVal) : default;
|
|
753
|
+
if (existing != null) return Update(entity);
|
|
754
|
+
return Create(entity);
|
|
755
|
+
}
|
|
756
|
+
|
|
442
757
|
public List<T> VectorSearch(List<double> vector, int take = 10, string whereClause = null, Dictionary<string, object> parameters = null, string vectorField = "Embedding", string distanceMetric = "cosine")
|
|
443
758
|
{
|
|
759
|
+
// 1. Primary path: Native database SQL vector query execution (VECTOR_DISTANCE)
|
|
760
|
+
try
|
|
761
|
+
{
|
|
762
|
+
var dim = vector.Count;
|
|
763
|
+
var vecJson = JsonSerializer.Serialize(vector);
|
|
764
|
+
var sql = $"SELECT TOP ({take}) *, VECTOR_DISTANCE('{distanceMetric}', CAST([{vectorField}] AS VECTOR({dim}, float32)), CAST(@query_vector AS VECTOR({dim}, float32))) AS distance FROM {TableName} WITH (NOLOCK)";
|
|
765
|
+
|
|
766
|
+
var p = parameters != null ? new Dictionary<string, object>(parameters) : new Dictionary<string, object>();
|
|
767
|
+
p["query_vector"] = vecJson;
|
|
768
|
+
|
|
769
|
+
if (!string.IsNullOrWhiteSpace(whereClause))
|
|
770
|
+
sql += $" WHERE [{vectorField}] IS NOT NULL AND ({whereClause})";
|
|
771
|
+
else
|
|
772
|
+
sql += $" WHERE [{vectorField}] IS NOT NULL";
|
|
773
|
+
sql += " ORDER BY distance ASC";
|
|
774
|
+
|
|
775
|
+
var nativeRows = QueryRaw(sql, p);
|
|
776
|
+
if (nativeRows != null) return nativeRows;
|
|
777
|
+
}
|
|
778
|
+
catch
|
|
779
|
+
{
|
|
780
|
+
// Fallback to in-memory similarity computation if DB instance lacks native VECTOR_DISTANCE
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// 2. Secondary fallback: In-memory similarity computation
|
|
444
784
|
var rows = FindMany(whereClause, parameters);
|
|
445
785
|
var results = new List<Tuple<T, double>>();
|
|
446
786
|
|
|
447
787
|
var propInfo = typeof(T).GetProperty(vectorField, BindingFlags.Public | BindingFlags.Instance);
|
|
448
788
|
if (propInfo == null)
|
|
449
789
|
{
|
|
450
|
-
// Fallback case-insensitive
|
|
451
790
|
propInfo = typeof(T).GetProperty(vectorField, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
452
791
|
}
|
|
453
792
|
if (propInfo == null) return rows;
|
|
@@ -477,12 +816,11 @@ namespace An5Orm
|
|
|
477
816
|
}
|
|
478
817
|
|
|
479
818
|
results.Sort((a, b) => a.Item2.CompareTo(b.Item2));
|
|
480
|
-
|
|
819
|
+
|
|
481
820
|
var output = new List<T>();
|
|
482
821
|
int limit = Math.Min(take, results.Count);
|
|
483
822
|
for (int i = 0; i < limit; i++)
|
|
484
823
|
{
|
|
485
|
-
// Set the distance property if the model has a public property named Distance
|
|
486
824
|
var distanceProp = typeof(T).GetProperty("Distance", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
487
825
|
if (distanceProp != null && distanceProp.PropertyType == typeof(double))
|
|
488
826
|
{
|