Skip to content

Commit e1f426a

Browse files
authored
🐛 add missing method overloads for Upsert/Update (#184)
1 parent c6e1987 commit e1f426a

9 files changed

+266
-9
lines changed

SurrealDb.Embedded.Internals/SurrealDbEmbeddedEngine.cs

+30
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,21 @@ CancellationToken cancellationToken
857857
.ConfigureAwait(false);
858858
}
859859

860+
public async Task<IEnumerable<TOutput>> Update<TData, TOutput>(
861+
string table,
862+
TData data,
863+
CancellationToken cancellationToken
864+
)
865+
where TOutput : IRecord
866+
{
867+
return await SendRequestAsync<IEnumerable<TOutput>>(
868+
Method.Update,
869+
[table, data],
870+
cancellationToken
871+
)
872+
.ConfigureAwait(false);
873+
}
874+
860875
public async Task<T> Upsert<T>(T data, CancellationToken cancellationToken)
861876
where T : IRecord
862877
{
@@ -893,6 +908,21 @@ CancellationToken cancellationToken
893908
.ConfigureAwait(false);
894909
}
895910

911+
public async Task<IEnumerable<TOutput>> Upsert<TData, TOutput>(
912+
string table,
913+
TData data,
914+
CancellationToken cancellationToken
915+
)
916+
where TOutput : IRecord
917+
{
918+
return await SendRequestAsync<IEnumerable<TOutput>>(
919+
Method.Upsert,
920+
[table, data],
921+
cancellationToken
922+
)
923+
.ConfigureAwait(false);
924+
}
925+
896926
public async Task<TOutput> Upsert<TData, TOutput>(
897927
RecordId recordId,
898928
TData data,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Semver;
2+
3+
namespace SurrealDb.Net.Tests.Fixtures;
4+
5+
public sealed class SinceSurrealVersionAttribute(string version)
6+
: SkipAttribute($"This test is only supported with Surreal v{version} or later.")
7+
{
8+
public override async Task<bool> ShouldSkip(BeforeTestContext context)
9+
{
10+
var expectedVersion = SemVersion.Parse(version, SemVersionStyles.Any);
11+
12+
int index = context
13+
.TestDetails.MethodInfo.GetParameters()
14+
.Index()
15+
.First(x => x.Item.Name == "connectionString")
16+
.Index;
17+
18+
var connectionString = context.TestDetails.TestMethodArguments[index] as string;
19+
var currentVersion = await SurrealDbClientGenerator.GetSurrealTestVersion(
20+
connectionString!
21+
);
22+
23+
bool shouldExecute = SemVersion.CompareSortOrder(currentVersion, expectedVersion) >= 0;
24+
return !shouldExecute;
25+
}
26+
}

SurrealDb.Net.Tests/SelectTests.cs

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ public class Post : SurrealDbRecord
1717
public string? Status { get; set; }
1818
}
1919

20+
public class Person : SurrealDbRecord
21+
{
22+
public string Title { get; set; } = string.Empty;
23+
public PersonName Name { get; set; }
24+
public bool Marketing { get; set; }
25+
}
26+
27+
public struct PersonName
28+
{
29+
public string FirstName { get; set; }
30+
public string LastName { get; set; }
31+
}
32+
2033
public class ObjectTableId
2134
{
2235
[CborProperty("location")]

SurrealDb.Net.Tests/UpsertTests.cs

+47-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System.Text;
2-
3-
namespace SurrealDb.Net.Tests;
1+
namespace SurrealDb.Net.Tests;
42

53
public class UpsertTests
64
{
@@ -16,7 +14,7 @@ public async Task ShouldCreateNewPost(string connectionString)
1614
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
1715
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
1816

19-
using var client = surrealDbClientGenerator.Create(connectionString);
17+
await using var client = surrealDbClientGenerator.Create(connectionString);
2018
await client.Use(dbInfo.Namespace, dbInfo.Database);
2119

2220
await client.ApplySchemaAsync(SurrealSchemaFile.Post);
@@ -64,7 +62,7 @@ public async Task ShouldUpdateExistingPost(string connectionString)
6462
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
6563
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
6664

67-
using var client = surrealDbClientGenerator.Create(connectionString);
65+
await using var client = surrealDbClientGenerator.Create(connectionString);
6866
await client.Use(dbInfo.Namespace, dbInfo.Database);
6967

7068
await client.ApplySchemaAsync(SurrealSchemaFile.Post);
@@ -109,7 +107,7 @@ public async Task ShouldCreateNewPostUsingStringRecordId(string connectionString
109107
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
110108
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
111109

112-
using var client = surrealDbClientGenerator.Create(connectionString);
110+
await using var client = surrealDbClientGenerator.Create(connectionString);
113111
await client.Use(dbInfo.Namespace, dbInfo.Database);
114112

115113
await client.ApplySchemaAsync(SurrealSchemaFile.Post);
@@ -156,7 +154,7 @@ public async Task ShouldUpdateExistingPostUsingStringRecordId(string connectionS
156154
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
157155
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
158156

159-
using var client = surrealDbClientGenerator.Create(connectionString);
157+
await using var client = surrealDbClientGenerator.Create(connectionString);
160158
await client.Use(dbInfo.Namespace, dbInfo.Database);
161159

162160
await client.ApplySchemaAsync(SurrealSchemaFile.Post);
@@ -187,4 +185,46 @@ public async Task ShouldUpdateExistingPostUsingStringRecordId(string connectionS
187185
result!.CreatedAt.Should().NotBeNull();
188186
result!.Status.Should().Be("DRAFT");
189187
}
188+
189+
[Test]
190+
[ConnectionStringFixtureGenerator]
191+
[SinceSurrealVersion("2.1")]
192+
public async Task ShouldCreatePersonUsingADictionary(string connectionString)
193+
{
194+
Person? result = null;
195+
196+
Func<Task> func = async () =>
197+
{
198+
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
199+
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
200+
201+
await using var client = surrealDbClientGenerator.Create(connectionString);
202+
await client.Use(dbInfo.Namespace, dbInfo.Database);
203+
204+
var person = new Dictionary<string, object>
205+
{
206+
{ "title", "Mr." },
207+
{
208+
"name",
209+
new Dictionary<string, object>
210+
{
211+
{ "first_name", "Jaime" },
212+
{ "last_name", "Lannister" },
213+
}
214+
},
215+
{ "marketing", false },
216+
};
217+
218+
var list = await client.Upsert<Dictionary<string, object>, Person>("person", person);
219+
result = list.Single();
220+
};
221+
222+
await func.Should().NotThrowAsync();
223+
224+
result.Should().NotBeNull();
225+
result!.Title.Should().Be("Mr.");
226+
result!.Name.FirstName.Should().Be("Jaime");
227+
result!.Name.LastName.Should().Be("Lannister");
228+
result!.Marketing.Should().BeFalse();
229+
}
190230
}

SurrealDb.Net/Internals/SurrealDbEngine.Http.cs

+31
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,20 @@ CancellationToken cancellationToken
791791
return dbResponse.DeserializeEnumerable<T>();
792792
}
793793

794+
public async Task<IEnumerable<TOutput>> Update<TData, TOutput>(
795+
string table,
796+
TData data,
797+
CancellationToken cancellationToken
798+
)
799+
where TOutput : IRecord
800+
{
801+
var request = new SurrealDbHttpRequest { Method = "update", Parameters = [table, data] };
802+
803+
var dbResponse = await ExecuteRequestAsync(request, cancellationToken)
804+
.ConfigureAwait(false);
805+
return dbResponse.DeserializeEnumerable<TOutput>();
806+
}
807+
794808
public async Task<TOutput> Update<TData, TOutput>(
795809
RecordId recordId,
796810
TData data,
@@ -860,6 +874,23 @@ CancellationToken cancellationToken
860874
return dbResponse.DeserializeEnumerable<T>();
861875
}
862876

877+
public async Task<IEnumerable<TOutput>> Upsert<TData, TOutput>(
878+
string table,
879+
TData data,
880+
CancellationToken cancellationToken
881+
)
882+
where TOutput : IRecord
883+
{
884+
await EnsureVersionIsSetAsync(cancellationToken).ConfigureAwait(false);
885+
886+
string method = _version?.Major > 1 ? "upsert" : "update";
887+
var request = new SurrealDbHttpRequest { Method = method, Parameters = [table, data] };
888+
889+
var dbResponse = await ExecuteRequestAsync(request, cancellationToken)
890+
.ConfigureAwait(false);
891+
return dbResponse.DeserializeEnumerable<TOutput>();
892+
}
893+
863894
public async Task<TOutput> Upsert<TData, TOutput>(
864895
RecordId recordId,
865896
TData data,

SurrealDb.Net/Internals/SurrealDbEngine.Interface.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,13 @@ CancellationToken cancellationToken
150150
where TOutput : IRecord;
151151
Task<IEnumerable<T>> Update<T>(string table, T data, CancellationToken cancellationToken)
152152
where T : class;
153+
154+
Task<IEnumerable<TOutput>> Update<TData, TOutput>(
155+
string table,
156+
TData data,
157+
CancellationToken cancellationToken
158+
)
159+
where TOutput : IRecord;
153160
Task<TOutput> Update<TData, TOutput>(
154161
RecordId recordId,
155162
TData data,
@@ -166,6 +173,12 @@ CancellationToken cancellationToken
166173
where TOutput : IRecord;
167174
Task<IEnumerable<T>> Upsert<T>(string table, T data, CancellationToken cancellationToken)
168175
where T : class;
176+
Task<IEnumerable<TOutput>> Upsert<TData, TOutput>(
177+
string table,
178+
TData data,
179+
CancellationToken cancellationToken
180+
)
181+
where TOutput : IRecord;
169182
Task<TOutput> Upsert<TData, TOutput>(
170183
RecordId recordId,
171184
TData data,
@@ -203,7 +216,7 @@ void Initialize(
203216
/// </remarks>
204217
/// <param name="input"></param>
205218
/// <param name="cancellationToken">The cancellationToken enables graceful cancellation of asynchronous operations</param>
206-
Task Import(string input, CancellationToken cancellationToken = default);
219+
Task Import(string input, CancellationToken cancellationToken);
207220
}
208221

209222
public interface ISurrealDbInMemoryEngine : ISurrealDbProviderEngine { }

SurrealDb.Net/Internals/SurrealDbEngine.Ws.cs

+37
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,23 @@ CancellationToken cancellationToken
10881088
return dbResponse.DeserializeEnumerable<T>();
10891089
}
10901090

1091+
public async Task<IEnumerable<TOutput>> Update<TData, TOutput>(
1092+
string table,
1093+
TData data,
1094+
CancellationToken cancellationToken
1095+
)
1096+
where TOutput : IRecord
1097+
{
1098+
var dbResponse = await SendRequestAsync(
1099+
"update",
1100+
[table, data],
1101+
SurrealDbWsRequestPriority.Normal,
1102+
cancellationToken
1103+
)
1104+
.ConfigureAwait(false);
1105+
return dbResponse.DeserializeEnumerable<TOutput>();
1106+
}
1107+
10911108
public async Task<TOutput> Update<TData, TOutput>(
10921109
RecordId recordId,
10931110
TData data,
@@ -1169,6 +1186,26 @@ CancellationToken cancellationToken
11691186
return dbResponse.DeserializeEnumerable<T>();
11701187
}
11711188

1189+
public async Task<IEnumerable<TOutput>> Upsert<TData, TOutput>(
1190+
string table,
1191+
TData data,
1192+
CancellationToken cancellationToken
1193+
)
1194+
where TOutput : IRecord
1195+
{
1196+
await EnsureVersionIsSetAsync(cancellationToken).ConfigureAwait(false);
1197+
1198+
string method = _version?.Major > 1 ? "upsert" : "update";
1199+
var dbResponse = await SendRequestAsync(
1200+
method,
1201+
[table, data],
1202+
SurrealDbWsRequestPriority.Normal,
1203+
cancellationToken
1204+
)
1205+
.ConfigureAwait(false);
1206+
return dbResponse.DeserializeEnumerable<TOutput>();
1207+
}
1208+
11721209
public async Task<TOutput> Upsert<TData, TOutput>(
11731210
RecordId recordId,
11741211
TData data,

SurrealDb.Net/SurrealDbClient.Interface.cs

+48-1
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,29 @@ Task<IEnumerable<T>> Update<T>(
908908
)
909909
where T : class;
910910

911+
/// <summary>
912+
/// Updates all records in the database.<br />
913+
/// <see href="https://surrealdb.com/docs/sdk/dotnet/methods/update">
914+
/// `Update` on surrealdb.com/docs
915+
/// </see>
916+
/// </summary>
917+
/// <typeparam name="TData">The type of data contained in the record.</typeparam>
918+
/// <typeparam name="TOutput">The type of the record updated.</typeparam>
919+
/// <param name="table">The name of the database table.</param>
920+
/// <param name="data">The record data to update.</param>
921+
/// <param name="cancellationToken">The cancellationToken enables graceful cancellation of asynchronous operations</param>
922+
/// <returns>The list of updated records.</returns>
923+
/// <exception cref="OperationCanceledException"></exception>
924+
/// <exception cref="HttpRequestException"></exception>
925+
/// <exception cref="InvalidOperationException"></exception>
926+
/// <exception cref="SurrealDbException"></exception>
927+
Task<IEnumerable<TOutput>> Update<TData, TOutput>(
928+
string table,
929+
TData data,
930+
CancellationToken cancellationToken = default
931+
)
932+
where TOutput : IRecord;
933+
911934
/// <summary>
912935
/// Updates the specified record in the database.
913936
/// </summary>
@@ -963,7 +986,7 @@ Task<TOutput> Upsert<TData, TOutput>(
963986
where TOutput : IRecord;
964987

965988
/// <summary>
966-
/// Creates a random record in the database.
989+
/// Updates or creates records in the database.
967990
/// Prior to SurrealDB v2.1.0, this method would update or create all records in the database.
968991
/// </summary>
969992
/// <typeparam name="T">The type of the record to upsert.</typeparam>
@@ -982,6 +1005,30 @@ Task<IEnumerable<T>> Upsert<T>(
9821005
)
9831006
where T : class; // TODO : Change return type from "IEnumerable<T>" to "T" in the future
9841007

1008+
/// <summary>
1009+
/// Updates or creates a record in the database.
1010+
/// Prior to SurrealDB v2.1.0, this method would update or create all records in the database.<br />
1011+
/// <see href="https://surrealdb.com/docs/sdk/dotnet/methods/upsert">
1012+
/// `Upsert` on surrealdb.com/docs
1013+
/// </see>
1014+
/// </summary>
1015+
/// <typeparam name="TData"></typeparam>
1016+
/// <typeparam name="TOutput"></typeparam>
1017+
/// <param name="table">The name of the database table.</param>
1018+
/// <param name="data">The record to create or update.</param>
1019+
/// <param name="cancellationToken">The cancellationToken enables graceful cancellation of asynchronous operations</param>
1020+
/// <returns>The list of created or updated records.</returns>
1021+
/// <exception cref="OperationCanceledException"></exception>
1022+
/// <exception cref="HttpRequestException"></exception>
1023+
/// <exception cref="InvalidOperationException"></exception>
1024+
/// <exception cref="SurrealDbException"></exception>
1025+
Task<IEnumerable<TOutput>> Upsert<TData, TOutput>(
1026+
string table,
1027+
TData data,
1028+
CancellationToken cancellationToken = default
1029+
)
1030+
where TOutput : IRecord;
1031+
9851032
/// <summary>
9861033
/// Updates or creates the specified record in the database.
9871034
/// </summary>

0 commit comments

Comments
 (0)