@an5/adapters 0.2.4 → 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.
Files changed (82) hide show
  1. package/README.md +189 -304
  2. package/dist/an5Adapter.d.ts +85 -7
  3. package/dist/an5Adapter.js +736 -64
  4. package/dist/base/metadata.d.ts +10 -0
  5. package/dist/base/metadata.js +107 -0
  6. package/dist/base/sql.d.ts +19 -1
  7. package/dist/base/sql.js +152 -24
  8. package/dist/base/types.d.ts +4 -1
  9. package/dist/browser.d.ts +5 -2
  10. package/dist/browser.js +10 -2
  11. package/dist/googlesheets/adapter.d.ts +1 -0
  12. package/dist/googlesheets/adapter.js +9 -2
  13. package/dist/googlesheets/config.d.ts +4 -4
  14. package/dist/googlesheets/helpers.js +2 -0
  15. package/dist/googlesheets/parseConnectionString.js +5 -2
  16. package/dist/googlesheets/sqlExecutor.js +29 -24
  17. package/dist/googlesheets/tableClient.js +14 -5
  18. package/dist/index.d.ts +5 -1
  19. package/dist/index.js +12 -1
  20. package/dist/mssql/engine.js +8 -4
  21. package/dist/mysql/engine.js +1 -1
  22. package/dist/postgres/engine.js +1 -1
  23. package/dist/sqlite/browserEngine.d.ts +27 -0
  24. package/dist/sqlite/browserEngine.js +161 -0
  25. package/dist/sqlite/engine.js +15 -3
  26. package/dist/sqlite/index.d.ts +1 -0
  27. package/dist/sqlite/index.js +1 -0
  28. package/dist/typed.d.ts +5 -0
  29. package/dist/typed.js +2 -0
  30. package/dotnet/An5Adapter.cs +1089 -275
  31. package/dotnet/Base/Core.cs +340 -82
  32. package/dotnet/Mssql/MssqlEngine.cs +145 -144
  33. package/dotnet/Postgres/PostgresEngine.cs +121 -121
  34. package/golang/an5_adapter.go +314 -0
  35. package/golang/base/base.go +100 -0
  36. package/golang/base/metadata.go +116 -0
  37. package/golang/base/where.go +311 -0
  38. package/golang/go.mod +3 -0
  39. package/golang/mssql/mssql.go +32 -0
  40. package/golang/postgres/postgres.go +37 -0
  41. package/golang/sqlite/sqlite.go +14 -0
  42. package/golang/sqlite_test.go +42 -0
  43. package/golang/table_client.go +1329 -0
  44. package/package.json +18 -8
  45. package/python/an5_adapter.py +119 -97
  46. package/python/base/__init__.py +3 -3
  47. package/python/base/dialects.py +11 -8
  48. package/python/base/metadata.py +35 -10
  49. package/python/base/sql.py +172 -121
  50. package/python/mssql/__init__.py +1 -1
  51. package/python/mssql/provider.py +62 -62
  52. package/python/postgres/__init__.py +1 -1
  53. package/python/postgres/provider.py +15 -15
  54. package/python/table_client.py +619 -216
  55. package/typescript/src/an5Adapter.ts +1220 -505
  56. package/typescript/src/base/index.ts +3 -3
  57. package/typescript/src/base/metadata.ts +148 -28
  58. package/typescript/src/base/sql.ts +264 -98
  59. package/typescript/src/base/types.ts +27 -24
  60. package/typescript/src/browser.ts +10 -6
  61. package/typescript/src/config.ts +95 -95
  62. package/typescript/src/googlesheets/adapter.ts +269 -259
  63. package/typescript/src/googlesheets/an5SheetsAdapter.ts +3 -3
  64. package/typescript/src/googlesheets/config.ts +47 -47
  65. package/typescript/src/googlesheets/helpers.ts +144 -143
  66. package/typescript/src/googlesheets/index.ts +3 -3
  67. package/typescript/src/googlesheets/parseConnectionString.ts +48 -46
  68. package/typescript/src/googlesheets/retry.ts +126 -126
  69. package/typescript/src/googlesheets/sqlExecutor.ts +304 -301
  70. package/typescript/src/googlesheets/tableClient.ts +461 -455
  71. package/typescript/src/googlesheets/types.ts +6 -6
  72. package/typescript/src/index.ts +32 -19
  73. package/typescript/src/mssql/engine.ts +134 -132
  74. package/typescript/src/mssql/index.ts +1 -1
  75. package/typescript/src/mysql/engine.ts +80 -80
  76. package/typescript/src/mysql/index.ts +1 -1
  77. package/typescript/src/postgres/engine.ts +87 -87
  78. package/typescript/src/postgres/index.ts +1 -1
  79. package/typescript/src/sqlite/browserEngine.ts +192 -0
  80. package/typescript/src/sqlite/engine.ts +66 -55
  81. package/typescript/src/sqlite/index.ts +3 -1
  82. package/typescript/src/typed.ts +52 -0
@@ -0,0 +1,311 @@
1
+ // Package base provides dialect-aware WHERE and ORDER BY builders for the AN5 Go adapter.
2
+ package base
3
+
4
+ import (
5
+ "fmt"
6
+ "sort"
7
+ "strings"
8
+ )
9
+
10
+ // operatorKeys are the supported comparison operators inside a filter object.
11
+ var operatorKeys = []string{"equals", "in", "notIn", "contains", "startsWith", "endsWith", "not", "gte", "lte", "gt", "lt"}
12
+
13
+ // isOperatorMap reports whether v is a filter dictionary containing ORM operators.
14
+ func isOperatorMap(v interface{}) bool {
15
+ m, ok := v.(map[string]interface{})
16
+ if !ok {
17
+ return false
18
+ }
19
+ for _, op := range operatorKeys {
20
+ if _, has := m[op]; has {
21
+ return true
22
+ }
23
+ }
24
+ return false
25
+ }
26
+
27
+ // isPlainMap reports whether v is a non-nil map.
28
+ func isPlainMap(v interface{}) (map[string]interface{}, bool) {
29
+ m, ok := v.(map[string]interface{})
30
+ if !ok || m == nil {
31
+ return nil, false
32
+ }
33
+ return m, true
34
+ }
35
+
36
+ // cleanWhere flattens relation-style nested where objects (key contains '_' with a plain-map value).
37
+ func cleanWhere(where map[string]interface{}) map[string]interface{} {
38
+ clean := map[string]interface{}{}
39
+ for k, v := range where {
40
+ if strings.Contains(k, "_") {
41
+ if m, ok := isPlainMap(v); ok && !isOperatorMap(v) {
42
+ for nk, nv := range m {
43
+ clean[nk] = nv
44
+ }
45
+ } else {
46
+ clean[k] = v
47
+ }
48
+ } else {
49
+ clean[k] = v
50
+ }
51
+ }
52
+ return clean
53
+ }
54
+
55
+ // sortedKeys returns the map keys in a stable sorted order for deterministic SQL.
56
+ func sortedKeys(m map[string]interface{}) []string {
57
+ keys := make([]string, 0, len(m))
58
+ for k := range m {
59
+ keys = append(keys, k)
60
+ }
61
+ sort.Strings(keys)
62
+ return keys
63
+ }
64
+
65
+ // BuildWhere converts an ORM-style where object into a SQL clause with ordered placeholders.
66
+ // ph returns the placeholder for arg position n (1-based).
67
+ func BuildWhere(where map[string]interface{}, dialect Dialect, ph func(n int) string) (string, []interface{}) {
68
+ args := make([]interface{}, 0, 8)
69
+ clause := buildWhereRec(cleanWhere(where), dialect, &args, ph, "")
70
+ return clause, args
71
+ }
72
+
73
+ func buildWhereRec(where map[string]interface{}, dialect Dialect, args *[]interface{}, ph func(n int) string, prefix string) string {
74
+ if where == nil || len(where) == 0 {
75
+ return ""
76
+ }
77
+ var conditions []string
78
+
79
+ for _, key := range sortedKeys(where) {
80
+ value := where[key]
81
+
82
+ switch key {
83
+ case "OR":
84
+ if list, ok := toSlice(value); ok && len(list) > 0 {
85
+ var subs []string
86
+ for i, item := range list {
87
+ if m, ok := item.(map[string]interface{}); ok && m != nil {
88
+ if s := buildWhereRec(m, dialect, args, ph, fmt.Sprintf("%sor%d_", prefix, i)); s != "" {
89
+ subs = append(subs, s)
90
+ }
91
+ }
92
+ }
93
+ if len(subs) > 0 {
94
+ conditions = append(conditions, "("+strings.Join(subs, " OR ")+")")
95
+ }
96
+ continue
97
+ }
98
+ case "AND":
99
+ if list, ok := toSlice(value); ok && len(list) > 0 {
100
+ var subs []string
101
+ for i, item := range list {
102
+ if m, ok := item.(map[string]interface{}); ok && m != nil {
103
+ if s := buildWhereRec(m, dialect, args, ph, fmt.Sprintf("%sand%d_", prefix, i)); s != "" {
104
+ subs = append(subs, s)
105
+ }
106
+ }
107
+ }
108
+ if len(subs) > 0 {
109
+ conditions = append(conditions, "("+strings.Join(subs, " AND ")+")")
110
+ }
111
+ continue
112
+ }
113
+ case "NOT":
114
+ items := value
115
+ if m, ok := value.(map[string]interface{}); ok {
116
+ items = []interface{}{m}
117
+ }
118
+ if list, ok := toSlice(items); ok && len(list) > 0 {
119
+ var subs []string
120
+ for i, item := range list {
121
+ if m, ok := item.(map[string]interface{}); ok && m != nil {
122
+ if s := buildWhereRec(m, dialect, args, ph, fmt.Sprintf("%snot%d_", prefix, i)); s != "" {
123
+ subs = append(subs, s)
124
+ }
125
+ }
126
+ }
127
+ if len(subs) > 0 {
128
+ conditions = append(conditions, "NOT ("+strings.Join(subs, " AND ")+")")
129
+ }
130
+ continue
131
+ }
132
+ }
133
+
134
+ conditions = append(conditions, buildFieldCondition(key, value, dialect, args, ph, prefix)...)
135
+ }
136
+
137
+ return strings.Join(conditions, " AND ")
138
+ }
139
+
140
+ func buildFieldCondition(key string, value interface{}, dialect Dialect, args *[]interface{}, ph func(n int) string, prefix string) []string {
141
+ col := QuoteIdentifier(key, dialect)
142
+
143
+ if value == nil {
144
+ return []string{col + " IS NULL"}
145
+ }
146
+
147
+ m, isMap := isPlainMap(value)
148
+ if !isMap {
149
+ n := len(*args) + 1
150
+ *args = append(*args, value)
151
+ return []string{fmt.Sprintf("%s = %s", col, ph(n))}
152
+ }
153
+
154
+ var parts []string
155
+ addArg := func(val interface{}) string {
156
+ n := len(*args) + 1
157
+ *args = append(*args, val)
158
+ return ph(n)
159
+ }
160
+
161
+ // Determine property order deterministically.
162
+ var opKeys []string
163
+ for k := range m {
164
+ opKeys = append(opKeys, k)
165
+ }
166
+ sort.Strings(opKeys)
167
+
168
+ for _, op := range opKeys {
169
+ val := m[op]
170
+ switch op {
171
+ case "not":
172
+ if val == nil {
173
+ parts = append(parts, col+" IS NOT NULL")
174
+ } else if nested, ok := val.(map[string]interface{}); ok && !isOperatorMap(val) {
175
+ if nestedArgs := len(*args); true {
176
+ nestedCond := buildWhereRec(map[string]interface{}{key: nested}, dialect, args, ph, key+"_not_")
177
+ _ = nestedArgs
178
+ if nestedCond != "" {
179
+ parts = append(parts, "NOT ("+nestedCond+")")
180
+ }
181
+ }
182
+ } else {
183
+ parts = append(parts, fmt.Sprintf("%s <> %s", col, addArg(val)))
184
+ }
185
+ case "equals":
186
+ if val == nil {
187
+ parts = append(parts, col+" IS NULL")
188
+ } else {
189
+ parts = append(parts, fmt.Sprintf("%s = %s", col, addArg(val)))
190
+ }
191
+ case "contains":
192
+ parts = append(parts, fmt.Sprintf("%s LIKE %s", col, addArg("%"+toString(val)+"%")))
193
+ case "startsWith":
194
+ parts = append(parts, fmt.Sprintf("%s LIKE %s", col, addArg(toString(val)+"%")))
195
+ case "endsWith":
196
+ parts = append(parts, fmt.Sprintf("%s LIKE %s", col, addArg("%"+toString(val))))
197
+ case "gte":
198
+ parts = append(parts, fmt.Sprintf("%s >= %s", col, addArg(val)))
199
+ case "lte":
200
+ parts = append(parts, fmt.Sprintf("%s <= %s", col, addArg(val)))
201
+ case "gt":
202
+ parts = append(parts, fmt.Sprintf("%s > %s", col, addArg(val)))
203
+ case "lt":
204
+ parts = append(parts, fmt.Sprintf("%s < %s", col, addArg(val)))
205
+ case "in":
206
+ if list, ok := toSlice(val); ok && len(list) > 0 {
207
+ var phs []string
208
+ for _, item := range list {
209
+ phs = append(phs, addArg(item))
210
+ }
211
+ parts = append(parts, fmt.Sprintf("%s IN (%s)", col, strings.Join(phs, ", ")))
212
+ } else {
213
+ parts = append(parts, "1=0")
214
+ }
215
+ case "notIn":
216
+ if list, ok := toSlice(val); ok && len(list) > 0 {
217
+ var phs []string
218
+ for _, item := range list {
219
+ phs = append(phs, addArg(item))
220
+ }
221
+ parts = append(parts, fmt.Sprintf("%s NOT IN (%s)", col, strings.Join(phs, ", ")))
222
+ } else {
223
+ parts = append(parts, "1=1")
224
+ }
225
+ }
226
+ }
227
+ return parts
228
+ }
229
+
230
+ // toSlice attempts to interpret value as []interface{} (also accepting []string and []int).
231
+ func toSlice(value interface{}) ([]interface{}, bool) {
232
+ switch v := value.(type) {
233
+ case []interface{}:
234
+ return v, true
235
+ case []string:
236
+ out := make([]interface{}, len(v))
237
+ for i, s := range v {
238
+ out[i] = s
239
+ }
240
+ return out, true
241
+ case []int:
242
+ out := make([]interface{}, len(v))
243
+ for i, s := range v {
244
+ out[i] = s
245
+ }
246
+ return out, true
247
+ case []float64:
248
+ out := make([]interface{}, len(v))
249
+ for i, s := range v {
250
+ out[i] = s
251
+ }
252
+ return out, true
253
+ default:
254
+ return nil, false
255
+ }
256
+ }
257
+
258
+ func toString(v interface{}) string {
259
+ if s, ok := v.(string); ok {
260
+ return s
261
+ }
262
+ return fmt.Sprintf("%v", v)
263
+ }
264
+
265
+ // normalizeSortDirection normalizes a direction to ASC or DESC.
266
+ func normalizeSortDirection(dir interface{}) string {
267
+ if s, ok := dir.(string); ok && strings.EqualFold(s, "DESC") {
268
+ return "DESC"
269
+ }
270
+ return "ASC"
271
+ }
272
+
273
+ // BuildOrderBy converts an ORM-style orderBy object (map or list of maps) into a SQL ORDER BY clause.
274
+ // Supports map[string]string, map[string]interface{}, []interface{} and a plain string.
275
+ func BuildOrderBy(orderBy interface{}, dialect Dialect) string {
276
+ var entries []map[string]interface{}
277
+
278
+ switch v := orderBy.(type) {
279
+ case string:
280
+ if strings.TrimSpace(v) == "" {
281
+ return ""
282
+ }
283
+ return "ORDER BY " + v
284
+ case []interface{}:
285
+ for _, item := range v {
286
+ if m, ok := item.(map[string]interface{}); ok {
287
+ entries = append(entries, m)
288
+ }
289
+ }
290
+ case []map[string]interface{}:
291
+ entries = v
292
+ case map[string]interface{}:
293
+ entries = []map[string]interface{}{v}
294
+ default:
295
+ return ""
296
+ }
297
+
298
+ if len(entries) == 0 {
299
+ return ""
300
+ }
301
+ var parts []string
302
+ for _, entry := range entries {
303
+ for _, key := range sortedKeys(entry) {
304
+ parts = append(parts, fmt.Sprintf("%s %s", QuoteIdentifier(key, dialect), normalizeSortDirection(entry[key])))
305
+ }
306
+ }
307
+ if len(parts) == 0 {
308
+ return ""
309
+ }
310
+ return "ORDER BY " + strings.Join(parts, ", ")
311
+ }
package/golang/go.mod ADDED
@@ -0,0 +1,3 @@
1
+ module an5adapters
2
+
3
+ go 1.18
@@ -0,0 +1,32 @@
1
+ // Package mssql provides SQL Server specific dialect execution and query generation for AN5 Go Adapters.
2
+ package mssql
3
+
4
+ import (
5
+ "an5adapters/base"
6
+ "fmt"
7
+ "strings"
8
+ )
9
+
10
+ // BuildVectorSearchQuery constructs native SQL Server VECTOR_DISTANCE query.
11
+ func BuildVectorSearchQuery(table, field, metric string, dim, take int, where string) string {
12
+ if field == "" {
13
+ field = "embedding"
14
+ }
15
+ if metric == "" {
16
+ metric = "cosine"
17
+ }
18
+ tableSql := base.QuoteTable(table, base.DialectMssql)
19
+ fieldSql := base.QuoteIdentifier(field, base.DialectMssql)
20
+
21
+ query := fmt.Sprintf(
22
+ "SELECT TOP (%d) *, VECTOR_DISTANCE('%s', CAST(%s AS VECTOR(%d, float32)), CAST(? AS VECTOR(%d, float32))) AS distance FROM %s WITH (NOLOCK)",
23
+ take, metric, fieldSql, dim, dim, tableSql,
24
+ )
25
+ if strings.TrimSpace(where) != "" {
26
+ query += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", fieldSql, where)
27
+ } else {
28
+ query += fmt.Sprintf(" WHERE %s IS NOT NULL", fieldSql)
29
+ }
30
+ query += " ORDER BY distance ASC"
31
+ return query
32
+ }
@@ -0,0 +1,37 @@
1
+ // Package postgres provides PostgreSQL specific dialect execution and pgvector query generation for AN5 Go Adapters.
2
+ // pgvector operators: <=> (cosine), <-> (euclidean), <#> (dot product).
3
+ package postgres
4
+
5
+ import (
6
+ "an5adapters/base"
7
+ "fmt"
8
+ "strings"
9
+ )
10
+
11
+ // BuildVectorSearchQuery constructs native PostgreSQL pgvector query.
12
+ func BuildVectorSearchQuery(table, field, metric string, dim, take int, where string) string {
13
+ if field == "" {
14
+ field = "embedding"
15
+ }
16
+ if metric == "" {
17
+ metric = "cosine"
18
+ }
19
+ tableSql := base.QuoteTable(table, base.DialectPostgres)
20
+ fieldSql := base.QuoteIdentifier(field, base.DialectPostgres)
21
+
22
+ op := "<=>"
23
+ if strings.EqualFold(metric, "euclidean") {
24
+ op = "<->"
25
+ } else if strings.EqualFold(metric, "dot") {
26
+ op = "<#>"
27
+ }
28
+
29
+ query := fmt.Sprintf("SELECT *, (%s %s $1::vector) AS distance FROM %s", fieldSql, op, tableSql)
30
+ if strings.TrimSpace(where) != "" {
31
+ query += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", fieldSql, where)
32
+ } else {
33
+ query += fmt.Sprintf(" WHERE %s IS NOT NULL", fieldSql)
34
+ }
35
+ query += fmt.Sprintf(" ORDER BY distance ASC LIMIT %d", take)
36
+ return query
37
+ }
@@ -0,0 +1,14 @@
1
+ // Package sqlite provides SQLite specific dialect execution and query generation for AN5 Go Adapters.
2
+ package sqlite
3
+
4
+ import (
5
+ "an5adapters/base"
6
+ )
7
+
8
+ // BuildVectorSearchQuery returns empty string as SQLite uses in-memory vector fallback by default.
9
+ func BuildVectorSearchQuery(table, field, metric string, dim, take int, where string) string {
10
+ _ = base.QuoteTable(table, base.DialectSqlite)
11
+ _ = base.QuoteIdentifier(field, base.DialectSqlite)
12
+ // Native vector extension not present in standard sqlite; in-memory fallback is used.
13
+ return ""
14
+ }
@@ -0,0 +1,42 @@
1
+ package an5adapters_test
2
+
3
+ import (
4
+ "testing"
5
+
6
+ an5adapters "an5adapters"
7
+ "an5adapters/base"
8
+ )
9
+
10
+ func TestSqliteDialectDetection(t *testing.T) {
11
+ tests := []struct {
12
+ connStr string
13
+ expected base.Dialect
14
+ }{
15
+ {"sqlite://data/test.db", base.DialectSqlite},
16
+ {"sqlite:memory:", base.DialectSqlite},
17
+ {"file:mydb.sqlite3", base.DialectSqlite},
18
+ {"app.db", base.DialectSqlite},
19
+ {":memory:", base.DialectSqlite},
20
+ {"postgres://user:pass@localhost:5432/db", base.DialectPostgres},
21
+ {"Server=localhost;Database=master;", base.DialectMssql},
22
+ }
23
+
24
+ for _, tt := range tests {
25
+ got := an5adapters.DetectDialect(tt.connStr)
26
+ if got != tt.expected {
27
+ t.Errorf("DetectDialect(%q) = %v, want %v", tt.connStr, got, tt.expected)
28
+ }
29
+ }
30
+ }
31
+
32
+ func TestSqliteQuoting(t *testing.T) {
33
+ id := an5adapters.QuoteIdentifier("user_name", base.DialectSqlite)
34
+ if id != `"user_name"` {
35
+ t.Errorf("QuoteIdentifier() = %v, want %v", id, `"user_name"`)
36
+ }
37
+
38
+ tbl := an5adapters.QuoteTable("users", base.DialectSqlite)
39
+ if tbl != `"users"` {
40
+ t.Errorf("QuoteTable() = %v, want %v", tbl, `"users"`)
41
+ }
42
+ }