@an5/orm 1.3.1 → 1.3.2
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.
|
@@ -968,9 +968,22 @@ namespace An5Orm
|
|
|
968
968
|
{
|
|
969
969
|
try
|
|
970
970
|
{
|
|
971
|
-
if (rawVal is
|
|
971
|
+
if (rawVal is List<double> dblList)
|
|
972
972
|
{
|
|
973
|
-
rowVector =
|
|
973
|
+
rowVector = dblList;
|
|
974
|
+
}
|
|
975
|
+
else
|
|
976
|
+
{
|
|
977
|
+
var decoded = DecodeVectorColumn(rawVal);
|
|
978
|
+
if (decoded != null)
|
|
979
|
+
{
|
|
980
|
+
rowVector = new List<double>(decoded.Length);
|
|
981
|
+
for (int i = 0; i < decoded.Length; i++) rowVector.Add(decoded[i]);
|
|
982
|
+
}
|
|
983
|
+
else if (rawVal is string jsonStr)
|
|
984
|
+
{
|
|
985
|
+
rowVector = ParseDoubleArray(jsonStr);
|
|
986
|
+
}
|
|
974
987
|
}
|
|
975
988
|
}
|
|
976
989
|
catch { }
|
|
@@ -978,8 +991,31 @@ namespace An5Orm
|
|
|
978
991
|
|
|
979
992
|
if (rowVector != null && rowVector.Count == vector.Count)
|
|
980
993
|
{
|
|
981
|
-
double
|
|
982
|
-
|
|
994
|
+
double distance;
|
|
995
|
+
if (distanceMetric.Equals("euclidean", StringComparison.OrdinalIgnoreCase) || distanceMetric.Equals("l2", StringComparison.OrdinalIgnoreCase))
|
|
996
|
+
{
|
|
997
|
+
double sum = 0.0;
|
|
998
|
+
for (int i = 0; i < vector.Count; i++)
|
|
999
|
+
{
|
|
1000
|
+
double diff = vector[i] - rowVector[i];
|
|
1001
|
+
sum += diff * diff;
|
|
1002
|
+
}
|
|
1003
|
+
distance = Math.Sqrt(sum);
|
|
1004
|
+
}
|
|
1005
|
+
else if (distanceMetric.Equals("dot", StringComparison.OrdinalIgnoreCase) || distanceMetric.Equals("inner_product", StringComparison.OrdinalIgnoreCase) || distanceMetric.Equals("ip", StringComparison.OrdinalIgnoreCase))
|
|
1006
|
+
{
|
|
1007
|
+
double dot = 0.0;
|
|
1008
|
+
for (int i = 0; i < vector.Count; i++)
|
|
1009
|
+
{
|
|
1010
|
+
dot += vector[i] * rowVector[i];
|
|
1011
|
+
}
|
|
1012
|
+
distance = -dot;
|
|
1013
|
+
}
|
|
1014
|
+
else
|
|
1015
|
+
{
|
|
1016
|
+
double sim = CosineSimilarity(vector, rowVector);
|
|
1017
|
+
distance = 1.0 - sim;
|
|
1018
|
+
}
|
|
983
1019
|
results.Add(Tuple.Create(row, distance));
|
|
984
1020
|
}
|
|
985
1021
|
}
|
|
@@ -1157,37 +1157,39 @@ func (c *TableClient[T]) VectorSearch(ctx context.Context, vector []float64, opt
|
|
|
1157
1157
|
\tvecStr := string(vecBytes)
|
|
1158
1158
|
|
|
1159
1159
|
\t// 1. Primary path: dialect-aware native SQL vector query
|
|
1160
|
-
\
|
|
1161
|
-
\
|
|
1162
|
-
\
|
|
1163
|
-
\t\
|
|
1164
|
-
\t\
|
|
1165
|
-
\t\tif strings.EqualFold(distanceMetric, "
|
|
1166
|
-
\t\
|
|
1167
|
-
\t\
|
|
1168
|
-
\t\
|
|
1169
|
-
\t\t\
|
|
1170
|
-
\t\t
|
|
1171
|
-
\t\t\
|
|
1172
|
-
\t\t
|
|
1173
|
-
\t\
|
|
1174
|
-
\t
|
|
1175
|
-
\t\tvcol := c.quoteName(vectorField)
|
|
1176
|
-
\t\tsqlQuery = fmt.Sprintf(
|
|
1177
|
-
\t\t\t"SELECT TOP (%d) *, VECTOR_DISTANCE('%s', CAST(%s AS VECTOR(%d, float32)), CAST(? AS VECTOR(%d, float32))) AS distance FROM %s WITH (NOLOCK)",
|
|
1178
|
-
\t\t\ttake, distanceMetric, vcol, dim, dim, c.quoteTable(),
|
|
1179
|
-
\t\t)
|
|
1180
|
-
\t\tif whereCl != "" {
|
|
1181
|
-
\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", vcol, whereCl)
|
|
1160
|
+
\tif c.Dialect != DialectSqlite {
|
|
1161
|
+
\t\tvar sqlQuery string
|
|
1162
|
+
\t\tqueryArgs := append([]interface{}{vecStr}, whereQueryArgs...)
|
|
1163
|
+
\t\tif c.Dialect == DialectPostgres {
|
|
1164
|
+
\t\t\top := "<=>"
|
|
1165
|
+
\t\t\tif strings.EqualFold(distanceMetric, "euclidean") { op = "<->" }
|
|
1166
|
+
\t\t\tif strings.EqualFold(distanceMetric, "dot") { op = "<#>" }
|
|
1167
|
+
\t\t\tvcol := c.quoteName(vectorField)
|
|
1168
|
+
\t\t\tsqlQuery = fmt.Sprintf("SELECT *, (%s %s $1::vector) AS distance FROM %s", vcol, op, c.quoteTable())
|
|
1169
|
+
\t\t\tif whereCl != "" {
|
|
1170
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", vcol, whereCl)
|
|
1171
|
+
\t\t\t} else {
|
|
1172
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL", vcol)
|
|
1173
|
+
\t\t\t}
|
|
1174
|
+
\t\t\tsqlQuery += fmt.Sprintf(" ORDER BY distance ASC LIMIT %d", take)
|
|
1182
1175
|
\t\t} else {
|
|
1183
|
-
\t\t\
|
|
1176
|
+
\t\t\tvcol := c.quoteName(vectorField)
|
|
1177
|
+
\t\t\tsqlQuery = fmt.Sprintf(
|
|
1178
|
+
\t\t\t\t"SELECT TOP (%d) *, VECTOR_DISTANCE('%s', CAST(%s AS VECTOR(%d, float32)), CAST(? AS VECTOR(%d, float32))) AS distance FROM %s WITH (NOLOCK)",
|
|
1179
|
+
\t\t\t\ttake, distanceMetric, vcol, dim, dim, c.quoteTable(),
|
|
1180
|
+
\t\t\t)
|
|
1181
|
+
\t\t\tif whereCl != "" {
|
|
1182
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", vcol, whereCl)
|
|
1183
|
+
\t\t\t} else {
|
|
1184
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL", vcol)
|
|
1185
|
+
\t\t\t}
|
|
1186
|
+
\t\t\tsqlQuery += " ORDER BY distance ASC"
|
|
1184
1187
|
\t\t}
|
|
1185
|
-
\t\tsqlQuery += " ORDER BY distance ASC"
|
|
1186
|
-
\t}
|
|
1187
1188
|
|
|
1188
|
-
\tnativeRows, nativeErr := c.queryAndScan(ctx, sqlQuery, queryArgs...)
|
|
1189
|
-
\tif nativeErr == nil {
|
|
1190
|
-
\t\treturn nativeRows, nil
|
|
1189
|
+
\t\tnativeRows, nativeErr := c.queryAndScan(ctx, sqlQuery, queryArgs...)
|
|
1190
|
+
\t\tif nativeErr == nil {
|
|
1191
|
+
\t\t\treturn nativeRows, nil
|
|
1192
|
+
\t\t}
|
|
1191
1193
|
\t}
|
|
1192
1194
|
|
|
1193
1195
|
\t// 2. Fallback: in-memory similarity
|
|
@@ -986,9 +986,22 @@ namespace An5Orm
|
|
|
986
986
|
{
|
|
987
987
|
try
|
|
988
988
|
{
|
|
989
|
-
if (rawVal is
|
|
989
|
+
if (rawVal is List<double> dblList)
|
|
990
990
|
{
|
|
991
|
-
rowVector =
|
|
991
|
+
rowVector = dblList;
|
|
992
|
+
}
|
|
993
|
+
else
|
|
994
|
+
{
|
|
995
|
+
var decoded = DecodeVectorColumn(rawVal);
|
|
996
|
+
if (decoded != null)
|
|
997
|
+
{
|
|
998
|
+
rowVector = new List<double>(decoded.Length);
|
|
999
|
+
for (int i = 0; i < decoded.Length; i++) rowVector.Add(decoded[i]);
|
|
1000
|
+
}
|
|
1001
|
+
else if (rawVal is string jsonStr)
|
|
1002
|
+
{
|
|
1003
|
+
rowVector = ParseDoubleArray(jsonStr);
|
|
1004
|
+
}
|
|
992
1005
|
}
|
|
993
1006
|
}
|
|
994
1007
|
catch { }
|
|
@@ -996,8 +1009,31 @@ namespace An5Orm
|
|
|
996
1009
|
|
|
997
1010
|
if (rowVector != null && rowVector.Count == vector.Count)
|
|
998
1011
|
{
|
|
999
|
-
double
|
|
1000
|
-
|
|
1012
|
+
double distance;
|
|
1013
|
+
if (distanceMetric.Equals("euclidean", StringComparison.OrdinalIgnoreCase) || distanceMetric.Equals("l2", StringComparison.OrdinalIgnoreCase))
|
|
1014
|
+
{
|
|
1015
|
+
double sum = 0.0;
|
|
1016
|
+
for (int i = 0; i < vector.Count; i++)
|
|
1017
|
+
{
|
|
1018
|
+
double diff = vector[i] - rowVector[i];
|
|
1019
|
+
sum += diff * diff;
|
|
1020
|
+
}
|
|
1021
|
+
distance = Math.Sqrt(sum);
|
|
1022
|
+
}
|
|
1023
|
+
else if (distanceMetric.Equals("dot", StringComparison.OrdinalIgnoreCase) || distanceMetric.Equals("inner_product", StringComparison.OrdinalIgnoreCase) || distanceMetric.Equals("ip", StringComparison.OrdinalIgnoreCase))
|
|
1024
|
+
{
|
|
1025
|
+
double dot = 0.0;
|
|
1026
|
+
for (int i = 0; i < vector.Count; i++)
|
|
1027
|
+
{
|
|
1028
|
+
dot += vector[i] * rowVector[i];
|
|
1029
|
+
}
|
|
1030
|
+
distance = -dot;
|
|
1031
|
+
}
|
|
1032
|
+
else
|
|
1033
|
+
{
|
|
1034
|
+
double sim = CosineSimilarity(vector, rowVector);
|
|
1035
|
+
distance = 1.0 - sim;
|
|
1036
|
+
}
|
|
1001
1037
|
results.Add(Tuple.Create(row, distance));
|
|
1002
1038
|
}
|
|
1003
1039
|
}
|
|
@@ -1180,37 +1180,39 @@ func (c *TableClient[T]) VectorSearch(ctx context.Context, vector []float64, opt
|
|
|
1180
1180
|
\tvecStr := string(vecBytes)
|
|
1181
1181
|
|
|
1182
1182
|
\t// 1. Primary path: dialect-aware native SQL vector query
|
|
1183
|
-
\
|
|
1184
|
-
\
|
|
1185
|
-
\
|
|
1186
|
-
\t\
|
|
1187
|
-
\t\
|
|
1188
|
-
\t\tif strings.EqualFold(distanceMetric, "
|
|
1189
|
-
\t\
|
|
1190
|
-
\t\
|
|
1191
|
-
\t\
|
|
1192
|
-
\t\t\
|
|
1193
|
-
\t\t
|
|
1194
|
-
\t\t\
|
|
1195
|
-
\t\t
|
|
1196
|
-
\t\
|
|
1197
|
-
\t
|
|
1198
|
-
\t\tvcol := c.quoteName(vectorField)
|
|
1199
|
-
\t\tsqlQuery = fmt.Sprintf(
|
|
1200
|
-
\t\t\t"SELECT TOP (%d) *, VECTOR_DISTANCE('%s', CAST(%s AS VECTOR(%d, float32)), CAST(? AS VECTOR(%d, float32))) AS distance FROM %s WITH (NOLOCK)",
|
|
1201
|
-
\t\t\ttake, distanceMetric, vcol, dim, dim, c.quoteTable(),
|
|
1202
|
-
\t\t)
|
|
1203
|
-
\t\tif whereCl != "" {
|
|
1204
|
-
\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", vcol, whereCl)
|
|
1183
|
+
\tif c.Dialect != DialectSqlite {
|
|
1184
|
+
\t\tvar sqlQuery string
|
|
1185
|
+
\t\tqueryArgs := append([]interface{}{vecStr}, whereQueryArgs...)
|
|
1186
|
+
\t\tif c.Dialect == DialectPostgres {
|
|
1187
|
+
\t\t\top := "<=>"
|
|
1188
|
+
\t\t\tif strings.EqualFold(distanceMetric, "euclidean") { op = "<->" }
|
|
1189
|
+
\t\t\tif strings.EqualFold(distanceMetric, "dot") { op = "<#>" }
|
|
1190
|
+
\t\t\tvcol := c.quoteName(vectorField)
|
|
1191
|
+
\t\t\tsqlQuery = fmt.Sprintf("SELECT *, (%s %s $1::vector) AS distance FROM %s", vcol, op, c.quoteTable())
|
|
1192
|
+
\t\t\tif whereCl != "" {
|
|
1193
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", vcol, whereCl)
|
|
1194
|
+
\t\t\t} else {
|
|
1195
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL", vcol)
|
|
1196
|
+
\t\t\t}
|
|
1197
|
+
\t\t\tsqlQuery += fmt.Sprintf(" ORDER BY distance ASC LIMIT %d", take)
|
|
1205
1198
|
\t\t} else {
|
|
1206
|
-
\t\t\
|
|
1199
|
+
\t\t\tvcol := c.quoteName(vectorField)
|
|
1200
|
+
\t\t\tsqlQuery = fmt.Sprintf(
|
|
1201
|
+
\t\t\t\t"SELECT TOP (%d) *, VECTOR_DISTANCE('%s', CAST(%s AS VECTOR(%d, float32)), CAST(? AS VECTOR(%d, float32))) AS distance FROM %s WITH (NOLOCK)",
|
|
1202
|
+
\t\t\t\ttake, distanceMetric, vcol, dim, dim, c.quoteTable(),
|
|
1203
|
+
\t\t\t)
|
|
1204
|
+
\t\t\tif whereCl != "" {
|
|
1205
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL AND (%s)", vcol, whereCl)
|
|
1206
|
+
\t\t\t} else {
|
|
1207
|
+
\t\t\t\tsqlQuery += fmt.Sprintf(" WHERE %s IS NOT NULL", vcol)
|
|
1208
|
+
\t\t\t}
|
|
1209
|
+
\t\t\tsqlQuery += " ORDER BY distance ASC"
|
|
1207
1210
|
\t\t}
|
|
1208
|
-
\t\tsqlQuery += " ORDER BY distance ASC"
|
|
1209
|
-
\t}
|
|
1210
1211
|
|
|
1211
|
-
\tnativeRows, nativeErr := c.queryAndScan(ctx, sqlQuery, queryArgs...)
|
|
1212
|
-
\tif nativeErr == nil {
|
|
1213
|
-
\t\treturn nativeRows, nil
|
|
1212
|
+
\t\tnativeRows, nativeErr := c.queryAndScan(ctx, sqlQuery, queryArgs...)
|
|
1213
|
+
\t\tif nativeErr == nil {
|
|
1214
|
+
\t\t\treturn nativeRows, nil
|
|
1215
|
+
\t\t}
|
|
1214
1216
|
\t}
|
|
1215
1217
|
|
|
1216
1218
|
\t// 2. Fallback: in-memory similarity
|
package/package.json
CHANGED