V3 rc2 #2

Merged
danieljsummers merged 6 commits from v3-rc2 into main 2024-01-24 02:23:24 +00:00
3 changed files with 145 additions and 1 deletions
Showing only changes of commit 58ac1834f5 - Show all commits

View File

@ -53,6 +53,21 @@ module Query =
sprintf sprintf
"UPDATE %s SET data = json_patch(data, json(@data)) WHERE %s" "UPDATE %s SET data = json_patch(data, json(@data)) WHERE %s"
tableName (Query.whereByField fieldName op "@field") tableName (Query.whereByField fieldName op "@field")
/// Queries to remove a field from a document
module RemoveField =
/// Query to remove a field from a document by the document's ID
[<CompiledName "ById">]
let byId tableName =
$"""UPDATE %s{tableName} SET data = json_remove(data, @name) WHERE {Query.whereById "@id"}"""
/// Query to remove a field from a document via a comparison on a JSON field within the document
[<CompiledName "ByField">]
let byField tableName fieldName op =
sprintf
"UPDATE %s SET data = json_remove(data, @name) WHERE %s"
tableName (Query.whereByField fieldName op "@field")
/// Parameter handling helpers /// Parameter handling helpers
@ -74,6 +89,11 @@ module Parameters =
let fieldParam (value: obj) = let fieldParam (value: obj) =
SqliteParameter("@field", value) SqliteParameter("@field", value)
/// Create a JSON field name parameter (name "@name")
[<CompiledName "FieldName">]
let fieldNameParam name =
SqliteParameter("@name", $"$.%s{name}")
/// An empty parameter sequence /// An empty parameter sequence
[<CompiledName "None">] [<CompiledName "None">]
let noParams = let noParams =
@ -315,6 +335,23 @@ module WithConn =
Custom.nonQuery Custom.nonQuery
(Query.Patch.byField tableName fieldName op) [ fieldParam value; jsonParam "@data" patch ] conn (Query.Patch.byField tableName fieldName op) [ fieldParam value; jsonParam "@data" patch ] conn
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
module RemoveField =
/// Remove a field from a document by the document's ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) fieldName conn =
Custom.nonQuery (Query.RemoveField.byId tableName) [ idParam docId; fieldNameParam fieldName ] conn
/// Remove a field from a document via a comparison on a JSON field in the document
[<CompiledName "ByField">]
let byField tableName whereFieldName op (value: obj) removeFieldName conn =
Custom.nonQuery
(Query.RemoveField.byField tableName whereFieldName op)
[ fieldParam value; fieldNameParam removeFieldName ]
conn
/// Commands to delete documents /// Commands to delete documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Delete = module Delete =
@ -522,6 +559,22 @@ module Patch =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Patch.byField tableName fieldName op value patch conn WithConn.Patch.byField tableName fieldName op value patch conn
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
module RemoveField =
/// Remove a field from a document by the document's ID
[<CompiledName "ById">]
let byId tableName (docId: 'TKey) fieldName =
use conn = Configuration.dbConn ()
WithConn.RemoveField.byId tableName docId fieldName conn
/// Remove a field from a document via a comparison on a JSON field in the document
[<CompiledName "ByField">]
let byField tableName whereFieldName op (value: obj) removeFieldName =
use conn = Configuration.dbConn ()
WithConn.RemoveField.byField tableName whereFieldName op value removeFieldName conn
/// Commands to delete documents /// Commands to delete documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Delete = module Delete =

View File

@ -1,4 +1,5 @@
using Expecto.CSharp; using System.Text.Json;
using Expecto.CSharp;
using Expecto; using Expecto;
using Microsoft.Data.Sqlite; using Microsoft.Data.Sqlite;
using Microsoft.FSharp.Core; using Microsoft.FSharp.Core;
@ -40,6 +41,21 @@ public static class SqliteCSharpTests
"UPDATE partial by JSON comparison query not correct"); "UPDATE partial by JSON comparison query not correct");
}) })
}), }),
TestList("RemoveField", new[]
{
TestCase("ById succeeds", () =>
{
Expect.equal(Sqlite.Query.RemoveField.ById("tbl"),
"UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Id' = @id",
"Remove field by ID query not correct");
}),
TestCase("ByField succeeds", () =>
{
Expect.equal(Sqlite.Query.RemoveField.ByField("tbl", "Fly", Op.LT),
"UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Fly' < @field",
"Remove field by field query not correct");
})
})
}), }),
TestList("Parameters", new[] TestList("Parameters", new[]
{ {
@ -540,6 +556,37 @@ public static class SqliteCSharpTests
}) })
}) })
}), }),
TestList("RemoveField", new[]
{
TestList("ById", new[]
{
TestCase("succeeds when a field is removed", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await RemoveField.ById(SqliteDb.TableName, "two", "Sub");
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.isNull(updated.Sub, "The sub-document should have been removed");
}),
TestCase("succeeds when a field is not removed", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
// This not raising an exception is the test
await RemoveField.ById(SqliteDb.TableName, "two", "AFieldThatIsNotThere");
}),
TestCase("succeeds when no document is matched", async () =>
{
await using var db = await SqliteDb.BuildDb();
// This not raising an exception is the test
await RemoveField.ById(SqliteDb.TableName, "two", "Value");
})
})
}),
TestList("Delete", new[] TestList("Delete", new[]
{ {
TestList("ById", new[] TestList("ById", new[]

View File

@ -1,5 +1,6 @@
module SqliteTests module SqliteTests
open System.Text.Json
open BitBadger.Documents open BitBadger.Documents
open BitBadger.Documents.Sqlite open BitBadger.Documents.Sqlite
open BitBadger.Documents.Tests open BitBadger.Documents.Tests
@ -31,6 +32,20 @@ let unitTests =
"UPDATE partial by JSON comparison query not correct" "UPDATE partial by JSON comparison query not correct"
} }
] ]
testList "RemoveField" [
test "byId succeeds" {
Expect.equal
(Query.RemoveField.byId "tbl")
"UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Id' = @id"
"Remove field by ID query not correct"
}
test "byField succeeds" {
Expect.equal
(Query.RemoveField.byField "tbl" "Fly" GT)
"UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Fly' > @field"
"Remove field by field query not correct"
}
]
] ]
testList "Parameters" [ testList "Parameters" [
test "idParam succeeds" { test "idParam succeeds" {
@ -489,6 +504,35 @@ let integrationTests =
} }
] ]
] ]
testList "RemoveField" [
testList "byId" [
testTask "succeeds when a field is removed" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
do! RemoveField.byId SqliteDb.TableName "two" "Sub"
try
let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "two"
Expect.isTrue false "The updated document should have failed to parse"
with
| :? JsonException -> ()
| exn as ex -> Expect.isTrue false $"Threw {ex.GetType().Name} ({ex.Message})"
}
testTask "succeeds when a field is not removed" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
// This not raising an exception is the test
do! RemoveField.byId SqliteDb.TableName "two" "AFieldThatIsNotThere"
}
testTask "succeeds when no document is matched" {
use! db = SqliteDb.BuildDb()
// This not raising an exception is the test
do! RemoveField.byId SqliteDb.TableName "two" "Value"
}
]
]
testList "Delete" [ testList "Delete" [
testList "byId" [ testList "byId" [
testTask "succeeds when a document is deleted" { testTask "succeeds when a document is deleted" {