@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,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,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
|
+
}
|