@an5/adapters 0.2.5 → 0.2.6
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 +189 -305
- package/dist/an5Adapter.d.ts +85 -7
- package/dist/an5Adapter.js +736 -64
- package/dist/base/metadata.d.ts +10 -0
- package/dist/base/metadata.js +107 -0
- package/dist/base/sql.d.ts +19 -1
- package/dist/base/sql.js +152 -24
- package/dist/base/types.d.ts +4 -1
- package/dist/browser.d.ts +4 -1
- package/dist/browser.js +9 -2
- package/dist/googlesheets/adapter.d.ts +1 -0
- package/dist/googlesheets/adapter.js +9 -2
- package/dist/googlesheets/config.d.ts +4 -4
- package/dist/googlesheets/helpers.js +2 -0
- package/dist/googlesheets/parseConnectionString.js +5 -2
- package/dist/googlesheets/sqlExecutor.js +29 -24
- package/dist/googlesheets/tableClient.js +14 -5
- package/dist/index.d.ts +3 -1
- package/dist/index.js +7 -1
- package/dist/mssql/engine.js +8 -4
- package/dist/mysql/engine.js +1 -1
- package/dist/postgres/engine.js +1 -1
- package/dist/sqlite/browserEngine.d.ts +27 -0
- package/dist/sqlite/browserEngine.js +161 -0
- package/dist/sqlite/engine.js +15 -3
- package/dist/sqlite/index.d.ts +1 -0
- package/dist/sqlite/index.js +1 -0
- package/dist/typed.d.ts +5 -0
- package/dist/typed.js +2 -0
- package/dotnet/An5Adapter.cs +1089 -275
- package/dotnet/Base/Core.cs +340 -82
- package/dotnet/Mssql/MssqlEngine.cs +145 -144
- package/dotnet/Postgres/PostgresEngine.cs +121 -121
- package/golang/an5_adapter.go +314 -0
- package/golang/base/base.go +100 -0
- package/golang/base/metadata.go +116 -0
- package/golang/base/where.go +311 -0
- package/golang/go.mod +3 -0
- package/golang/mssql/mssql.go +32 -0
- package/golang/postgres/postgres.go +37 -0
- package/golang/sqlite/sqlite.go +14 -0
- package/golang/sqlite_test.go +42 -0
- package/golang/table_client.go +1329 -0
- package/package.json +17 -7
- package/python/an5_adapter.py +119 -97
- package/python/base/__init__.py +3 -3
- package/python/base/dialects.py +11 -8
- package/python/base/metadata.py +35 -10
- package/python/base/sql.py +172 -121
- package/python/mssql/__init__.py +1 -1
- package/python/mssql/provider.py +62 -62
- package/python/postgres/__init__.py +1 -1
- package/python/postgres/provider.py +15 -15
- package/python/table_client.py +619 -216
- package/typescript/src/an5Adapter.ts +1220 -505
- package/typescript/src/base/index.ts +3 -3
- package/typescript/src/base/metadata.ts +148 -28
- package/typescript/src/base/sql.ts +264 -98
- package/typescript/src/base/types.ts +27 -24
- package/typescript/src/browser.ts +10 -6
- package/typescript/src/config.ts +95 -95
- package/typescript/src/googlesheets/adapter.ts +269 -259
- package/typescript/src/googlesheets/an5SheetsAdapter.ts +3 -3
- package/typescript/src/googlesheets/config.ts +47 -47
- package/typescript/src/googlesheets/helpers.ts +144 -143
- package/typescript/src/googlesheets/index.ts +3 -3
- package/typescript/src/googlesheets/parseConnectionString.ts +48 -46
- package/typescript/src/googlesheets/retry.ts +126 -126
- package/typescript/src/googlesheets/sqlExecutor.ts +304 -301
- package/typescript/src/googlesheets/tableClient.ts +461 -455
- package/typescript/src/googlesheets/types.ts +6 -6
- package/typescript/src/index.ts +31 -27
- package/typescript/src/mssql/engine.ts +134 -132
- package/typescript/src/mssql/index.ts +1 -1
- package/typescript/src/mysql/engine.ts +80 -80
- package/typescript/src/mysql/index.ts +1 -1
- package/typescript/src/postgres/engine.ts +87 -87
- package/typescript/src/postgres/index.ts +1 -1
- package/typescript/src/sqlite/browserEngine.ts +192 -0
- package/typescript/src/sqlite/engine.ts +66 -55
- package/typescript/src/sqlite/index.ts +3 -1
- package/typescript/src/typed.ts +52 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
// Package an5adapters provides standalone database adapter engines for Go applications.
|
|
2
|
+
package an5adapters
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"context"
|
|
6
|
+
"database/sql"
|
|
7
|
+
"encoding/json"
|
|
8
|
+
"sort"
|
|
9
|
+
"strings"
|
|
10
|
+
|
|
11
|
+
"an5adapters/base"
|
|
12
|
+
"an5adapters/mssql"
|
|
13
|
+
"an5adapters/postgres"
|
|
14
|
+
"an5adapters/sqlite"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
// Re-export core base types and functions for package root convenience.
|
|
18
|
+
type Dialect = base.Dialect
|
|
19
|
+
type AdapterMetadata = base.AdapterMetadata
|
|
20
|
+
type RelationDef = base.RelationDef
|
|
21
|
+
|
|
22
|
+
const (
|
|
23
|
+
DialectMssql = base.DialectMssql
|
|
24
|
+
DialectPostgres = base.DialectPostgres
|
|
25
|
+
DialectSqlite = base.DialectSqlite
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
func DetectDialect(connStr string) Dialect {
|
|
29
|
+
return base.DetectDialect(connStr)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func QuoteIdentifier(name string, dialect Dialect) string {
|
|
33
|
+
return base.QuoteIdentifier(name, dialect)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
func QuoteTable(tableName string, dialect Dialect) string {
|
|
37
|
+
return base.QuoteTable(tableName, dialect)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
func CosineSimilarity(v1, v2 []float64) float64 {
|
|
41
|
+
return base.CosineSimilarity(v1, v2)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
func EuclideanDistance(v1, v2 []float64) float64 {
|
|
45
|
+
return base.EuclideanDistance(v1, v2)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
func DotProduct(v1, v2 []float64) float64 {
|
|
49
|
+
return base.DotProduct(v1, v2)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// SetAdapterMetadata injects model-to-table metadata from the generated client.
|
|
53
|
+
func SetAdapterMetadata(meta AdapterMetadata) {
|
|
54
|
+
base.SetAdapterMetadata(meta)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// GetModelToTable returns the current model-to-table mapping.
|
|
58
|
+
func GetModelToTable() map[string]string {
|
|
59
|
+
return base.GetModelToTable()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// GetRelationsForModel returns the relation definitions for the given model name.
|
|
63
|
+
func GetRelationsForModel(modelName string) map[string]RelationDef {
|
|
64
|
+
return base.GetRelationsForModel(modelName)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// An5Adapter manages runtime database execution for Go applications.
|
|
68
|
+
type An5Adapter struct {
|
|
69
|
+
DB *sql.DB
|
|
70
|
+
Dialect Dialect
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// NewAn5Adapter constructs an An5Adapter instance.
|
|
74
|
+
func NewAn5Adapter(db *sql.DB, connStr string) *An5Adapter {
|
|
75
|
+
return &An5Adapter{
|
|
76
|
+
DB: db,
|
|
77
|
+
Dialect: base.DetectDialect(connStr),
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Connect validates the database connection (ping).
|
|
82
|
+
func (a *An5Adapter) Connect(ctx context.Context) error {
|
|
83
|
+
return a.DB.PingContext(ctx)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Disconnect closes the underlying database connection pool.
|
|
87
|
+
func (a *An5Adapter) Disconnect() error {
|
|
88
|
+
return a.DB.Close()
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// QueryRaw executes a SELECT query and returns rows as maps.
|
|
92
|
+
func (a *An5Adapter) QueryRaw(ctx context.Context, query string, args ...interface{}) ([]map[string]interface{}, error) {
|
|
93
|
+
rows, err := a.DB.QueryContext(ctx, query, args...)
|
|
94
|
+
if err != nil {
|
|
95
|
+
return nil, err
|
|
96
|
+
}
|
|
97
|
+
defer rows.Close()
|
|
98
|
+
|
|
99
|
+
cols, err := rows.Columns()
|
|
100
|
+
if err != nil {
|
|
101
|
+
return nil, err
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
var results []map[string]interface{}
|
|
105
|
+
for rows.Next() {
|
|
106
|
+
columns := make([]interface{}, len(cols))
|
|
107
|
+
columnPointers := make([]interface{}, len(cols))
|
|
108
|
+
for i := range columns {
|
|
109
|
+
columnPointers[i] = &columns[i]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if err := rows.Scan(columnPointers...); err != nil {
|
|
113
|
+
return nil, err
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
m := make(map[string]interface{})
|
|
117
|
+
for i, colName := range cols {
|
|
118
|
+
val := columnPointers[i].(*interface{})
|
|
119
|
+
m[colName] = *val
|
|
120
|
+
}
|
|
121
|
+
results = append(results, m)
|
|
122
|
+
}
|
|
123
|
+
if err := rows.Err(); err != nil {
|
|
124
|
+
return nil, err
|
|
125
|
+
}
|
|
126
|
+
return results, nil
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ExecuteRaw executes an INSERT, UPDATE, or DELETE query and returns affected rows count.
|
|
130
|
+
func (a *An5Adapter) ExecuteRaw(ctx context.Context, query string, args ...interface{}) (int64, error) {
|
|
131
|
+
res, err := a.DB.ExecContext(ctx, query, args...)
|
|
132
|
+
if err != nil {
|
|
133
|
+
return 0, err
|
|
134
|
+
}
|
|
135
|
+
return res.RowsAffected()
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Transaction executes a function within a database transaction.
|
|
139
|
+
func (a *An5Adapter) Transaction(ctx context.Context, fn func(tx *sql.Tx) error) error {
|
|
140
|
+
tx, err := a.DB.BeginTx(ctx, nil)
|
|
141
|
+
if err != nil {
|
|
142
|
+
return err
|
|
143
|
+
}
|
|
144
|
+
defer func() {
|
|
145
|
+
if p := recover(); p != nil {
|
|
146
|
+
_ = tx.Rollback()
|
|
147
|
+
panic(p)
|
|
148
|
+
}
|
|
149
|
+
}()
|
|
150
|
+
|
|
151
|
+
if err := fn(tx); err != nil {
|
|
152
|
+
_ = tx.Rollback()
|
|
153
|
+
return err
|
|
154
|
+
}
|
|
155
|
+
return tx.Commit()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// VectorSearch ranks rows by vector distance using native SQL execution with in-memory fallback.
|
|
159
|
+
func (a *An5Adapter) VectorSearch(ctx context.Context, tableName string, targetVector []float64, take int, whereClause string, vectorField string, distanceMetric string, args ...interface{}) ([]map[string]interface{}, error) {
|
|
160
|
+
if vectorField == "" {
|
|
161
|
+
vectorField = "embedding"
|
|
162
|
+
}
|
|
163
|
+
if distanceMetric == "" {
|
|
164
|
+
distanceMetric = "cosine"
|
|
165
|
+
}
|
|
166
|
+
dim := len(targetVector)
|
|
167
|
+
vecBytes, _ := json.Marshal(targetVector)
|
|
168
|
+
vecStr := string(vecBytes)
|
|
169
|
+
|
|
170
|
+
// 1. Primary path: Native database SQL vector query execution via dialect provider (mssql / postgres / sqlite)
|
|
171
|
+
var sqlQuery string
|
|
172
|
+
if a.Dialect == base.DialectPostgres {
|
|
173
|
+
sqlQuery = postgres.BuildVectorSearchQuery(tableName, vectorField, distanceMetric, dim, take, whereClause)
|
|
174
|
+
} else if a.Dialect == base.DialectMssql {
|
|
175
|
+
sqlQuery = mssql.BuildVectorSearchQuery(tableName, vectorField, distanceMetric, dim, take, whereClause)
|
|
176
|
+
} else if a.Dialect == base.DialectSqlite {
|
|
177
|
+
sqlQuery = sqlite.BuildVectorSearchQuery(tableName, vectorField, distanceMetric, dim, take, whereClause)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if sqlQuery != "" {
|
|
181
|
+
queryArgs := append([]interface{}{vecStr}, args...)
|
|
182
|
+
nativeRows, err := a.QueryRaw(ctx, sqlQuery, queryArgs...)
|
|
183
|
+
if err == nil {
|
|
184
|
+
return nativeRows, nil
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 2. Secondary fallback: Fetch rows and compute vector distance in-memory if DB lacks native vector extensions
|
|
189
|
+
fallbackQuery := "SELECT * FROM " + base.QuoteTable(tableName, a.Dialect)
|
|
190
|
+
if strings.TrimSpace(whereClause) != "" {
|
|
191
|
+
fallbackQuery += " WHERE " + whereClause
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
rows, err := a.QueryRaw(ctx, fallbackQuery, args...)
|
|
195
|
+
if err != nil {
|
|
196
|
+
return nil, err
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return VectorSearchFallback(rows, targetVector, take, vectorField, distanceMetric), nil
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// VectorSearchFallback ranks rows in-memory.
|
|
203
|
+
func VectorSearchFallback(rows []map[string]interface{}, targetVector []float64, take int, vectorField string, distanceMetric string) []map[string]interface{} {
|
|
204
|
+
type scored struct {
|
|
205
|
+
row map[string]interface{}
|
|
206
|
+
distance float64
|
|
207
|
+
}
|
|
208
|
+
var list []scored
|
|
209
|
+
|
|
210
|
+
for _, r := range rows {
|
|
211
|
+
raw, ok := r[vectorField]
|
|
212
|
+
if !ok || raw == nil {
|
|
213
|
+
continue
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
var rowVec []float64
|
|
217
|
+
switch v := raw.(type) {
|
|
218
|
+
case string:
|
|
219
|
+
_ = json.Unmarshal([]byte(v), &rowVec)
|
|
220
|
+
case []byte:
|
|
221
|
+
_ = json.Unmarshal(v, &rowVec)
|
|
222
|
+
case []float64:
|
|
223
|
+
rowVec = v
|
|
224
|
+
case []interface{}:
|
|
225
|
+
for _, item := range v {
|
|
226
|
+
if f, ok := item.(float64); ok {
|
|
227
|
+
rowVec = append(rowVec, f)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if len(rowVec) != len(targetVector) {
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
var dist float64
|
|
237
|
+
switch strings.ToLower(distanceMetric) {
|
|
238
|
+
case "euclidean":
|
|
239
|
+
dist = base.EuclideanDistance(targetVector, rowVec)
|
|
240
|
+
case "dot":
|
|
241
|
+
dist = -base.DotProduct(targetVector, rowVec)
|
|
242
|
+
default: // cosine
|
|
243
|
+
dist = 1.0 - base.CosineSimilarity(targetVector, rowVec)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
rCopy := make(map[string]interface{})
|
|
247
|
+
for k, val := range r {
|
|
248
|
+
rCopy[k] = val
|
|
249
|
+
}
|
|
250
|
+
rCopy["distance"] = dist
|
|
251
|
+
list = append(list, scored{row: rCopy, distance: dist})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
sort.Slice(list, func(i, j int) bool {
|
|
255
|
+
return list[i].distance < list[j].distance
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
var result []map[string]interface{}
|
|
259
|
+
limit := take
|
|
260
|
+
if limit <= 0 || limit > len(list) {
|
|
261
|
+
limit = len(list)
|
|
262
|
+
}
|
|
263
|
+
for i := 0; i < limit; i++ {
|
|
264
|
+
result = append(result, list[i].row)
|
|
265
|
+
}
|
|
266
|
+
return result
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// QueryProc executes a stored procedure and returns the result rows.
|
|
270
|
+
func (a *An5Adapter) QueryProc(ctx context.Context, procName string, args ...interface{}) ([]map[string]interface{}, error) {
|
|
271
|
+
var sql string
|
|
272
|
+
if a.Dialect == base.DialectPostgres {
|
|
273
|
+
placeholders := make([]string, len(args))
|
|
274
|
+
for i := range args {
|
|
275
|
+
placeholders[i] = placeholder(a.Dialect, i+1)
|
|
276
|
+
}
|
|
277
|
+
sql = "CALL " + procName + "(" + strings.Join(placeholders, ", ") + ")"
|
|
278
|
+
} else {
|
|
279
|
+
placeholders := make([]string, len(args))
|
|
280
|
+
for i := range args {
|
|
281
|
+
placeholders[i] = "?"
|
|
282
|
+
}
|
|
283
|
+
if len(placeholders) > 0 {
|
|
284
|
+
sql = "EXEC " + procName + " " + strings.Join(placeholders, ", ")
|
|
285
|
+
} else {
|
|
286
|
+
sql = "EXEC " + procName
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return a.QueryRaw(ctx, sql, args...)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// ExecuteProc executes a stored procedure and returns affected rows.
|
|
293
|
+
func (a *An5Adapter) ExecuteProc(ctx context.Context, procName string, args ...interface{}) (int64, error) {
|
|
294
|
+
var sql string
|
|
295
|
+
if a.Dialect == base.DialectPostgres {
|
|
296
|
+
placeholders := make([]string, len(args))
|
|
297
|
+
for i := range args {
|
|
298
|
+
placeholders[i] = placeholder(a.Dialect, i+1)
|
|
299
|
+
}
|
|
300
|
+
sql = "CALL " + procName + "(" + strings.Join(placeholders, ", ") + ")"
|
|
301
|
+
} else {
|
|
302
|
+
placeholders := make([]string, len(args))
|
|
303
|
+
for i := range args {
|
|
304
|
+
placeholders[i] = "?"
|
|
305
|
+
}
|
|
306
|
+
if len(placeholders) > 0 {
|
|
307
|
+
sql = "EXEC " + procName + " " + strings.Join(placeholders, ", ")
|
|
308
|
+
} else {
|
|
309
|
+
sql = "EXEC " + procName
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return a.ExecuteRaw(ctx, sql, args...)
|
|
313
|
+
}
|
|
314
|
+
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// Package base provides core dialect types, quoting utilities, and vector math for AN5 Go Adapters.
|
|
2
|
+
package base
|
|
3
|
+
|
|
4
|
+
import (
|
|
5
|
+
"math"
|
|
6
|
+
"strings"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// Dialect represents supported database engines.
|
|
10
|
+
type Dialect string
|
|
11
|
+
|
|
12
|
+
const (
|
|
13
|
+
DialectMssql Dialect = "mssql"
|
|
14
|
+
DialectPostgres Dialect = "postgres"
|
|
15
|
+
DialectSqlite Dialect = "sqlite"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
// DetectDialect parses connection string to determine database dialect.
|
|
19
|
+
func DetectDialect(connStr string) Dialect {
|
|
20
|
+
lower := strings.ToLower(strings.TrimSpace(connStr))
|
|
21
|
+
if strings.HasPrefix(lower, "postgres://") || strings.HasPrefix(lower, "postgresql://") || strings.Contains(lower, "port=5432") {
|
|
22
|
+
return DialectPostgres
|
|
23
|
+
}
|
|
24
|
+
if strings.HasPrefix(lower, "sqlite://") || strings.HasPrefix(lower, "sqlite:") || strings.HasPrefix(lower, "file:") || strings.HasSuffix(lower, ".db") || strings.HasSuffix(lower, ".sqlite") || strings.HasSuffix(lower, ".sqlite3") || lower == ":memory:" {
|
|
25
|
+
return DialectSqlite
|
|
26
|
+
}
|
|
27
|
+
return DialectMssql
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// QuoteIdentifier quotes column or identifier names according to dialect.
|
|
31
|
+
func QuoteIdentifier(name string, dialect Dialect) string {
|
|
32
|
+
raw := stripWrapping(name, "[", "]")
|
|
33
|
+
if dialect == DialectPostgres || dialect == DialectSqlite {
|
|
34
|
+
raw = stripWrapping(raw, `"`, `"`)
|
|
35
|
+
return `"` + strings.ReplaceAll(raw, `"`, `""`) + `"`
|
|
36
|
+
}
|
|
37
|
+
return "[" + strings.ReplaceAll(raw, "]", "]]") + "]"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// QuoteTable quotes schema-qualified table names (e.g. "dbo.users" -> "[dbo].[users]").
|
|
41
|
+
func QuoteTable(tableName string, dialect Dialect) string {
|
|
42
|
+
if strings.HasPrefix(tableName, "[") || strings.HasPrefix(tableName, `"`) {
|
|
43
|
+
return tableName
|
|
44
|
+
}
|
|
45
|
+
parts := strings.Split(tableName, ".")
|
|
46
|
+
var quoted []string
|
|
47
|
+
for _, p := range parts {
|
|
48
|
+
quoted = append(quoted, QuoteIdentifier(p, dialect))
|
|
49
|
+
}
|
|
50
|
+
return strings.Join(quoted, ".")
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func stripWrapping(name, left, right string) string {
|
|
54
|
+
if strings.HasPrefix(name, left) && strings.HasSuffix(name, right) {
|
|
55
|
+
return strings.TrimSuffix(strings.TrimPrefix(name, left), right)
|
|
56
|
+
}
|
|
57
|
+
return name
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// CosineSimilarity computes cosine similarity between two float64 vectors.
|
|
61
|
+
func CosineSimilarity(v1, v2 []float64) float64 {
|
|
62
|
+
if len(v1) != len(v2) || len(v1) == 0 {
|
|
63
|
+
return 0
|
|
64
|
+
}
|
|
65
|
+
var dot, m1, m2 float64
|
|
66
|
+
for i := 0; i < len(v1); i++ {
|
|
67
|
+
dot += v1[i] * v2[i]
|
|
68
|
+
m1 += v1[i] * v1[i]
|
|
69
|
+
m2 += v2[i] * v2[i]
|
|
70
|
+
}
|
|
71
|
+
if m1 == 0 || m2 == 0 {
|
|
72
|
+
return 0
|
|
73
|
+
}
|
|
74
|
+
return dot / (math.Sqrt(m1) * math.Sqrt(m2))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// EuclideanDistance computes L2 distance between two float64 vectors.
|
|
78
|
+
func EuclideanDistance(v1, v2 []float64) float64 {
|
|
79
|
+
if len(v1) != len(v2) || len(v1) == 0 {
|
|
80
|
+
return 0
|
|
81
|
+
}
|
|
82
|
+
var sum float64
|
|
83
|
+
for i := 0; i < len(v1); i++ {
|
|
84
|
+
diff := v1[i] - v2[i]
|
|
85
|
+
sum += diff * diff
|
|
86
|
+
}
|
|
87
|
+
return math.Sqrt(sum)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// DotProduct computes inner product of two float64 vectors.
|
|
91
|
+
func DotProduct(v1, v2 []float64) float64 {
|
|
92
|
+
if len(v1) != len(v2) || len(v1) == 0 {
|
|
93
|
+
return 0
|
|
94
|
+
}
|
|
95
|
+
var dot float64
|
|
96
|
+
for i := 0; i < len(v1); i++ {
|
|
97
|
+
dot += v1[i] * v2[i]
|
|
98
|
+
}
|
|
99
|
+
return dot
|
|
100
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Package base provides core dialect types, quoting utilities, vector math,
|
|
2
|
+
// and adapter metadata for AN5 Go Adapters.
|
|
3
|
+
package base
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"strings"
|
|
7
|
+
"sync"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// RelationDef describes a model relation for eager-loading (include) support.
|
|
11
|
+
type RelationDef struct {
|
|
12
|
+
ModelName string
|
|
13
|
+
RelationType string // "many" | "one"
|
|
14
|
+
ForeignKey string
|
|
15
|
+
LocalKey string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// AdapterMetadata holds model-to-table, field mapping, and relation mapping injected by generated clients.
|
|
19
|
+
type AdapterMetadata struct {
|
|
20
|
+
ModelToTable map[string]string
|
|
21
|
+
ModelFields map[string]interface{}
|
|
22
|
+
RelationMap map[string]map[string]RelationDef
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var (
|
|
26
|
+
metaMu sync.RWMutex
|
|
27
|
+
modelToTable = map[string]string{}
|
|
28
|
+
modelFields = map[string]interface{}{}
|
|
29
|
+
relationMap = map[string]map[string]RelationDef{}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
// SetAdapterMetadata stores metadata provided by the generated an5Client.
|
|
33
|
+
// Call this once at application startup after initializing the DB context.
|
|
34
|
+
func SetAdapterMetadata(meta AdapterMetadata) {
|
|
35
|
+
metaMu.Lock()
|
|
36
|
+
defer metaMu.Unlock()
|
|
37
|
+
modelToTable = make(map[string]string, len(meta.ModelToTable))
|
|
38
|
+
for k, v := range meta.ModelToTable {
|
|
39
|
+
modelToTable[k] = v
|
|
40
|
+
}
|
|
41
|
+
modelFields = make(map[string]interface{}, len(meta.ModelFields))
|
|
42
|
+
for k, v := range meta.ModelFields {
|
|
43
|
+
modelFields[k] = v
|
|
44
|
+
}
|
|
45
|
+
relationMap = make(map[string]map[string]RelationDef, len(meta.RelationMap))
|
|
46
|
+
for model, rels := range meta.RelationMap {
|
|
47
|
+
relationMap[model] = make(map[string]RelationDef, len(rels))
|
|
48
|
+
for key, def := range rels {
|
|
49
|
+
relationMap[model][key] = def
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// GetModelToTable returns the model-to-table mapping (read-only snapshot).
|
|
55
|
+
func GetModelToTable() map[string]string {
|
|
56
|
+
metaMu.RLock()
|
|
57
|
+
defer metaMu.RUnlock()
|
|
58
|
+
out := make(map[string]string, len(modelToTable))
|
|
59
|
+
for k, v := range modelToTable {
|
|
60
|
+
out[k] = v
|
|
61
|
+
}
|
|
62
|
+
return out
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// GetFieldsForModel returns field metadata for the given model name.
|
|
66
|
+
func GetFieldsForModel(modelName string) interface{} {
|
|
67
|
+
metaMu.RLock()
|
|
68
|
+
defer metaMu.RUnlock()
|
|
69
|
+
return modelFields[modelName]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// GetRelationsForModel returns the relation map for the given model name (read-only snapshot).
|
|
73
|
+
func GetRelationsForModel(modelName string) map[string]RelationDef {
|
|
74
|
+
metaMu.RLock()
|
|
75
|
+
defer metaMu.RUnlock()
|
|
76
|
+
rels := relationMap[modelName]
|
|
77
|
+
if rels == nil {
|
|
78
|
+
return map[string]RelationDef{}
|
|
79
|
+
}
|
|
80
|
+
out := make(map[string]RelationDef, len(rels))
|
|
81
|
+
for k, v := range rels {
|
|
82
|
+
out[k] = v
|
|
83
|
+
}
|
|
84
|
+
return out
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// GetRelationMap returns the full model-to-relations map (read-only snapshot).
|
|
88
|
+
func GetRelationMap() map[string]map[string]RelationDef {
|
|
89
|
+
metaMu.RLock()
|
|
90
|
+
defer metaMu.RUnlock()
|
|
91
|
+
out := make(map[string]map[string]RelationDef, len(relationMap))
|
|
92
|
+
for model, rels := range relationMap {
|
|
93
|
+
out[model] = make(map[string]RelationDef, len(rels))
|
|
94
|
+
for k, v := range rels {
|
|
95
|
+
out[model][k] = v
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return out
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ResolveTable looks up the physical table name for a model name (case-insensitive fallback).
|
|
102
|
+
func ResolveTable(modelName string) string {
|
|
103
|
+
metaMu.RLock()
|
|
104
|
+
defer metaMu.RUnlock()
|
|
105
|
+
if t, ok := modelToTable[modelName]; ok {
|
|
106
|
+
return t
|
|
107
|
+
}
|
|
108
|
+
// lowercase fallback
|
|
109
|
+
lower := strings.ToLower(modelName)
|
|
110
|
+
for k, v := range modelToTable {
|
|
111
|
+
if strings.ToLower(k) == lower {
|
|
112
|
+
return v
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return modelName
|
|
116
|
+
}
|