Skip to content

Commit

Permalink
✨ add method to create multiple records at once (surrealdb#117)
Browse files Browse the repository at this point in the history
  • Loading branch information
Odonno authored and EricSites committed Oct 1, 2024
1 parent 2dab881 commit 7e27c31
Show file tree
Hide file tree
Showing 8 changed files with 258 additions and 9 deletions.
11 changes: 11 additions & 0 deletions SurrealDb.Embedded.InMemory/Internals/SurrealDbEngine.InMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,17 @@ public Task<T> Info<T>(CancellationToken cancellationToken)
throw new NotSupportedException("Authentication is not enabled in embedded mode.");
}

public async Task<IEnumerable<T>> Insert<T>(
string table,
IEnumerable<T> data,
CancellationToken cancellationToken
)
where T : Record
{
return await SendRequestAsync<List<T>>(Method.Insert, [table, data], cancellationToken)
.ConfigureAwait(false);
}

public Task Invalidate(CancellationToken cancellationToken)
{
throw new NotSupportedException("Authentication is not enabled in embedded mode.");
Expand Down
1 change: 1 addition & 0 deletions SurrealDb.Embedded.InMemory/NativeMethods.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ internal enum Method : byte
Set = 3,
Unset = 4,
Select = 5,
Insert = 6,
Create = 7,
Update = 8,
Merge = 9,
Expand Down
10 changes: 10 additions & 0 deletions SurrealDb.Embedded.InMemory/SurrealDbMemoryClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ public Task<T> Info<T>(CancellationToken cancellationToken = default)
return _engine.Info<T>(cancellationToken);
}

public Task<IEnumerable<T>> Insert<T>(
string table,
IEnumerable<T> data,
CancellationToken cancellationToken = default
)
where T : Record
{
return _engine.Insert(table, data, cancellationToken);
}

public Task Invalidate(CancellationToken cancellationToken = default)
{
return _engine.Invalidate(cancellationToken);
Expand Down
220 changes: 220 additions & 0 deletions SurrealDb.Net.Tests/InsertTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
using System.Text;

namespace SurrealDb.Net.Tests;

public class InsertTests
{
[Theory]
[InlineData("Endpoint=mem://")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=CBOR")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=CBOR")]
public async Task ShouldInsertZeroRecord(string connectionString)
{
IEnumerable<Empty>? list = null;
IEnumerable<Empty>? result = null;

Func<Task> func = async () =>
{
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
string filePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"Schemas/post.surql"
);
string fileContent = File.ReadAllText(filePath, Encoding.UTF8);
string query = fileContent;
using var client = surrealDbClientGenerator.Create(connectionString);
await client.Use(dbInfo.Namespace, dbInfo.Database);
await client.RawQuery(query);
result = await client.Insert("empty", Enumerable.Empty<Empty>());
list = await client.Select<Empty>("empty");
};

await func.Should().NotThrowAsync();

list.Should().NotBeNull().And.HaveCount(0);

result.Should().NotBeNull().And.HaveCount(0);
}

[Theory]
[InlineData("Endpoint=mem://")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=CBOR")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=CBOR")]
public async Task ShouldInsertPostsWithAutogeneratedId(string connectionString)
{
IEnumerable<Post>? list = null;
IEnumerable<Post>? result = null;

Func<Task> func = async () =>
{
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
string filePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"Schemas/post.surql"
);
string fileContent = File.ReadAllText(filePath, Encoding.UTF8);
string query = fileContent;
using var client = surrealDbClientGenerator.Create(connectionString);
await client.Use(dbInfo.Namespace, dbInfo.Database);
await client.RawQuery(query);
var post = new Post
{
Title = "A new article",
Content = "This is a new article created using the .NET SDK"
};
result = await client.Insert("post", new[] { post });
list = await client.Select<Post>("post");
};

await func.Should().NotThrowAsync();

list.Should().NotBeNull().And.HaveCount(3);

result.Should().NotBeNull().And.HaveCount(1);

var post = result!.First();
post!.Title.Should().Be("A new article");
post!.Content.Should().Be("This is a new article created using the .NET SDK");
post!.CreatedAt.Should().NotBeNull();
post!.Status.Should().Be("DRAFT");
}

[Theory]
[InlineData("Endpoint=mem://")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=CBOR")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=CBOR")]
public async Task ShouldInsertPostsWithPredefinedId(string connectionString)
{
IEnumerable<Post>? list = null;
IEnumerable<Post>? result = null;

Func<Task> func = async () =>
{
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
string filePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"Schemas/post.surql"
);
string fileContent = File.ReadAllText(filePath, Encoding.UTF8);
string query = fileContent;
using var client = surrealDbClientGenerator.Create(connectionString);
await client.Use(dbInfo.Namespace, dbInfo.Database);
await client.RawQuery(query);
var post = new Post
{
Id = new Thing("post", "another"),
Title = "A new article",
Content = "This is a new article created using the .NET SDK"
};
result = await client.Insert("post", new[] { post });
list = await client.Select<Post>("post");
};

await func.Should().NotThrowAsync();

list.Should().NotBeNull().And.HaveCount(3);

result.Should().NotBeNull().And.HaveCount(1);

var post = result!.First();
post!.Title.Should().Be("A new article");
post!.Content.Should().Be("This is a new article created using the .NET SDK");
post!.CreatedAt.Should().NotBeNull();
post!.Status.Should().Be("DRAFT");

var anotherPost = list!.First(r => r.Id!.Id == "another");

anotherPost.Should().NotBeNull();
anotherPost!.Title.Should().Be("A new article");
anotherPost!.Content.Should().Be("This is a new article created using the .NET SDK");
anotherPost!.CreatedAt.Should().NotBeNull();
anotherPost!.Status.Should().Be("DRAFT");
}

[Theory]
[InlineData("Endpoint=mem://")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=http://127.0.0.1:8000;User=root;Pass=root;Serialization=CBOR")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=JSON")]
[InlineData("Endpoint=ws://127.0.0.1:8000/rpc;User=root;Pass=root;Serialization=CBOR")]
public async Task ShouldInsertMultiplePosts(string connectionString)
{
IEnumerable<Post>? result = null;
IEnumerable<Post>? list = null;

Func<Task> func = async () =>
{
await using var surrealDbClientGenerator = new SurrealDbClientGenerator();
var dbInfo = surrealDbClientGenerator.GenerateDatabaseInfo();
string filePath = Path.Combine(
AppDomain.CurrentDomain.BaseDirectory,
"Schemas/post.surql"
);
string fileContent = File.ReadAllText(filePath, Encoding.UTF8);
string query = fileContent;
using var client = surrealDbClientGenerator.Create(connectionString);
await client.Use(dbInfo.Namespace, dbInfo.Database);
await client.RawQuery(query);
var posts = new List<Post>
{
new Post
{
Id = new Thing("post", "A"),
Title = "An article",
Content = "This is a new article"
},
new Post
{
Id = new Thing("post", "B"),
Title = "An article",
Content = "This is a new article"
},
new Post
{
Id = new Thing("post", "C"),
Title = "An article",
Content = "This is a new article"
}
};
result = await client.Insert("post", posts);
list = await client.Select<Post>("post");
};

await func.Should().NotThrowAsync();

result.Should().NotBeNull().And.HaveCount(3);
list.Should().NotBeNull().And.HaveCount(5);
}
}
5 changes: 2 additions & 3 deletions SurrealDb.Net/Internals/SurrealDbEngine.Http.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,13 +267,12 @@ CancellationToken cancellationToken
)
where T : IRecord
{
object?[] @params = [data];
var request = new SurrealDbHttpRequest { Method = "insert", Parameters = @params };
var request = new SurrealDbHttpRequest { Method = "insert", Parameters = [table, data] };

var dbResponse = await ExecuteRequestAsync(request, cancellationToken)
.ConfigureAwait(false);

return dbResponse.GetValue<IEnumerable<T>>()!;
return dbResponse.DeserializeEnumerable<T>();
}

public Task Invalidate(CancellationToken _)
Expand Down
15 changes: 11 additions & 4 deletions SurrealDb.Net/SurrealDbClient.Interface.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public interface ISurrealDbClient : IDisposable
/// <summary>
/// Creates the specific record in the database.
/// </summary>
/// <remarks>
/// Note: This method creates only a single record. If the record already exist, it will throw an error.
/// </remarks>
/// <typeparam name="T">The type of the record to create.</typeparam>
/// <param name="data">The record to create.</param>
/// <param name="cancellationToken">The cancellationToken enables graceful cancellation of asynchronous operations</param>
Expand Down Expand Up @@ -174,11 +177,15 @@ Task<TOutput> Create<TData, TOutput>(
Task<T> Info<T>(CancellationToken cancellationToken = default);

/// <summary>
/// Inserts a set of records in a table in the database.
/// Inserts a collection of records in the database.
/// </summary>
/// <typeparam name="T">The type of the records to insert.</typeparam>
/// <param name="table">The table name where the record will be stored.</param>
/// <param name="data">The records to insert.</param>
/// <remarks>
/// Note: This method allows you to create multiple records at once.
/// In case a record already exist, it will not throw error and it will not update the existing record.
/// </remarks>
/// <typeparam name="T">The type of the record to create.</typeparam>
/// <param name="table">The table name where the records will be stored.</param>
/// <param name="data">The records to create.</param>
/// <param name="cancellationToken">The cancellationToken enables graceful cancellation of asynchronous operations</param>
/// <returns>The records created.</returns>
/// <exception cref="OperationCanceledException"></exception>
Expand Down
2 changes: 1 addition & 1 deletion SurrealDb.Net/SurrealDbClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ public Task<IEnumerable<T>> Insert<T>(
)
where T : IRecord
{
return _engine.Insert<T>(table, data, cancellationToken);
return _engine.Insert(table, data, cancellationToken);
}

public Task Invalidate(CancellationToken cancellationToken = default)
Expand Down
3 changes: 2 additions & 1 deletion rust-embedded/memory/src/models/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub enum Method {
Set = 3,
Unset = 4,
Select = 5,
//Insert = 6, // TODO
Insert = 6,
Create = 7,
Update = 8,
Merge = 9,
Expand All @@ -24,6 +24,7 @@ impl From<Method> for surrealdb::rpc::method::Method {
Method::Set => surrealdb::rpc::method::Method::Set,
Method::Unset => surrealdb::rpc::method::Method::Unset,
Method::Select => surrealdb::rpc::method::Method::Select,
Method::Insert => surrealdb::rpc::method::Method::Insert,
Method::Create => surrealdb::rpc::method::Method::Create,
Method::Update => surrealdb::rpc::method::Method::Update,
Method::Merge => surrealdb::rpc::method::Method::Merge,
Expand Down

0 comments on commit 7e27c31

Please sign in to comment.