@@ -33,18 +33,31 @@ public SqlServerClient(SqlConnection connection, string schema)
33
33
this . _schema = schema ;
34
34
}
35
35
36
+ private async Task < bool > HasJsonNativeTypeAsync ( CancellationToken cancellationToken = default )
37
+ {
38
+ using ( await this . OpenConnectionAsync ( cancellationToken ) . ConfigureAwait ( false ) )
39
+ {
40
+ using var cmd = this . _connection . CreateCommand ( ) ;
41
+ cmd . CommandText = "select [name] from sys.types where system_type_id = 244 and user_type_id = 244" ;
42
+ var typeName = ( string ) await cmd . ExecuteScalarAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
43
+ return string . Equals ( typeName , "json" , StringComparison . OrdinalIgnoreCase ) ;
44
+ }
45
+ }
46
+
36
47
/// <inheritdoc/>
37
48
public async Task CreateTableAsync ( string tableName , CancellationToken cancellationToken = default )
38
49
{
39
50
var fullTableName = this . GetSanitizedFullTableName ( tableName ) ;
51
+ var metadataType = await this . HasJsonNativeTypeAsync ( cancellationToken ) . ConfigureAwait ( false ) ? "json" : "nvarchar(max)" ;
52
+
40
53
using ( await this . OpenConnectionAsync ( cancellationToken ) . ConfigureAwait ( false ) )
41
54
{
42
55
using var cmd = this . _connection . CreateCommand ( ) ;
43
56
cmd . CommandText = $ """
44
57
IF OBJECT_ID(N'{ fullTableName } ', N'U') IS NULL
45
58
CREATE TABLE { fullTableName } (
46
59
[key] nvarchar(255) collate latin1_general_bin2 not null,
47
- [metadata] nvarchar(max) not null,
60
+ [metadata] { metadataType } not null,
48
61
[embedding] varbinary(8000),
49
62
[timestamp] datetimeoffset,
50
63
PRIMARY KEY NONCLUSTERED ([key]),
@@ -138,9 +151,9 @@ WHEN NOT MATCHED THEN
138
151
/// <inheritdoc/>
139
152
public async IAsyncEnumerable < SqlServerMemoryEntry > ReadBatchAsync ( string tableName , IEnumerable < string > keys , bool withEmbeddings = false , [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
140
153
{
141
- var queryColumns = withEmbeddings
142
- ? "[key], [metadata], [timestamp], VECTOR_TO_JSON_ARRAY([embedding]) AS [embedding]"
143
- : "[key], [metadata], [timestamp]" ;
154
+ var queryColumns = "[key], [metadata], [timestamp]" +
155
+ ( withEmbeddings ? ", VECTOR_TO_JSON_ARRAY([embedding]) AS [embedding]" : string . Empty ) ;
156
+
144
157
var fullTableName = this . GetSanitizedFullTableName ( tableName ) ;
145
158
var keysList = keys . ToList ( ) ;
146
159
var keysParams = string . Join ( ", " , keysList . Select ( ( _ , i ) => $ "@k{ i } ") ) ;
@@ -189,9 +202,8 @@ WHERE [key] IN ({keysParams})
189
202
/// <inheritdoc/>
190
203
public async IAsyncEnumerable < ( SqlServerMemoryEntry , double ) > GetNearestMatchesAsync ( string tableName , ReadOnlyMemory < float > embedding , int limit , double minRelevanceScore = 0 , bool withEmbeddings = false , [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
191
204
{
192
- var queryColumns = withEmbeddings
193
- ? "[key], [metadata], [timestamp], 1 - VECTOR_DISTANCE('cosine', [embedding], JSON_ARRAY_TO_VECTOR(@e)) AS [cosine_similarity], VECTOR_TO_JSON_ARRAY([embedding]) AS [embedding]"
194
- : "[key], [metadata], [timestamp], 1 - VECTOR_DISTANCE('cosine', [embedding], JSON_ARRAY_TO_VECTOR(@e)) AS [cosine_similarity]" ;
205
+ var queryColumns = "[key], [metadata], [timestamp], 1 - VECTOR_DISTANCE('cosine', [embedding], JSON_ARRAY_TO_VECTOR(@e)) AS [cosine_similarity]" +
206
+ ( withEmbeddings ? ", VECTOR_TO_JSON_ARRAY([embedding]) AS [embedding]" : string . Empty ) ;
195
207
var fullTableName = this . GetSanitizedFullTableName ( tableName ) ;
196
208
using ( await this . OpenConnectionAsync ( cancellationToken ) . ConfigureAwait ( false ) )
197
209
{
@@ -221,6 +233,7 @@ ORDER BY [cosine_similarity] DESC
221
233
private string GetSanitizedFullTableName ( string tableName ) => $ "{ DelimitIdentifier ( this . _schema ) } .{ DelimitIdentifier ( tableName ) } ";
222
234
223
235
private string SerializeEmbedding ( ReadOnlyMemory < float > embedding ) => JsonSerializer . Serialize ( embedding ) ;
236
+
224
237
private ReadOnlyMemory < float > DeserializeEmbedding ( string embedding ) => JsonSerializer . Deserialize < ReadOnlyMemory < float > > ( embedding ) ;
225
238
226
239
private SqlServerMemoryEntry ReadEntry ( SqlDataReader reader , bool hasEmbedding )
@@ -247,6 +260,7 @@ private async Task<IDisposable> OpenConnectionAsync(CancellationToken cancellati
247
260
}
248
261
249
262
private static string DelimitIdentifier ( string identifier ) => $ "[{ EscapeIdentifier ( identifier ) } ]";
263
+
250
264
private static string EscapeIdentifier ( string identifier ) => identifier . Replace ( "]" , "]]" ) ;
251
265
252
266
private readonly struct Closer ( SqlServerClient client , bool shouldClose ) : IDisposable
0 commit comments