From 58ac1834f5cb791b0581e7942f5dc013b1b2f4e1 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 22 Jan 2024 20:52:27 -0500 Subject: [PATCH 1/6] WIP on RemoveField functions --- src/Sqlite/Library.fs | 53 +++++++++++++++++++++++++++ src/Tests.CSharp/SqliteCSharpTests.cs | 49 ++++++++++++++++++++++++- src/Tests/SqliteTests.fs | 44 ++++++++++++++++++++++ 3 files changed, 145 insertions(+), 1 deletion(-) diff --git a/src/Sqlite/Library.fs b/src/Sqlite/Library.fs index 6a9c4a2..6be811e 100644 --- a/src/Sqlite/Library.fs +++ b/src/Sqlite/Library.fs @@ -53,6 +53,21 @@ module Query = sprintf "UPDATE %s SET data = json_patch(data, json(@data)) WHERE %s" 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 + [] + 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 + [] + 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 @@ -74,6 +89,11 @@ module Parameters = let fieldParam (value: obj) = SqliteParameter("@field", value) + /// Create a JSON field name parameter (name "@name") + [] + let fieldNameParam name = + SqliteParameter("@name", $"$.%s{name}") + /// An empty parameter sequence [] let noParams = @@ -315,6 +335,23 @@ module WithConn = Custom.nonQuery (Query.Patch.byField tableName fieldName op) [ fieldParam value; jsonParam "@data" patch ] conn + /// Commands to remove fields from documents + [] + module RemoveField = + + /// Remove a field from a document by the document's ID + [] + 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 + [] + 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 [] module Delete = @@ -522,6 +559,22 @@ module Patch = use conn = Configuration.dbConn () WithConn.Patch.byField tableName fieldName op value patch conn +/// Commands to remove fields from documents +[] +module RemoveField = + + /// Remove a field from a document by the document's ID + [] + 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 + [] + 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 [] module Delete = diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs index f2e0621..91fb9ce 100644 --- a/src/Tests.CSharp/SqliteCSharpTests.cs +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -1,4 +1,5 @@ -using Expecto.CSharp; +using System.Text.Json; +using Expecto.CSharp; using Expecto; using Microsoft.Data.Sqlite; using Microsoft.FSharp.Core; @@ -40,6 +41,21 @@ public static class SqliteCSharpTests "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[] { @@ -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(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("ById", new[] diff --git a/src/Tests/SqliteTests.fs b/src/Tests/SqliteTests.fs index caaa437..582b2b1 100644 --- a/src/Tests/SqliteTests.fs +++ b/src/Tests/SqliteTests.fs @@ -1,5 +1,6 @@ module SqliteTests +open System.Text.Json open BitBadger.Documents open BitBadger.Documents.Sqlite open BitBadger.Documents.Tests @@ -31,6 +32,20 @@ let unitTests = "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" [ 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 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 "byId" [ testTask "succeeds when a document is deleted" { -- 2.45.1 From 7ac174350cd616d94503a5f6de2e80084b9ab26e Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 22 Jan 2024 21:33:31 -0500 Subject: [PATCH 2/6] Finish SQLite RemoveField functions --- src/Sqlite/Extensions.fs | 18 ++++++ .../SqliteCSharpExtensionTests.cs | 62 +++++++++++++++++++ src/Tests.CSharp/SqliteCSharpTests.cs | 28 +++++++++ src/Tests/SqliteExtensionTests.fs | 61 ++++++++++++++++++ src/Tests/SqliteTests.fs | 27 ++++++++ 5 files changed, 196 insertions(+) diff --git a/src/Sqlite/Extensions.fs b/src/Sqlite/Extensions.fs index 91304b5..89fab23 100644 --- a/src/Sqlite/Extensions.fs +++ b/src/Sqlite/Extensions.fs @@ -88,6 +88,14 @@ module Extensions = member conn.patchByField tableName fieldName op (value: obj) (patch: 'TPatch) = WithConn.Patch.byField tableName fieldName op value patch conn + /// Remove a field from a document by the document's ID + member conn.removeFieldById tableName (docId: 'TKey) fieldName = + WithConn.RemoveField.byId tableName docId fieldName conn + + /// Remove a field from a document via a comparison on a JSON field in the document + member conn.removeFieldByField tableName whereFieldName op (value: obj) removeFieldName = + WithConn.RemoveField.byField tableName whereFieldName op value removeFieldName conn + /// Delete a document by its ID member conn.deleteById tableName (docId: 'TKey) = WithConn.Delete.byId tableName docId conn @@ -204,6 +212,16 @@ type SqliteConnectionCSharpExtensions = static member inline PatchByField<'TPatch>(conn, tableName, fieldName, op, value: obj, patch: 'TPatch) = WithConn.Patch.byField tableName fieldName op value patch conn + /// Remove a field from a document by the document's ID + [] + static member inline RemoveFieldById<'TKey>(conn, tableName, docId: 'TKey, fieldName) = + WithConn.RemoveField.byId tableName docId fieldName conn + + /// Remove a field from a document via a comparison on a JSON field in the document + [] + static member inline RemoveFieldByField(conn, tableName, whereFieldName, op, value: obj, removeFieldName) = + WithConn.RemoveField.byField tableName whereFieldName op value removeFieldName conn + /// Delete a document by its ID [] static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) = diff --git a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs index cc5fb5f..89e2178 100644 --- a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs +++ b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs @@ -467,6 +467,68 @@ public static class SqliteCSharpExtensionTests await conn.PatchByField(SqliteDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" }); }) }), + TestList("RemoveFieldById", new[] + { + TestCase("succeeds when a field is removed", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(); + + await conn.RemoveFieldById(SqliteDb.TableName, "two", "Sub"); + var updated = await Find.ById(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 using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(); + + // This not raising an exception is the test + await conn.RemoveFieldById(SqliteDb.TableName, "two", "AFieldThatIsNotThere"); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + + // This not raising an exception is the test + await conn.RemoveFieldById(SqliteDb.TableName, "two", "Value"); + }) + }), + TestList("RemoveFieldByField", new[] + { + TestCase("succeeds when a field is removed", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(); + + await conn.RemoveFieldByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Sub"); + var updated = await Find.ById(SqliteDb.TableName, "four"); + 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 using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(); + + // This not raising an exception is the test + await conn.RemoveFieldByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Nothing"); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + + // This not raising an exception is the test + await conn.RemoveFieldByField(SqliteDb.TableName, "Abracadabra", Op.NE, "apple", "Value"); + }) + }), TestList("DeleteById", new[] { TestCase("succeeds when a document is deleted", async () => diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs index 91fb9ce..cd7440d 100644 --- a/src/Tests.CSharp/SqliteCSharpTests.cs +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -585,6 +585,34 @@ public static class SqliteCSharpTests // This not raising an exception is the test await RemoveField.ById(SqliteDb.TableName, "two", "Value"); }) + }), + TestList("ByField", new[] + { + TestCase("succeeds when a field is removed", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + await RemoveField.ByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Sub"); + var updated = await Find.ById(SqliteDb.TableName, "four"); + 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.ByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Nothing"); + }), + 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.ByField(SqliteDb.TableName, "Abracadabra", Op.NE, "apple", "Value"); + }) }) }), TestList("Delete", new[] diff --git a/src/Tests/SqliteExtensionTests.fs b/src/Tests/SqliteExtensionTests.fs index 4c6e2b5..b7586e3 100644 --- a/src/Tests/SqliteExtensionTests.fs +++ b/src/Tests/SqliteExtensionTests.fs @@ -1,5 +1,6 @@ module SqliteExtensionTests +open System.Text.Json open BitBadger.Documents open BitBadger.Documents.Sqlite open BitBadger.Documents.Tests @@ -344,6 +345,66 @@ let integrationTests = do! conn.patchByField SqliteDb.TableName "Value" EQ "burgundy" {| Foo = "green" |} } ] + testList "removeFieldById" [ + testTask "succeeds when a field is removed" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs () + + do! conn.removeFieldById SqliteDb.TableName "two" "Sub" + try + let! _ = conn.findById 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() + use conn = Configuration.dbConn () + do! loadDocs () + + // This not raising an exception is the test + do! conn.removeFieldById SqliteDb.TableName "two" "AFieldThatIsNotThere" + } + testTask "succeeds when no document is matched" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + + // This not raising an exception is the test + do! conn.removeFieldById SqliteDb.TableName "two" "Value" + } + ] + testList "removeFieldByField" [ + testTask "succeeds when a field is removed" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs () + + do! conn.removeFieldByField SqliteDb.TableName "NumValue" EQ 17 "Sub" + try + let! _ = conn.findById SqliteDb.TableName "four" + 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() + use conn = Configuration.dbConn () + do! loadDocs () + + // This not raising an exception is the test + do! conn.removeFieldByField SqliteDb.TableName "NumValue" EQ 17 "Nothing" + } + testTask "succeeds when no document is matched" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + + // This not raising an exception is the test + do! conn.removeFieldByField SqliteDb.TableName "Abracadabra" NE "apple" "Value" + } + ] testList "deleteById" [ testTask "succeeds when a document is deleted" { use! db = SqliteDb.BuildDb() diff --git a/src/Tests/SqliteTests.fs b/src/Tests/SqliteTests.fs index 582b2b1..613d0ac 100644 --- a/src/Tests/SqliteTests.fs +++ b/src/Tests/SqliteTests.fs @@ -532,6 +532,33 @@ let integrationTests = do! RemoveField.byId SqliteDb.TableName "two" "Value" } ] + testList "byField" [ + testTask "succeeds when a field is removed" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + do! RemoveField.byField SqliteDb.TableName "NumValue" EQ 17 "Sub" + try + let! _ = Find.byId SqliteDb.TableName "four" + 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.byField SqliteDb.TableName "NumValue" EQ 17 "Nothing" + } + testTask "succeeds when no document is matched" { + use! db = SqliteDb.BuildDb() + + // This not raising an exception is the test + do! RemoveField.byField SqliteDb.TableName "Abracadabra" NE "apple" "Value" + } + ] ] testList "Delete" [ testList "byId" [ -- 2.45.1 From 097208139d6f99bd09c32803c2d139ab6857bda6 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 22 Jan 2024 22:58:22 -0500 Subject: [PATCH 3/6] Add Field type for criteria - Implement in SQLite --- src/Common/Library.fs | 45 ++++++++++ src/Sqlite/Extensions.fs | 56 ++++++------ src/Sqlite/Library.fs | 86 +++++++++++-------- src/Tests.CSharp/CommonCSharpTests.cs | 57 ++++++++++++ .../SqliteCSharpExtensionTests.cs | 32 +++---- src/Tests.CSharp/SqliteCSharpTests.cs | 32 +++---- src/Tests/SqliteExtensionTests.fs | 32 +++---- src/Tests/SqliteTests.fs | 32 +++---- 8 files changed, 242 insertions(+), 130 deletions(-) diff --git a/src/Common/Library.fs b/src/Common/Library.fs index 2f45c2b..47caade 100644 --- a/src/Common/Library.fs +++ b/src/Common/Library.fs @@ -32,6 +32,51 @@ type Op = | NEX -> "IS NULL" +/// Criteria for a field WHERE clause +type Field = { + /// The name of the field + Name: string + + /// The operation by which the field will be compared + Op: Op + + /// The value of the field + Value: obj +} with + + /// Create an equals (=) field criteria + static member EQ name (value: obj) = + { Name = name; Op = EQ; Value = value } + + /// Create a greater than (>) field criteria + static member GT name (value: obj) = + { Name = name; Op = GT; Value = value } + + /// Create a greater than or equal to (>=) field criteria + static member GE name (value: obj) = + { Name = name; Op = GE; Value = value } + + /// Create a less than (<) field criteria + static member LT name (value: obj) = + { Name = name; Op = LT; Value = value } + + /// Create a less than or equal to (<=) field criteria + static member LE name (value: obj) = + { Name = name; Op = LE; Value = value } + + /// Create a not equals (<>) field criteria + static member NE name (value: obj) = + { Name = name; Op = NE; Value = value } + + /// Create an exists (IS NOT NULL) field criteria + static member EX name = + { Name = name; Op = EX; Value = obj () } + + /// Create an not exists (IS NULL) field criteria + static member NEX name = + { Name = name; Op = NEX; Value = obj () } + + /// The required document serialization implementation type IDocumentSerializer = diff --git a/src/Sqlite/Extensions.fs b/src/Sqlite/Extensions.fs index 89fab23..3416614 100644 --- a/src/Sqlite/Extensions.fs +++ b/src/Sqlite/Extensions.fs @@ -45,16 +45,16 @@ module Extensions = WithConn.Count.all tableName conn /// Count matching documents using a comparison on a JSON field - member conn.countByField tableName fieldName op (value: obj) = - WithConn.Count.byField tableName fieldName op value conn + member conn.countByField tableName field = + WithConn.Count.byField tableName field conn /// Determine if a document exists for the given ID member conn.existsById tableName (docId: 'TKey) = WithConn.Exists.byId tableName docId conn /// Determine if a document exists using a comparison on a JSON field - member conn.existsByField tableName fieldName op (value: obj) = - WithConn.Exists.byField tableName fieldName op value conn + member conn.existsByField tableName field = + WithConn.Exists.byField tableName field conn /// Retrieve all documents in the given table member conn.findAll<'TDoc> tableName = @@ -65,12 +65,12 @@ module Extensions = WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn /// Retrieve documents via a comparison on a JSON field - member conn.findByField<'TDoc> tableName fieldName op (value: obj) = - WithConn.Find.byField<'TDoc> tableName fieldName op value conn + member conn.findByField<'TDoc> tableName field = + WithConn.Find.byField<'TDoc> tableName field conn /// Retrieve documents via a comparison on a JSON field, returning only the first result - member conn.findFirstByField<'TDoc> tableName fieldName op (value: obj) = - WithConn.Find.firstByField<'TDoc> tableName fieldName op value conn + member conn.findFirstByField<'TDoc> tableName field = + WithConn.Find.firstByField<'TDoc> tableName field conn /// Update an entire document by its ID member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) = @@ -85,24 +85,24 @@ module Extensions = WithConn.Patch.byId tableName docId patch conn /// Patch documents using a comparison on a JSON field - member conn.patchByField tableName fieldName op (value: obj) (patch: 'TPatch) = - WithConn.Patch.byField tableName fieldName op value patch conn + member conn.patchByField tableName field (patch: 'TPatch) = + WithConn.Patch.byField tableName field patch conn /// Remove a field from a document by the document's ID member conn.removeFieldById tableName (docId: 'TKey) fieldName = WithConn.RemoveField.byId tableName docId fieldName conn /// Remove a field from a document via a comparison on a JSON field in the document - member conn.removeFieldByField tableName whereFieldName op (value: obj) removeFieldName = - WithConn.RemoveField.byField tableName whereFieldName op value removeFieldName conn + member conn.removeFieldByField tableName field fieldName = + WithConn.RemoveField.byField tableName field fieldName conn /// Delete a document by its ID member conn.deleteById tableName (docId: 'TKey) = WithConn.Delete.byId tableName docId conn /// Delete documents by matching a comparison on a JSON field - member conn.deleteByField tableName fieldName op (value: obj) = - WithConn.Delete.byField tableName fieldName op value conn + member conn.deleteByField tableName field = + WithConn.Delete.byField tableName field conn open System.Runtime.CompilerServices @@ -159,8 +159,8 @@ type SqliteConnectionCSharpExtensions = /// Count matching documents using a comparison on a JSON field [] - static member inline CountByField(conn, tableName, fieldName, op, value: obj) = - WithConn.Count.byField tableName fieldName op value conn + static member inline CountByField(conn, tableName, field) = + WithConn.Count.byField tableName field conn /// Determine if a document exists for the given ID [] @@ -169,8 +169,8 @@ type SqliteConnectionCSharpExtensions = /// Determine if a document exists using a comparison on a JSON field [] - static member inline ExistsByField(conn, tableName, fieldName, op, value: obj) = - WithConn.Exists.byField tableName fieldName op value conn + static member inline ExistsByField(conn, tableName, field) = + WithConn.Exists.byField tableName field conn /// Retrieve all documents in the given table [] @@ -184,13 +184,13 @@ type SqliteConnectionCSharpExtensions = /// Retrieve documents via a comparison on a JSON field [] - static member inline FindByField<'TDoc>(conn, tableName, fieldName, op, value) = - WithConn.Find.ByField<'TDoc>(tableName, fieldName, op, value, conn) + static member inline FindByField<'TDoc>(conn, tableName, field) = + WithConn.Find.ByField<'TDoc>(tableName, field, conn) /// Retrieve documents via a comparison on a JSON field, returning only the first result [] - static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, fieldName, op, value: obj) = - WithConn.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, conn) + static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) = + WithConn.Find.FirstByField<'TDoc>(tableName, field, conn) /// Update an entire document by its ID [] @@ -209,8 +209,8 @@ type SqliteConnectionCSharpExtensions = /// Patch documents using a comparison on a JSON field [] - static member inline PatchByField<'TPatch>(conn, tableName, fieldName, op, value: obj, patch: 'TPatch) = - WithConn.Patch.byField tableName fieldName op value patch conn + static member inline PatchByField<'TPatch>(conn, tableName, field, patch: 'TPatch) = + WithConn.Patch.byField tableName field patch conn /// Remove a field from a document by the document's ID [] @@ -219,8 +219,8 @@ type SqliteConnectionCSharpExtensions = /// Remove a field from a document via a comparison on a JSON field in the document [] - static member inline RemoveFieldByField(conn, tableName, whereFieldName, op, value: obj, removeFieldName) = - WithConn.RemoveField.byField tableName whereFieldName op value removeFieldName conn + static member inline RemoveFieldByField(conn, tableName, field, fieldName) = + WithConn.RemoveField.byField tableName field fieldName conn /// Delete a document by its ID [] @@ -229,5 +229,5 @@ type SqliteConnectionCSharpExtensions = /// Delete documents by matching a comparison on a JSON field [] - static member inline DeleteByField(conn, tableName, fieldName, op, value: obj) = - WithConn.Delete.byField tableName fieldName op value conn + static member inline DeleteByField(conn, tableName, field) = + WithConn.Delete.byField tableName field conn diff --git a/src/Sqlite/Library.fs b/src/Sqlite/Library.fs index 6be811e..86b4ee6 100644 --- a/src/Sqlite/Library.fs +++ b/src/Sqlite/Library.fs @@ -12,7 +12,7 @@ module Configuration = /// Register a connection string to use for query execution (enables foreign keys) [] let useConnectionString connStr = - let builder = SqliteConnectionStringBuilder(connStr) + let builder = SqliteConnectionStringBuilder connStr builder.ForeignKeys <- Option.toNullable (Some true) connectionString <- Some (string builder) @@ -243,8 +243,8 @@ module WithConn = /// Count matching documents using a comparison on a JSON field [] - let byField tableName fieldName op (value: obj) conn = - Custom.scalar (Query.Count.byField tableName fieldName op) [ fieldParam value ] toCount conn + let byField tableName field conn = + Custom.scalar (Query.Count.byField tableName field.Name field.Op) [ fieldParam field.Value ] toCount conn /// Commands to determine if documents exist [] @@ -257,8 +257,8 @@ module WithConn = /// Determine if a document exists using a comparison on a JSON field [] - let byField tableName fieldName op (value: obj) conn = - Custom.scalar (Query.Exists.byField tableName fieldName op) [ fieldParam value ] toExists conn + let byField tableName field conn = + Custom.scalar (Query.Exists.byField tableName field.Name field.Op) [ fieldParam field.Value ] toExists conn /// Commands to retrieve documents [] @@ -284,23 +284,31 @@ module WithConn = /// Retrieve documents via a comparison on a JSON field [] - let byField<'TDoc> tableName fieldName op (value: obj) conn = - Custom.list<'TDoc> (Query.Find.byField tableName fieldName op) [ fieldParam value ] fromData<'TDoc> conn + let byField<'TDoc> tableName field conn = + Custom.list<'TDoc> + (Query.Find.byField tableName field.Name field.Op) [ fieldParam field.Value ] fromData<'TDoc> conn /// Retrieve documents via a comparison on a JSON field - let ByField<'TDoc>(tableName, fieldName, op, value: obj, conn) = - Custom.List<'TDoc>(Query.Find.byField tableName fieldName op, [ fieldParam value ], fromData<'TDoc>, conn) + let ByField<'TDoc>(tableName, field, conn) = + Custom.List<'TDoc>( + Query.Find.byField tableName field.Name field.Op, [ fieldParam field.Value ], fromData<'TDoc>, conn) /// Retrieve documents via a comparison on a JSON field, returning only the first result [] - let firstByField<'TDoc> tableName fieldName op (value: obj) conn = + let firstByField<'TDoc> tableName field conn = Custom.single - $"{Query.Find.byField tableName fieldName op} LIMIT 1" [ fieldParam value ] fromData<'TDoc> conn + $"{Query.Find.byField tableName field.Name field.Op} LIMIT 1" + [ fieldParam field.Value ] + fromData<'TDoc> + conn /// Retrieve documents via a comparison on a JSON field, returning only the first result - let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value: obj, conn) = + let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) = Custom.Single( - $"{Query.Find.byField tableName fieldName op} LIMIT 1", [ fieldParam value ], fromData<'TDoc>, conn) + $"{Query.Find.byField tableName field.Name field.Op} LIMIT 1", + [ fieldParam field.Value ], + fromData<'TDoc>, + conn) /// Commands to update documents [] @@ -331,9 +339,11 @@ module WithConn = /// Patch documents using a comparison on a JSON field [] - let byField tableName fieldName op (value: obj) (patch: 'TPatch) (conn: SqliteConnection) = + let byField tableName field (patch: 'TPatch) (conn: SqliteConnection) = Custom.nonQuery - (Query.Patch.byField tableName fieldName op) [ fieldParam value; jsonParam "@data" patch ] conn + (Query.Patch.byField tableName field.Name field.Op) + [ fieldParam field.Value; jsonParam "@data" patch ] + conn /// Commands to remove fields from documents [] @@ -346,10 +356,10 @@ module WithConn = /// Remove a field from a document via a comparison on a JSON field in the document [] - let byField tableName whereFieldName op (value: obj) removeFieldName conn = + let byField tableName field fieldName conn = Custom.nonQuery - (Query.RemoveField.byField tableName whereFieldName op) - [ fieldParam value; fieldNameParam removeFieldName ] + (Query.RemoveField.byField tableName field.Name field.Op) + [ fieldParam field.Value; fieldNameParam fieldName ] conn /// Commands to delete documents @@ -363,8 +373,8 @@ module WithConn = /// Delete documents by matching a comparison on a JSON field [] - let byField tableName fieldName op (value: obj) conn = - Custom.nonQuery (Query.Delete.byField tableName fieldName op) [ fieldParam value ] conn + let byField tableName field conn = + Custom.nonQuery (Query.Delete.byField tableName field.Name field.Op) [ fieldParam field.Value ] conn /// Commands to execute custom SQL queries @@ -454,9 +464,9 @@ module Count = /// Count matching documents using a comparison on a JSON field [] - let byField tableName fieldName op (value: obj) = + let byField tableName field = use conn = Configuration.dbConn () - WithConn.Count.byField tableName fieldName op value conn + WithConn.Count.byField tableName field conn /// Commands to determine if documents exist [] @@ -470,9 +480,9 @@ module Exists = /// Determine if a document exists using a comparison on a JSON field [] - let byField tableName fieldName op (value: obj) = + let byField tableName field = use conn = Configuration.dbConn () - WithConn.Exists.byField tableName fieldName op value conn + WithConn.Exists.byField tableName field conn /// Commands to determine if documents exist [] @@ -502,25 +512,25 @@ module Find = /// Retrieve documents via a comparison on a JSON field [] - let byField<'TDoc> tableName fieldName op value = + let byField<'TDoc> tableName field = use conn = Configuration.dbConn () - WithConn.Find.byField<'TDoc> tableName fieldName op value conn + WithConn.Find.byField<'TDoc> tableName field conn /// Retrieve documents via a comparison on a JSON field - let ByField<'TDoc>(tableName, fieldName, op, value) = + let ByField<'TDoc>(tableName, field) = use conn = Configuration.dbConn () - WithConn.Find.ByField<'TDoc>(tableName, fieldName, op, value, conn) + WithConn.Find.ByField<'TDoc>(tableName, field, conn) /// Retrieve documents via a comparison on a JSON field, returning only the first result [] - let firstByField<'TDoc> tableName fieldName op value = + let firstByField<'TDoc> tableName field = use conn = Configuration.dbConn () - WithConn.Find.firstByField<'TDoc> tableName fieldName op value conn + WithConn.Find.firstByField<'TDoc> tableName field conn /// Retrieve documents via a comparison on a JSON field, returning only the first result - let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value) = + let FirstByField<'TDoc when 'TDoc: null>(tableName, field) = use conn = Configuration.dbConn () - WithConn.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, conn) + WithConn.Find.FirstByField<'TDoc>(tableName, field, conn) /// Commands to update documents [] @@ -555,9 +565,9 @@ module Patch = /// Patch documents using a comparison on a JSON field in the WHERE clause [] - let byField tableName fieldName op (value: obj) (patch: 'TPatch) = + let byField tableName field (patch: 'TPatch) = use conn = Configuration.dbConn () - WithConn.Patch.byField tableName fieldName op value patch conn + WithConn.Patch.byField tableName field patch conn /// Commands to remove fields from documents [] @@ -571,9 +581,9 @@ module RemoveField = /// Remove a field from a document via a comparison on a JSON field in the document [] - let byField tableName whereFieldName op (value: obj) removeFieldName = + let byField tableName field fieldName = use conn = Configuration.dbConn () - WithConn.RemoveField.byField tableName whereFieldName op value removeFieldName conn + WithConn.RemoveField.byField tableName field fieldName conn /// Commands to delete documents [] @@ -587,6 +597,6 @@ module Delete = /// Delete documents by matching a comparison on a JSON field [] - let byField tableName fieldName op (value: obj) = + let byField tableName field = use conn = Configuration.dbConn () - WithConn.Delete.byField tableName fieldName op value conn + WithConn.Delete.byField tableName field conn diff --git a/src/Tests.CSharp/CommonCSharpTests.cs b/src/Tests.CSharp/CommonCSharpTests.cs index c75d2ba..7f989d9 100644 --- a/src/Tests.CSharp/CommonCSharpTests.cs +++ b/src/Tests.CSharp/CommonCSharpTests.cs @@ -105,6 +105,63 @@ public static class CommonCSharpTests Expect.equal(Op.NEX.ToString(), "IS NULL", "The \"not exists\" operator was not correct"); }) }), + TestList("Field", new[] + { + TestCase("EQ succeeds", () => + { + var field = Field.EQ("Test", 14); + Expect.equal(field.Name, "Test", "Field name incorrect"); + Expect.equal(field.Op, Op.EQ, "Operator incorrect"); + Expect.equal(field.Value, 14, "Value incorrect"); + }), + TestCase("GT succeeds", () => + { + var field = Field.GT("Great", "night"); + Expect.equal(field.Name, "Great", "Field name incorrect"); + Expect.equal(field.Op, Op.GT, "Operator incorrect"); + Expect.equal(field.Value, "night", "Value incorrect"); + }), + TestCase("GE succeeds", () => + { + var field = Field.GE("Nice", 88L); + Expect.equal(field.Name, "Nice", "Field name incorrect"); + Expect.equal(field.Op, Op.GE, "Operator incorrect"); + Expect.equal(field.Value, 88L, "Value incorrect"); + }), + TestCase("LT succeeds", () => + { + var field = Field.LT("Lesser", "seven"); + Expect.equal(field.Name, "Lesser", "Field name incorrect"); + Expect.equal(field.Op, Op.LT, "Operator incorrect"); + Expect.equal(field.Value, "seven", "Value incorrect"); + }), + TestCase("LE succeeds", () => + { + var field = Field.LE("Nobody", "KNOWS"); + Expect.equal(field.Name, "Nobody", "Field name incorrect"); + Expect.equal(field.Op, Op.LE, "Operator incorrect"); + Expect.equal(field.Value, "KNOWS", "Value incorrect"); + }), + TestCase("NE succeeds", () => + { + var field = Field.NE("Park", "here"); + Expect.equal(field.Name, "Park", "Field name incorrect"); + Expect.equal(field.Op, Op.NE, "Operator incorrect"); + Expect.equal(field.Value, "here", "Value incorrect"); + }), + TestCase("EX succeeds", () => + { + var field = Field.EX("Groovy"); + Expect.equal(field.Name, "Groovy", "Field name incorrect"); + Expect.equal(field.Op, Op.EX, "Operator incorrect"); + }), + TestCase("NEX succeeds", () => + { + var field = Field.NEX("Rad"); + Expect.equal(field.Name, "Rad", "Field name incorrect"); + Expect.equal(field.Op, Op.NEX, "Operator incorrect"); + }) + }), TestList("Query", new[] { TestCase("SelectFromTable succeeds", () => diff --git a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs index 89e2178..cc5061d 100644 --- a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs +++ b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs @@ -219,7 +219,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var theCount = await conn.CountByField(SqliteDb.TableName, "Value", Op.EQ, "purple"); + var theCount = await conn.CountByField(SqliteDb.TableName, Field.EQ("Value", "purple")); Expect.equal(theCount, 2L, "There should have been 2 matching documents"); }), TestList("ExistsById", new[] @@ -251,7 +251,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var exists = await conn.ExistsByField(SqliteDb.TableName, "NumValue", Op.GE, 10); + var exists = await conn.ExistsByField(SqliteDb.TableName, Field.GE("NumValue", 10)); Expect.isTrue(exists, "There should have been existing documents"); }), TestCase("succeeds when no matching documents exist", async () => @@ -260,7 +260,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var exists = await conn.ExistsByField(SqliteDb.TableName, "Nothing", Op.EQ, "none"); + var exists = await conn.ExistsByField(SqliteDb.TableName, Field.EQ("Nothing", "none")); Expect.isFalse(exists, "There should not have been any existing documents"); }) }), @@ -316,7 +316,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var docs = await conn.FindByField(SqliteDb.TableName, "NumValue", Op.GT, 15); + var docs = await conn.FindByField(SqliteDb.TableName, Field.GT("NumValue", 15)); Expect.equal(docs.Count, 2, "There should have been two documents returned"); }), TestCase("succeeds when documents are not found", async () => @@ -325,7 +325,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var docs = await conn.FindByField(SqliteDb.TableName, "Value", Op.EQ, "mauve"); + var docs = await conn.FindByField(SqliteDb.TableName, Field.EQ("Value", "mauve")); Expect.isEmpty(docs, "There should have been no documents returned"); }) }), @@ -337,7 +337,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var doc = await conn.FindFirstByField(SqliteDb.TableName, "Value", Op.EQ, "another"); + var doc = await conn.FindFirstByField(SqliteDb.TableName, Field.EQ("Value", "another")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc!.Id, "two", "The incorrect document was returned"); }), @@ -347,7 +347,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var doc = await conn.FindFirstByField(SqliteDb.TableName, "Sub.Foo", Op.EQ, "green"); + var doc = await conn.FindFirstByField(SqliteDb.TableName, Field.EQ("Sub.Foo", "green")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned"); }), @@ -357,7 +357,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - var doc = await conn.FindFirstByField(SqliteDb.TableName, "Value", Op.EQ, "absent"); + var doc = await conn.FindFirstByField(SqliteDb.TableName, Field.EQ("Value", "absent")); Expect.isNull(doc, "There should not have been a document returned"); }) }), @@ -452,8 +452,8 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.PatchByField(SqliteDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 }); - var after = await conn.CountByField(SqliteDb.TableName, "NumValue", Op.EQ, 77); + await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); + var after = await conn.CountByField(SqliteDb.TableName, Field.EQ("NumValue", 77)); Expect.equal(after, 2L, "There should have been 2 documents returned"); }), TestCase("succeeds when no document is updated", async () => @@ -464,7 +464,7 @@ public static class SqliteCSharpExtensionTests Expect.isEmpty(before, "There should have been no documents returned"); // This not raising an exception is the test - await conn.PatchByField(SqliteDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" }); + await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); }) }), TestList("RemoveFieldById", new[] @@ -506,7 +506,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.RemoveFieldByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Sub"); + await conn.RemoveFieldByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Sub"); var updated = await Find.ById(SqliteDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); @@ -518,7 +518,7 @@ public static class SqliteCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Nothing"); + await conn.RemoveFieldByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Nothing"); }), TestCase("succeeds when no document is matched", async () => { @@ -526,7 +526,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); // This not raising an exception is the test - await conn.RemoveFieldByField(SqliteDb.TableName, "Abracadabra", Op.NE, "apple", "Value"); + await conn.RemoveFieldByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), "Value"); }) }), TestList("DeleteById", new[] @@ -560,7 +560,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.DeleteByField(SqliteDb.TableName, "Value", Op.NE, "purple"); + await conn.DeleteByField(SqliteDb.TableName, Field.NE("Value", "purple")); var remaining = await conn.CountAll(SqliteDb.TableName); Expect.equal(remaining, 2L, "There should have been 2 documents remaining"); }), @@ -570,7 +570,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.DeleteByField(SqliteDb.TableName, "Value", Op.EQ, "crimson"); + await conn.DeleteByField(SqliteDb.TableName, Field.EQ("Value", "crimson")); var remaining = await conn.CountAll(SqliteDb.TableName); Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); }) diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs index cd7440d..c86cc07 100644 --- a/src/Tests.CSharp/SqliteCSharpTests.cs +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -312,7 +312,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var theCount = await Count.ByField(SqliteDb.TableName, "Value", Op.EQ, "purple"); + var theCount = await Count.ByField(SqliteDb.TableName, Field.EQ("Value", "purple")); Expect.equal(theCount, 2L, "There should have been 2 matching documents"); }) }), @@ -344,7 +344,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var exists = await Exists.ByField(SqliteDb.TableName, "NumValue", Op.GE, 10); + var exists = await Exists.ByField(SqliteDb.TableName, Field.GE("NumValue", 10)); Expect.isTrue(exists, "There should have been existing documents"); }), TestCase("succeeds when no matching documents exist", async () => @@ -352,7 +352,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var exists = await Exists.ByField(SqliteDb.TableName, "Nothing", Op.EQ, "none"); + var exists = await Exists.ByField(SqliteDb.TableName, Field.EQ("Nothing", "none")); Expect.isFalse(exists, "There should not have been any existing documents"); }) }) @@ -406,7 +406,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var docs = await Find.ByField(SqliteDb.TableName, "NumValue", Op.GT, 15); + var docs = await Find.ByField(SqliteDb.TableName, Field.GT("NumValue", 15)); Expect.equal(docs.Count, 2, "There should have been two documents returned"); }), TestCase("succeeds when documents are not found", async () => @@ -414,7 +414,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var docs = await Find.ByField(SqliteDb.TableName, "Value", Op.EQ, "mauve"); + var docs = await Find.ByField(SqliteDb.TableName, Field.EQ("Value", "mauve")); Expect.isEmpty(docs, "There should have been no documents returned"); }) }), @@ -425,7 +425,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var doc = await Find.FirstByField(SqliteDb.TableName, "Value", Op.EQ, "another"); + var doc = await Find.FirstByField(SqliteDb.TableName, Field.EQ("Value", "another")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc!.Id, "two", "The incorrect document was returned"); }), @@ -434,7 +434,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var doc = await Find.FirstByField(SqliteDb.TableName, "Sub.Foo", Op.EQ, "green"); + var doc = await Find.FirstByField(SqliteDb.TableName, Field.EQ("Sub.Foo", "green")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned"); }), @@ -443,7 +443,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var doc = await Find.FirstByField(SqliteDb.TableName, "Value", Op.EQ, "absent"); + var doc = await Find.FirstByField(SqliteDb.TableName, Field.EQ("Value", "absent")); Expect.isNull(doc, "There should not have been a document returned"); }) }) @@ -540,8 +540,8 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await Patch.ByField(SqliteDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 }); - var after = await Count.ByField(SqliteDb.TableName, "NumValue", Op.EQ, 77); + await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); + var after = await Count.ByField(SqliteDb.TableName, Field.EQ("NumValue", 77)); Expect.equal(after, 2L, "There should have been 2 documents returned"); }), TestCase("succeeds when no document is updated", async () => @@ -552,7 +552,7 @@ public static class SqliteCSharpTests Expect.isEmpty(before, "There should have been no documents returned"); // This not raising an exception is the test - await Patch.ByField(SqliteDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" }); + await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); }) }) }), @@ -593,7 +593,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await RemoveField.ByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Sub"); + await RemoveField.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Sub"); var updated = await Find.ById(SqliteDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); @@ -604,14 +604,14 @@ public static class SqliteCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveField.ByField(SqliteDb.TableName, "NumValue", Op.EQ, 17, "Nothing"); + await RemoveField.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Nothing"); }), 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.ByField(SqliteDb.TableName, "Abracadabra", Op.NE, "apple", "Value"); + await RemoveField.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), "Value"); }) }) }), @@ -645,7 +645,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await Delete.ByField(SqliteDb.TableName, "Value", Op.NE, "purple"); + await Delete.ByField(SqliteDb.TableName, Field.NE("Value", "purple")); var remaining = await Count.All(SqliteDb.TableName); Expect.equal(remaining, 2L, "There should have been 2 documents remaining"); }), @@ -654,7 +654,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await Delete.ByField(SqliteDb.TableName, "Value", Op.EQ, "crimson"); + await Delete.ByField(SqliteDb.TableName, Field.EQ("Value", "crimson")); var remaining = await Count.All(SqliteDb.TableName); Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); }) diff --git a/src/Tests/SqliteExtensionTests.fs b/src/Tests/SqliteExtensionTests.fs index b7586e3..ee89a9b 100644 --- a/src/Tests/SqliteExtensionTests.fs +++ b/src/Tests/SqliteExtensionTests.fs @@ -118,7 +118,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! theCount = conn.countByField SqliteDb.TableName "Value" EQ "purple" + let! theCount = conn.countByField SqliteDb.TableName (Field.EQ "Value" "purple") Expect.equal theCount 2L "There should have been 2 matching documents" } testList "existsById" [ @@ -145,7 +145,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! exists = conn.existsByField SqliteDb.TableName "NumValue" EQ 10 + let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "NumValue" 10) Expect.isTrue exists "There should have been existing documents" } testTask "succeeds when no matching documents exist" { @@ -153,7 +153,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! exists = conn.existsByField SqliteDb.TableName "Nothing" EQ "none" + let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "Nothing" "none") Expect.isFalse exists "There should not have been any existing documents" } ] @@ -206,7 +206,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! docs = conn.findByField SqliteDb.TableName "Sub.Foo" EQ "green" + let! docs = conn.findByField SqliteDb.TableName (Field.EQ "Sub.Foo" "green") Expect.equal (List.length docs) 2 "There should have been two documents returned" } testTask "succeeds when documents are not found" { @@ -214,7 +214,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! docs = conn.findByField SqliteDb.TableName "Value" EQ "mauve" + let! docs = conn.findByField SqliteDb.TableName (Field.EQ "Value" "mauve") Expect.isTrue (List.isEmpty docs) "There should have been no documents returned" } ] @@ -224,7 +224,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! doc = conn.findFirstByField SqliteDb.TableName "Value" EQ "another" + let! doc = conn.findFirstByField SqliteDb.TableName (Field.EQ "Value" "another") Expect.isTrue (Option.isSome doc) "There should have been a document returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned" } @@ -233,7 +233,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! doc = conn.findFirstByField SqliteDb.TableName "Sub.Foo" EQ "green" + let! doc = conn.findFirstByField SqliteDb.TableName (Field.EQ "Sub.Foo" "green") Expect.isTrue (Option.isSome doc) "There should have been a document returned" Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned" } @@ -242,7 +242,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - let! doc = conn.findFirstByField SqliteDb.TableName "Value" EQ "absent" + let! doc = conn.findFirstByField SqliteDb.TableName (Field.EQ "Value" "absent") Expect.isFalse (Option.isSome doc) "There should not have been a document returned" } ] @@ -330,8 +330,8 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - do! conn.patchByField SqliteDb.TableName "Value" EQ "purple" {| NumValue = 77 |} - let! after = conn.countByField SqliteDb.TableName "NumValue" EQ 77 + do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} + let! after = conn.countByField SqliteDb.TableName (Field.EQ "NumValue" 77) Expect.equal after 2L "There should have been 2 documents returned" } testTask "succeeds when no document is updated" { @@ -342,7 +342,7 @@ let integrationTests = Expect.isEmpty before "There should have been no documents returned" // This not raising an exception is the test - do! conn.patchByField SqliteDb.TableName "Value" EQ "burgundy" {| Foo = "green" |} + do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |} } ] testList "removeFieldById" [ @@ -381,7 +381,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - do! conn.removeFieldByField SqliteDb.TableName "NumValue" EQ 17 "Sub" + do! conn.removeFieldByField SqliteDb.TableName (Field.EQ "NumValue" 17) "Sub" try let! _ = conn.findById SqliteDb.TableName "four" Expect.isTrue false "The updated document should have failed to parse" @@ -395,14 +395,14 @@ let integrationTests = do! loadDocs () // This not raising an exception is the test - do! conn.removeFieldByField SqliteDb.TableName "NumValue" EQ 17 "Nothing" + do! conn.removeFieldByField SqliteDb.TableName (Field.EQ "NumValue" 17) "Nothing" } testTask "succeeds when no document is matched" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () // This not raising an exception is the test - do! conn.removeFieldByField SqliteDb.TableName "Abracadabra" NE "apple" "Value" + do! conn.removeFieldByField SqliteDb.TableName (Field.NE "Abracadabra" "apple") "Value" } ] testList "deleteById" [ @@ -431,7 +431,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - do! conn.deleteByField SqliteDb.TableName "Value" NE "purple" + do! conn.deleteByField SqliteDb.TableName (Field.NE "Value" "purple") let! remaining = conn.countAll SqliteDb.TableName Expect.equal remaining 2L "There should have been 2 documents remaining" } @@ -440,7 +440,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - do! conn.deleteByField SqliteDb.TableName "Value" EQ "crimson" + do! conn.deleteByField SqliteDb.TableName (Field.EQ "Value" "crimson") let! remaining = conn.countAll SqliteDb.TableName Expect.equal remaining 5L "There should have been 5 documents remaining" } diff --git a/src/Tests/SqliteTests.fs b/src/Tests/SqliteTests.fs index 613d0ac..3f09467 100644 --- a/src/Tests/SqliteTests.fs +++ b/src/Tests/SqliteTests.fs @@ -292,7 +292,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - let! theCount = Count.byField SqliteDb.TableName "Value" EQ "purple" + let! theCount = Count.byField SqliteDb.TableName (Field.EQ "Value" "purple") Expect.equal theCount 2L "There should have been 2 matching documents" } ] @@ -318,14 +318,14 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - let! exists = Exists.byField SqliteDb.TableName "NumValue" EQ 10 + let! exists = Exists.byField SqliteDb.TableName (Field.EQ "NumValue" 10) Expect.isTrue exists "There should have been existing documents" } testTask "succeeds when no matching documents exist" { use! db = SqliteDb.BuildDb() do! loadDocs () - let! exists = Exists.byField SqliteDb.TableName "Nothing" LT "none" + let! exists = Exists.byField SqliteDb.TableName (Field.LT "Nothing" "none") Expect.isFalse exists "There should not have been any existing documents" } ] @@ -375,14 +375,14 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - let! docs = Find.byField SqliteDb.TableName "NumValue" GT 15 + let! docs = Find.byField SqliteDb.TableName (Field.GT "NumValue" 15) Expect.equal (List.length docs) 2 "There should have been two documents returned" } testTask "succeeds when documents are not found" { use! db = SqliteDb.BuildDb() do! loadDocs () - let! docs = Find.byField SqliteDb.TableName "NumValue" GT 100 + let! docs = Find.byField SqliteDb.TableName (Field.GT "NumValue" 100) Expect.isTrue (List.isEmpty docs) "There should have been no documents returned" } ] @@ -391,7 +391,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - let! doc = Find.firstByField SqliteDb.TableName "Value" EQ "another" + let! doc = Find.firstByField SqliteDb.TableName (Field.EQ "Value" "another") Expect.isTrue (Option.isSome doc) "There should have been a document returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned" } @@ -399,7 +399,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - let! doc = Find.firstByField SqliteDb.TableName "Sub.Foo" EQ "green" + let! doc = Find.firstByField SqliteDb.TableName (Field.EQ "Sub.Foo" "green") Expect.isTrue (Option.isSome doc) "There should have been a document returned" Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned" } @@ -407,7 +407,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - let! doc = Find.firstByField SqliteDb.TableName "Value" EQ "absent" + let! doc = Find.firstByField SqliteDb.TableName (Field.EQ "Value" "absent") Expect.isFalse (Option.isSome doc) "There should not have been a document returned" } ] @@ -489,8 +489,8 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - do! Patch.byField SqliteDb.TableName "Value" EQ "purple" {| NumValue = 77 |} - let! after = Count.byField SqliteDb.TableName "NumValue" EQ 77 + do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} + let! after = Count.byField SqliteDb.TableName (Field.EQ "NumValue" 77) Expect.equal after 2L "There should have been 2 documents returned" } testTask "succeeds when no document is updated" { @@ -500,7 +500,7 @@ let integrationTests = Expect.isEmpty before "There should have been no documents returned" // This not raising an exception is the test - do! Patch.byField SqliteDb.TableName "Value" EQ "burgundy" {| Foo = "green" |} + do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |} } ] ] @@ -537,7 +537,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - do! RemoveField.byField SqliteDb.TableName "NumValue" EQ 17 "Sub" + do! RemoveField.byField SqliteDb.TableName (Field.EQ "NumValue" 17) "Sub" try let! _ = Find.byId SqliteDb.TableName "four" Expect.isTrue false "The updated document should have failed to parse" @@ -550,13 +550,13 @@ let integrationTests = do! loadDocs () // This not raising an exception is the test - do! RemoveField.byField SqliteDb.TableName "NumValue" EQ 17 "Nothing" + do! RemoveField.byField SqliteDb.TableName (Field.EQ "NumValue" 17) "Nothing" } testTask "succeeds when no document is matched" { use! db = SqliteDb.BuildDb() // This not raising an exception is the test - do! RemoveField.byField SqliteDb.TableName "Abracadabra" NE "apple" "Value" + do! RemoveField.byField SqliteDb.TableName (Field.NE "Abracadabra" "apple") "Value" } ] ] @@ -584,7 +584,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - do! Delete.byField SqliteDb.TableName "Value" NE "purple" + do! Delete.byField SqliteDb.TableName (Field.NE "Value" "purple") let! remaining = Count.all SqliteDb.TableName Expect.equal remaining 2L "There should have been 2 documents remaining" } @@ -592,7 +592,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - do! Delete.byField SqliteDb.TableName "Value" EQ "crimson" + do! Delete.byField SqliteDb.TableName (Field.EQ "Value" "crimson") let! remaining = Count.all SqliteDb.TableName Expect.equal remaining 5L "There should have been 5 documents remaining" } -- 2.45.1 From f2836bd759cb1e1c340ca4a5249b80cce666a041 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 23 Jan 2024 12:03:01 -0500 Subject: [PATCH 4/6] Implement Field throughout - Do not add a parameter for EX/NEX --- src/Common/Library.fs | 43 +++++----- src/Postgres/Extensions.fs | 48 +++++------ src/Postgres/Library.fs | 84 ++++++++++--------- src/Sqlite/Library.fs | 56 ++++++------- src/Tests.CSharp/CommonCSharpTests.cs | 12 +-- .../PostgresCSharpExtensionTests.cs | 26 +++--- src/Tests.CSharp/PostgresCSharpTests.cs | 46 ++++++---- src/Tests.CSharp/SqliteCSharpTests.cs | 15 +++- src/Tests/CommonTests.fs | 12 +-- src/Tests/PostgresExtensionTests.fs | 26 +++--- src/Tests/PostgresTests.fs | 54 +++++++----- src/Tests/SqliteTests.fs | 22 +++-- 12 files changed, 237 insertions(+), 207 deletions(-) diff --git a/src/Common/Library.fs b/src/Common/Library.fs index 47caade..56b6d1b 100644 --- a/src/Common/Library.fs +++ b/src/Common/Library.fs @@ -44,35 +44,35 @@ type Field = { Value: obj } with - /// Create an equals (=) field criteria + /// Create an equals (=) field criterion static member EQ name (value: obj) = { Name = name; Op = EQ; Value = value } - /// Create a greater than (>) field criteria + /// Create a greater than (>) field criterion static member GT name (value: obj) = { Name = name; Op = GT; Value = value } - /// Create a greater than or equal to (>=) field criteria + /// Create a greater than or equal to (>=) field criterion static member GE name (value: obj) = { Name = name; Op = GE; Value = value } - /// Create a less than (<) field criteria + /// Create a less than (<) field criterion static member LT name (value: obj) = { Name = name; Op = LT; Value = value } - /// Create a less than or equal to (<=) field criteria + /// Create a less than or equal to (<=) field criterion static member LE name (value: obj) = { Name = name; Op = LE; Value = value } - /// Create a not equals (<>) field criteria + /// Create a not equals (<>) field criterion static member NE name (value: obj) = { Name = name; Op = NE; Value = value } - /// Create an exists (IS NOT NULL) field criteria + /// Create an exists (IS NOT NULL) field criterion static member EX name = { Name = name; Op = EX; Value = obj () } - /// Create an not exists (IS NULL) field criteria + /// Create an not exists (IS NULL) field criterion static member NEX name = { Name = name; Op = NEX; Value = obj () } @@ -152,17 +152,14 @@ module Query = /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document [] - let whereByField fieldName op paramName = - let theRest = - match op with - | EX | NEX -> string op - | _ -> $"{op} %s{paramName}" - $"data ->> '%s{fieldName}' {theRest}" + let whereByField field paramName = + let theRest = match field.Op with EX | NEX -> string field.Op | _ -> $"{field.Op} %s{paramName}" + $"data ->> '%s{field.Name}' {theRest}" /// Create a WHERE clause fragment to implement an ID-based query [] let whereById paramName = - whereByField (Configuration.idField ()) EQ paramName + whereByField (Field.EQ (Configuration.idField ()) 0) paramName /// Queries to define tables and indexes module Definition = @@ -223,8 +220,8 @@ module Query = /// Query to count matching documents using a text comparison on a JSON field [] - let byField tableName fieldName op = - $"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField fieldName op "@field"}""" + let byField tableName field = + $"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField field "@field"}""" /// Queries for determining document existence module Exists = @@ -236,8 +233,8 @@ module Query = /// Query to determine if documents exist using a comparison on a JSON field [] - let byField tableName fieldName op = - $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField fieldName op "@field"}) AS it""" + let byField tableName field = + $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField field "@field"}) AS it""" /// Queries for retrieving documents module Find = @@ -249,8 +246,8 @@ module Query = /// Query to retrieve documents using a comparison on a JSON field [] - let byField tableName fieldName op = - $"""{selectFromTable tableName} WHERE {whereByField fieldName op "@field"}""" + let byField tableName field = + $"""{selectFromTable tableName} WHERE {whereByField field "@field"}""" /// Queries to delete documents module Delete = @@ -262,5 +259,5 @@ module Query = /// Query to delete documents using a comparison on a JSON field [] - let byField tableName fieldName op = - $"""DELETE FROM %s{tableName} WHERE {whereByField fieldName op "@field"}""" + let byField tableName field = + $"""DELETE FROM %s{tableName} WHERE {whereByField field "@field"}""" diff --git a/src/Postgres/Extensions.fs b/src/Postgres/Extensions.fs index 4602341..9b89b84 100644 --- a/src/Postgres/Extensions.fs +++ b/src/Postgres/Extensions.fs @@ -50,8 +50,8 @@ module Extensions = WithProps.Count.all tableName (Sql.existingConnection conn) /// Count matching documents using a JSON field comparison query (->> =) - member conn.countByField tableName fieldName op (value: obj) = - WithProps.Count.byField tableName fieldName op value (Sql.existingConnection conn) + member conn.countByField tableName field = + WithProps.Count.byField tableName field (Sql.existingConnection conn) /// Count matching documents using a JSON containment query (@>) member conn.countByContains tableName criteria = @@ -66,8 +66,8 @@ module Extensions = WithProps.Exists.byId tableName docId (Sql.existingConnection conn) /// Determine if documents exist using a JSON field comparison query (->> =) - member conn.existsByField tableName fieldName op (value: obj) = - WithProps.Exists.byField tableName fieldName op value (Sql.existingConnection conn) + member conn.existsByField tableName field = + WithProps.Exists.byField tableName field (Sql.existingConnection conn) /// Determine if documents exist using a JSON containment query (@>) member conn.existsByContains tableName criteria = @@ -86,8 +86,8 @@ module Extensions = WithProps.Find.byId<'TKey, 'TDoc> tableName docId (Sql.existingConnection conn) /// Retrieve documents matching a JSON field comparison query (->> =) - member conn.findByField<'TDoc> tableName fieldName op (value: obj) = - WithProps.Find.byField<'TDoc> tableName fieldName op value (Sql.existingConnection conn) + member conn.findByField<'TDoc> tableName field = + WithProps.Find.byField<'TDoc> tableName field (Sql.existingConnection conn) /// Retrieve documents matching a JSON containment query (@>) member conn.findByContains<'TDoc> tableName (criteria: obj) = @@ -98,8 +98,8 @@ module Extensions = WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn) /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found - member conn.findFirstByField<'TDoc> tableName fieldName op (value: obj) = - WithProps.Find.firstByField<'TDoc> tableName fieldName op value (Sql.existingConnection conn) + member conn.findFirstByField<'TDoc> tableName field = + WithProps.Find.firstByField<'TDoc> tableName field (Sql.existingConnection conn) /// Retrieve the first document matching a JSON containment query (@>); returns None if not found member conn.findFirstByContains<'TDoc> tableName (criteria: obj) = @@ -122,8 +122,8 @@ module Extensions = WithProps.Patch.byId tableName docId patch (Sql.existingConnection conn) /// Patch documents using a JSON field comparison query in the WHERE clause (->> =) - member conn.patchByField tableName fieldName op (value: obj) (patch: 'TPatch) = - WithProps.Patch.byField tableName fieldName op value patch (Sql.existingConnection conn) + member conn.patchByField tableName field (patch: 'TPatch) = + WithProps.Patch.byField tableName field patch (Sql.existingConnection conn) /// Patch documents using a JSON containment query in the WHERE clause (@>) member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) = @@ -138,8 +138,8 @@ module Extensions = WithProps.Delete.byId tableName docId (Sql.existingConnection conn) /// Delete documents by matching a JSON field comparison query (->> =) - member conn.deleteByField tableName fieldName op (value: obj) = - WithProps.Delete.byField tableName fieldName op value (Sql.existingConnection conn) + member conn.deleteByField tableName field = + WithProps.Delete.byField tableName field (Sql.existingConnection conn) /// Delete documents by matching a JSON containment query (@>) member conn.deleteByContains tableName (criteria: 'TContains) = @@ -209,8 +209,8 @@ type NpgsqlConnectionCSharpExtensions = /// Count matching documents using a JSON field comparison query (->> =) [] - static member inline CountByField(conn, tableName, fieldName, op, value: obj) = - WithProps.Count.byField tableName fieldName op value (Sql.existingConnection conn) + static member inline CountByField(conn, tableName, field) = + WithProps.Count.byField tableName field (Sql.existingConnection conn) /// Count matching documents using a JSON containment query (@>) [] @@ -229,8 +229,8 @@ type NpgsqlConnectionCSharpExtensions = /// Determine if documents exist using a JSON field comparison query (->> =) [] - static member inline ExistsByField(conn, tableName, fieldName, op, value: obj) = - WithProps.Exists.byField tableName fieldName op value (Sql.existingConnection conn) + static member inline ExistsByField(conn, tableName, field) = + WithProps.Exists.byField tableName field (Sql.existingConnection conn) /// Determine if documents exist using a JSON containment query (@>) [] @@ -254,8 +254,8 @@ type NpgsqlConnectionCSharpExtensions = /// Retrieve documents matching a JSON field comparison query (->> =) [] - static member inline FindByField<'TDoc>(conn, tableName, fieldName, op, value: obj) = - WithProps.Find.ByField<'TDoc>(tableName, fieldName, op, value, Sql.existingConnection conn) + static member inline FindByField<'TDoc>(conn, tableName, field) = + WithProps.Find.ByField<'TDoc>(tableName, field, Sql.existingConnection conn) /// Retrieve documents matching a JSON containment query (@>) [] @@ -269,8 +269,8 @@ type NpgsqlConnectionCSharpExtensions = /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found [] - static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, fieldName, op, value: obj) = - WithProps.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, Sql.existingConnection conn) + static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) = + WithProps.Find.FirstByField<'TDoc>(tableName, field, Sql.existingConnection conn) /// Retrieve the first document matching a JSON containment query (@>); returns None if not found [] @@ -299,8 +299,8 @@ type NpgsqlConnectionCSharpExtensions = /// Patch documents using a JSON field comparison query in the WHERE clause (->> =) [] - static member inline PatchByField(conn, tableName, fieldName, op, value: obj, patch: 'TPatch) = - WithProps.Patch.byField tableName fieldName op value patch (Sql.existingConnection conn) + static member inline PatchByField(conn, tableName, field, patch: 'TPatch) = + WithProps.Patch.byField tableName field patch (Sql.existingConnection conn) /// Patch documents using a JSON containment query in the WHERE clause (@>) [] @@ -319,8 +319,8 @@ type NpgsqlConnectionCSharpExtensions = /// Delete documents by matching a JSON field comparison query (->> =) [] - static member inline DeleteByField(conn, tableName, fieldName, op, value: obj) = - WithProps.Delete.byField tableName fieldName op value (Sql.existingConnection conn) + static member inline DeleteByField(conn, tableName, field) = + WithProps.Delete.byField tableName field (Sql.existingConnection conn) /// Delete documents by matching a JSON containment query (@>) [] diff --git a/src/Postgres/Library.fs b/src/Postgres/Library.fs index 5fcb7cc..bb496ee 100644 --- a/src/Postgres/Library.fs +++ b/src/Postgres/Library.fs @@ -64,9 +64,18 @@ module Parameters = name, Sql.jsonb (Configuration.serializer().Serialize it) /// Create a JSON field parameter (name "@field") - [] - let fieldParam (value: obj) = - "@field", Sql.parameter (NpgsqlParameter("@field", value)) + [] + let addFieldParam field parameters = + match field.Op with + | EX | NEX -> parameters + | _ -> ("@field", Sql.parameter (NpgsqlParameter("@field", field.Value))) :: parameters + + /// Create a JSON field parameter (name "@field") + let AddField field parameters = + match field.Op with + | EX | NEX -> parameters + | _ -> + ("@field", Sql.parameter (NpgsqlParameter("@field", field.Value))) |> Seq.singleton |> Seq.append parameters /// An empty parameter sequence [] @@ -152,8 +161,8 @@ module Query = /// Query to patch documents match a JSON field comparison (->> =) [] - let byField tableName fieldName op = - $"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereByField fieldName op "@field"}""" + let byField tableName field = + $"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereByField field "@field"}""" /// Query to patch documents matching a JSON containment query (@>) [] @@ -302,8 +311,8 @@ module WithProps = /// Count matching documents using a JSON field comparison (->> =) [] - let byField tableName fieldName op (value: obj) sqlProps = - Custom.scalar (Query.Count.byField tableName fieldName op) [ fieldParam value ] toCount sqlProps + let byField tableName field sqlProps = + Custom.scalar (Query.Count.byField tableName field) (addFieldParam field []) toCount sqlProps /// Count matching documents using a JSON containment query (@>) [] @@ -326,8 +335,8 @@ module WithProps = /// Determine if a document exists using a JSON field comparison (->> =) [] - let byField tableName fieldName op (value: obj) sqlProps = - Custom.scalar (Query.Exists.byField tableName fieldName op) [ fieldParam value ] toExists sqlProps + let byField tableName field sqlProps = + Custom.scalar (Query.Exists.byField tableName field) (addFieldParam field []) toExists sqlProps /// Determine if a document exists using a JSON containment query (@>) [] @@ -363,13 +372,12 @@ module WithProps = /// Retrieve documents matching a JSON field comparison (->> =) [] - let byField<'TDoc> tableName fieldName op (value: obj) sqlProps = - Custom.list<'TDoc> (Query.Find.byField tableName fieldName op) [ fieldParam value ] fromData<'TDoc> sqlProps + let byField<'TDoc> tableName field sqlProps = + Custom.list<'TDoc> (Query.Find.byField tableName field) (addFieldParam field []) fromData<'TDoc> sqlProps /// Retrieve documents matching a JSON field comparison (->> =) - let ByField<'TDoc>(tableName, fieldName, op, value: obj, sqlProps) = - Custom.List<'TDoc>( - Query.Find.byField tableName fieldName op, [ fieldParam value ], fromData<'TDoc>, sqlProps) + let ByField<'TDoc>(tableName, field, sqlProps) = + Custom.List<'TDoc>(Query.Find.byField tableName field, addFieldParam field [], fromData<'TDoc>, sqlProps) /// Retrieve documents matching a JSON containment query (@>) [] @@ -395,14 +403,14 @@ module WithProps = /// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found [] - let firstByField<'TDoc> tableName fieldName op (value: obj) sqlProps = + let firstByField<'TDoc> tableName field sqlProps = Custom.single<'TDoc> - $"{Query.Find.byField tableName fieldName op} LIMIT 1" [ fieldParam value ] fromData<'TDoc> sqlProps + $"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam field []) fromData<'TDoc> sqlProps /// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found - let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value: obj, sqlProps) = + let FirstByField<'TDoc when 'TDoc: null>(tableName, field, sqlProps) = Custom.Single<'TDoc>( - $"{Query.Find.byField tableName fieldName op} LIMIT 1", [ fieldParam value ], fromData<'TDoc>, sqlProps) + $"{Query.Find.byField tableName field} LIMIT 1", addFieldParam field [], fromData<'TDoc>, sqlProps) /// Retrieve the first document matching a JSON containment query (@>); returns None if not found [] @@ -461,9 +469,9 @@ module WithProps = /// Patch documents using a JSON field comparison query in the WHERE clause (->> =) [] - let byField tableName fieldName op (value: obj) (patch: 'TPatch) sqlProps = + let byField tableName field (patch: 'TPatch) sqlProps = Custom.nonQuery - (Query.Patch.byField tableName fieldName op) [ jsonParam "@data" patch; fieldParam value ] sqlProps + (Query.Patch.byField tableName field) (addFieldParam field [ jsonParam "@data" patch ]) sqlProps /// Patch documents using a JSON containment query in the WHERE clause (@>) [] @@ -488,8 +496,8 @@ module WithProps = /// Delete documents by matching a JSON field comparison query (->> =) [] - let byField tableName fieldName op (value: obj) sqlProps = - Custom.nonQuery (Query.Delete.byField tableName fieldName op) [ fieldParam value ] sqlProps + let byField tableName field sqlProps = + Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam field []) sqlProps /// Delete documents by matching a JSON contains query (@>) [] @@ -585,8 +593,8 @@ module Count = /// Count matching documents using a JSON field comparison query (->> =) [] - let byField tableName fieldName op (value: obj) = - WithProps.Count.byField tableName fieldName op value (fromDataSource ()) + let byField tableName field = + WithProps.Count.byField tableName field (fromDataSource ()) /// Count matching documents using a JSON containment query (@>) [] @@ -610,8 +618,8 @@ module Exists = /// Determine if documents exist using a JSON field comparison query (->> =) [] - let byField tableName fieldName op (value: obj) = - WithProps.Exists.byField tableName fieldName op value (fromDataSource ()) + let byField tableName field = + WithProps.Exists.byField tableName field (fromDataSource ()) /// Determine if documents exist using a JSON containment query (@>) [] @@ -648,12 +656,12 @@ module Find = /// Retrieve documents matching a JSON field comparison query (->> =) [] - let byField<'TDoc> tableName fieldName op (value: obj) = - WithProps.Find.byField<'TDoc> tableName fieldName op value (fromDataSource ()) + let byField<'TDoc> tableName field = + WithProps.Find.byField<'TDoc> tableName field (fromDataSource ()) /// Retrieve documents matching a JSON field comparison query (->> =) - let ByField<'TDoc>(tableName, fieldName, op, value: obj) = - WithProps.Find.ByField<'TDoc>(tableName, fieldName, op, value, fromDataSource ()) + let ByField<'TDoc>(tableName, field) = + WithProps.Find.ByField<'TDoc>(tableName, field, fromDataSource ()) /// Retrieve documents matching a JSON containment query (@>) [] @@ -675,12 +683,12 @@ module Find = /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found [] - let firstByField<'TDoc> tableName fieldName op (value: obj) = - WithProps.Find.firstByField<'TDoc> tableName fieldName op value (fromDataSource ()) + let firstByField<'TDoc> tableName field = + WithProps.Find.firstByField<'TDoc> tableName field (fromDataSource ()) /// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found - let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value: obj) = - WithProps.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, fromDataSource ()) + let FirstByField<'TDoc when 'TDoc: null>(tableName, field) = + WithProps.Find.FirstByField<'TDoc>(tableName, field, fromDataSource ()) /// Retrieve the first document matching a JSON containment query (@>); returns None if not found [] @@ -731,8 +739,8 @@ module Patch = /// Patch documents using a JSON field comparison query in the WHERE clause (->> =) [] - let byField tableName fieldName op (value: obj) (patch: 'TPatch) = - WithProps.Patch.byField tableName fieldName op value patch (fromDataSource ()) + let byField tableName field (patch: 'TPatch) = + WithProps.Patch.byField tableName field patch (fromDataSource ()) /// Patch documents using a JSON containment query in the WHERE clause (@>) [] @@ -756,8 +764,8 @@ module Delete = /// Delete documents by matching a JSON field comparison query (->> =) [] - let byField tableName fieldName op (value: obj) = - WithProps.Delete.byField tableName fieldName op value (fromDataSource ()) + let byField tableName field = + WithProps.Delete.byField tableName field (fromDataSource ()) /// Delete documents by matching a JSON containment query (@>) [] diff --git a/src/Sqlite/Library.fs b/src/Sqlite/Library.fs index 86b4ee6..12a72e8 100644 --- a/src/Sqlite/Library.fs +++ b/src/Sqlite/Library.fs @@ -49,10 +49,10 @@ module Query = /// Query to patch (partially update) a document via a comparison on a JSON field [] - let byField tableName fieldName op = + let byField tableName field = sprintf "UPDATE %s SET data = json_patch(data, json(@data)) WHERE %s" - tableName (Query.whereByField fieldName op "@field") + tableName (Query.whereByField field "@field") /// Queries to remove a field from a document module RemoveField = @@ -64,10 +64,8 @@ module Query = /// Query to remove a field from a document via a comparison on a JSON field within the document [] - let byField tableName fieldName op = - sprintf - "UPDATE %s SET data = json_remove(data, @name) WHERE %s" - tableName (Query.whereByField fieldName op "@field") + let byField tableName field = + $"""UPDATE %s{tableName} SET data = json_remove(data, @name) WHERE {Query.whereByField field "@field"}""" /// Parameter handling helpers @@ -85,9 +83,17 @@ module Parameters = SqliteParameter(name, Configuration.serializer().Serialize it) /// Create a JSON field parameter (name "@field") - [] - let fieldParam (value: obj) = - SqliteParameter("@field", value) + [] + let addFieldParam field parameters = + match field.Op with + | EX | NEX -> parameters + | _ -> SqliteParameter("@field", field.Value) :: parameters + + /// Create a JSON field parameter (name "@field") + let AddField field parameters = + match field.Op with + | EX | NEX -> parameters + | _ -> SqliteParameter("@field", field.Value) |> Seq.singleton |> Seq.append parameters /// Create a JSON field name parameter (name "@name") [] @@ -244,7 +250,7 @@ module WithConn = /// Count matching documents using a comparison on a JSON field [] let byField tableName field conn = - Custom.scalar (Query.Count.byField tableName field.Name field.Op) [ fieldParam field.Value ] toCount conn + Custom.scalar (Query.Count.byField tableName field) (addFieldParam field []) toCount conn /// Commands to determine if documents exist [] @@ -258,7 +264,7 @@ module WithConn = /// Determine if a document exists using a comparison on a JSON field [] let byField tableName field conn = - Custom.scalar (Query.Exists.byField tableName field.Name field.Op) [ fieldParam field.Value ] toExists conn + Custom.scalar (Query.Exists.byField tableName field) (addFieldParam field []) toExists conn /// Commands to retrieve documents [] @@ -285,30 +291,21 @@ module WithConn = /// Retrieve documents via a comparison on a JSON field [] let byField<'TDoc> tableName field conn = - Custom.list<'TDoc> - (Query.Find.byField tableName field.Name field.Op) [ fieldParam field.Value ] fromData<'TDoc> conn + Custom.list<'TDoc> (Query.Find.byField tableName field) (addFieldParam field []) fromData<'TDoc> conn /// Retrieve documents via a comparison on a JSON field let ByField<'TDoc>(tableName, field, conn) = - Custom.List<'TDoc>( - Query.Find.byField tableName field.Name field.Op, [ fieldParam field.Value ], fromData<'TDoc>, conn) + Custom.List<'TDoc>(Query.Find.byField tableName field, addFieldParam field [], fromData<'TDoc>, conn) /// Retrieve documents via a comparison on a JSON field, returning only the first result [] let firstByField<'TDoc> tableName field conn = - Custom.single - $"{Query.Find.byField tableName field.Name field.Op} LIMIT 1" - [ fieldParam field.Value ] - fromData<'TDoc> - conn + Custom.single $"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam field []) fromData<'TDoc> conn /// Retrieve documents via a comparison on a JSON field, returning only the first result let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) = Custom.Single( - $"{Query.Find.byField tableName field.Name field.Op} LIMIT 1", - [ fieldParam field.Value ], - fromData<'TDoc>, - conn) + $"{Query.Find.byField tableName field} LIMIT 1", addFieldParam field [], fromData<'TDoc>, conn) /// Commands to update documents [] @@ -340,10 +337,7 @@ module WithConn = /// Patch documents using a comparison on a JSON field [] let byField tableName field (patch: 'TPatch) (conn: SqliteConnection) = - Custom.nonQuery - (Query.Patch.byField tableName field.Name field.Op) - [ fieldParam field.Value; jsonParam "@data" patch ] - conn + Custom.nonQuery (Query.Patch.byField tableName field) (addFieldParam field [ jsonParam "@data" patch ]) conn /// Commands to remove fields from documents [] @@ -358,9 +352,7 @@ module WithConn = [] let byField tableName field fieldName conn = Custom.nonQuery - (Query.RemoveField.byField tableName field.Name field.Op) - [ fieldParam field.Value; fieldNameParam fieldName ] - conn + (Query.RemoveField.byField tableName field) (addFieldParam field [ fieldNameParam fieldName ]) conn /// Commands to delete documents [] @@ -374,7 +366,7 @@ module WithConn = /// Delete documents by matching a comparison on a JSON field [] let byField tableName field conn = - Custom.nonQuery (Query.Delete.byField tableName field.Name field.Op) [ fieldParam field.Value ] conn + Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam field []) conn /// Commands to execute custom SQL queries diff --git a/src/Tests.CSharp/CommonCSharpTests.cs b/src/Tests.CSharp/CommonCSharpTests.cs index 7f989d9..1426c3c 100644 --- a/src/Tests.CSharp/CommonCSharpTests.cs +++ b/src/Tests.CSharp/CommonCSharpTests.cs @@ -177,12 +177,12 @@ public static class CommonCSharpTests { TestCase("succeeds when a logical operator is passed", () => { - Expect.equal(Query.WhereByField("theField", Op.GT, "@test"), "data ->> 'theField' > @test", + Expect.equal(Query.WhereByField(Field.GT("theField", 0), "@test"), "data ->> 'theField' > @test", "WHERE clause not correct"); }), TestCase("succeeds when an existence operator is passed", () => { - Expect.equal(Query.WhereByField("thatField", Op.NEX, ""), "data ->> 'thatField' IS NULL", + Expect.equal(Query.WhereByField(Field.NEX("thatField"), ""), "data ->> 'thatField' IS NULL", "WHERE clause not correct"); }) }), @@ -242,7 +242,7 @@ public static class CommonCSharpTests }), TestCase("ByField succeeds", () => { - Expect.equal(Query.Count.ByField("tbl", "thatField", Op.EQ), + Expect.equal(Query.Count.ByField("tbl", Field.EQ("thatField", 0)), "SELECT COUNT(*) AS it FROM tbl WHERE data ->> 'thatField' = @field", "JSON field text comparison count query not correct"); }) @@ -257,7 +257,7 @@ public static class CommonCSharpTests }), TestCase("ByField succeeds", () => { - Expect.equal(Query.Exists.ByField("tbl", "Test", Op.LT), + Expect.equal(Query.Exists.ByField("tbl", Field.LT("Test", 0)), "SELECT EXISTS (SELECT 1 FROM tbl WHERE data ->> 'Test' < @field) AS it", "JSON field text comparison exists query not correct"); }) @@ -271,7 +271,7 @@ public static class CommonCSharpTests }), TestCase("ByField succeeds", () => { - Expect.equal(Query.Find.ByField("tbl", "Golf", Op.GE), + Expect.equal(Query.Find.ByField("tbl", Field.GE("Golf", 0)), "SELECT data FROM tbl WHERE data ->> 'Golf' >= @field", "SELECT by JSON comparison query not correct"); }) @@ -285,7 +285,7 @@ public static class CommonCSharpTests }), TestCase("ByField succeeds", () => { - Expect.equal(Query.Delete.ByField("tbl", "gone", Op.NEX), + Expect.equal(Query.Delete.ByField("tbl", Field.NEX("gone")), "DELETE FROM tbl WHERE data ->> 'gone' IS NULL", "DELETE by JSON comparison query not correct"); }) diff --git a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs index 4f0ee63..7dede84 100644 --- a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs +++ b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs @@ -246,7 +246,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var theCount = await conn.CountByField(PostgresDb.TableName, "Value", Op.EQ, "purple"); + var theCount = await conn.CountByField(PostgresDb.TableName, Field.EQ("Value", "purple")); Expect.equal(theCount, 2, "There should have been 2 matching documents"); }), TestCase("CountByContains succeeds", async () => @@ -296,7 +296,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var exists = await conn.ExistsByField(PostgresDb.TableName, "Sub", Op.EX, ""); + var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EX("Sub")); Expect.isTrue(exists, "There should have been existing documents"); }), TestCase("succeeds when documents do not exist", async () => @@ -305,7 +305,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var exists = await conn.ExistsByField(PostgresDb.TableName, "NumValue", Op.EQ, "six"); + var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EQ("NumValue", "six")); Expect.isFalse(exists, "There should not have been existing documents"); }) }), @@ -403,7 +403,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var docs = await conn.FindByField(PostgresDb.TableName, "Value", Op.EQ, "another"); + var docs = await conn.FindByField(PostgresDb.TableName, Field.EQ("Value", "another")); Expect.equal(docs.Count, 1, "There should have been one document returned"); }), TestCase("succeeds when documents are not found", async () => @@ -412,7 +412,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var docs = await conn.FindByField(PostgresDb.TableName, "Value", Op.EQ, "mauve"); + var docs = await conn.FindByField(PostgresDb.TableName, Field.EQ("Value", "mauve")); Expect.isEmpty(docs, "There should have been no documents returned"); }) }), @@ -467,7 +467,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var doc = await conn.FindFirstByField(PostgresDb.TableName, "Value", Op.EQ, "another"); + var doc = await conn.FindFirstByField(PostgresDb.TableName, Field.EQ("Value", "another")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc.Id, "two", "The incorrect document was returned"); }), @@ -477,7 +477,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var doc = await conn.FindFirstByField(PostgresDb.TableName, "Value", Op.EQ, "purple"); + var doc = await conn.FindFirstByField(PostgresDb.TableName, Field.EQ("Value", "purple")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.contains(new[] { "five", "four" }, doc.Id, "An incorrect document was returned"); }), @@ -487,7 +487,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var doc = await conn.FindFirstByField(PostgresDb.TableName, "Value", Op.EQ, "absent"); + var doc = await conn.FindFirstByField(PostgresDb.TableName, Field.EQ("Value", "absent")); Expect.isNull(doc, "There should not have been a document returned"); }) }), @@ -650,8 +650,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.PatchByField(PostgresDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 }); - var after = await conn.CountByField(PostgresDb.TableName, "NumValue", Op.EQ, "77"); + await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); + var after = await conn.CountByField(PostgresDb.TableName, Field.EQ("NumValue", "77")); Expect.equal(after, 2, "There should have been 2 documents returned"); }), TestCase("succeeds when no document is updated", async () => @@ -662,7 +662,7 @@ public class PostgresCSharpExtensionTests Expect.equal(before, 0, "There should have been no documents returned"); // This not raising an exception is the test - await conn.PatchByField(PostgresDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" }); + await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); }) }), TestList("PatchByContains", new[] @@ -742,7 +742,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.DeleteByField(PostgresDb.TableName, "Value", Op.NE, "purple"); + await conn.DeleteByField(PostgresDb.TableName, Field.NE("Value", "purple")); var remaining = await conn.CountAll(PostgresDb.TableName); Expect.equal(remaining, 2, "There should have been 2 documents remaining"); }), @@ -752,7 +752,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.DeleteByField(PostgresDb.TableName, "Value", Op.EQ, "crimson"); + await conn.DeleteByField(PostgresDb.TableName, Field.EQ("Value", "crimson")); var remaining = await conn.CountAll(PostgresDb.TableName); Expect.equal(remaining, 5, "There should have been 5 documents remaining"); }) diff --git a/src/Tests.CSharp/PostgresCSharpTests.cs b/src/Tests.CSharp/PostgresCSharpTests.cs index a4713c7..84a0080 100644 --- a/src/Tests.CSharp/PostgresCSharpTests.cs +++ b/src/Tests.CSharp/PostgresCSharpTests.cs @@ -32,11 +32,21 @@ public class PostgresCSharpTests Expect.equal(it.Item1, "@test", "JSON parameter not constructed correctly"); Expect.equal(it.Item2, Sql.jsonb("{\"Something\":\"good\"}"), "JSON parameter value incorrect"); }), - TestCase("Field succeeds", () => + TestList("AddField", new [] { - var it = Parameters.Field(242); - Expect.equal(it.Item1, "@field", "Field parameter not constructed correctly"); - Expect.isTrue(it.Item2.IsParameter, "Field parameter value incorrect"); + TestCase("succeeds when a parameter is added", () => + { + var it = Parameters.AddField(Field.EQ("it", "242"), Enumerable.Empty>()) + .ToList(); + Expect.hasLength(it, 1, "There should have been a parameter added"); + Expect.equal(it[0].Item1, "@field", "Field parameter not constructed correctly"); + Expect.isTrue(it[0].Item2.IsParameter, "Field parameter value incorrect"); + }), + TestCase("succeeds when a parameter is not added", () => + { + var it = Parameters.AddField(Field.EX("It"), Enumerable.Empty>()); + Expect.isEmpty(it, "There should not have been any parameters added"); + }) }), TestCase("None succeeds", () => { @@ -134,7 +144,7 @@ public class PostgresCSharpTests }), TestCase("ByField succeeds", () => { - Expect.equal(Postgres.Query.Patch.ByField(PostgresDb.TableName, "Snail", Op.LT), + Expect.equal(Postgres.Query.Patch.ByField(PostgresDb.TableName, Field.LT("Snail", 0)), $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Snail' < @field", "UPDATE partial by ID statement not correct"); }), @@ -431,7 +441,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var theCount = await Count.ByField(PostgresDb.TableName, "Value", Op.EQ, "purple"); + var theCount = await Count.ByField(PostgresDb.TableName, Field.EQ("Value", "purple")); Expect.equal(theCount, 2, "There should have been 2 matching documents"); }), TestCase("ByContains succeeds", async () => @@ -479,7 +489,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var exists = await Exists.ByField(PostgresDb.TableName, "Sub", Op.NEX, ""); + var exists = await Exists.ByField(PostgresDb.TableName, Field.NEX("Sub")); Expect.isTrue(exists, "There should have been existing documents"); }), TestCase("succeeds when documents do not exist", async () => @@ -487,7 +497,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var exists = await Exists.ByField(PostgresDb.TableName, "NumValue", Op.EQ, "six"); + var exists = await Exists.ByField(PostgresDb.TableName, Field.EQ("NumValue", "six")); Expect.isFalse(exists, "There should not have been existing documents"); }) }), @@ -578,7 +588,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var docs = await Find.ByField(PostgresDb.TableName, "Value", Op.EQ, "another"); + var docs = await Find.ByField(PostgresDb.TableName, Field.EQ("Value", "another")); Expect.equal(docs.Count, 1, "There should have been one document returned"); }), TestCase("succeeds when documents are not found", async () => @@ -586,7 +596,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var docs = await Find.ByField(PostgresDb.TableName, "Value", Op.EQ, "mauve"); + var docs = await Find.ByField(PostgresDb.TableName, Field.EQ("Value", "mauve")); Expect.isEmpty(docs, "There should have been no documents returned"); }) }), @@ -636,7 +646,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var doc = await Find.FirstByField(PostgresDb.TableName, "Value", Op.EQ, "another"); + var doc = await Find.FirstByField(PostgresDb.TableName, Field.EQ("Value", "another")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc.Id, "two", "The incorrect document was returned"); }), @@ -645,7 +655,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var doc = await Find.FirstByField(PostgresDb.TableName, "Value", Op.EQ, "purple"); + var doc = await Find.FirstByField(PostgresDb.TableName, Field.EQ("Value", "purple")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.contains(new[] { "five", "four" }, doc.Id, "An incorrect document was returned"); }), @@ -654,7 +664,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - var doc = await Find.FirstByField(PostgresDb.TableName, "Value", Op.EQ, "absent"); + var doc = await Find.FirstByField(PostgresDb.TableName, Field.EQ("Value", "absent")); Expect.isNull(doc, "There should not have been a document returned"); }) }), @@ -813,8 +823,8 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await Patch.ByField(PostgresDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 }); - var after = await Count.ByField(PostgresDb.TableName, "NumValue", Op.EQ, "77"); + await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); + var after = await Count.ByField(PostgresDb.TableName, Field.EQ("NumValue", "77")); Expect.equal(after, 2, "There should have been 2 documents returned"); }), TestCase("succeeds when no document is updated", async () => @@ -825,7 +835,7 @@ public class PostgresCSharpTests Expect.equal(before, 0, "There should have been no documents returned"); // This not raising an exception is the test - await Patch.ByField(PostgresDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" }); + await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); }) }), TestList("ByContains", new[] @@ -903,7 +913,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await Delete.ByField(PostgresDb.TableName, "Value", Op.EQ, "purple"); + await Delete.ByField(PostgresDb.TableName, Field.EQ("Value", "purple")); var remaining = await Count.All(PostgresDb.TableName); Expect.equal(remaining, 3, "There should have been 3 documents remaining"); }), @@ -912,7 +922,7 @@ public class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await Delete.ByField(PostgresDb.TableName, "Value", Op.EQ, "crimson"); + await Delete.ByField(PostgresDb.TableName, Field.EQ("Value", "crimson")); var remaining = await Count.All(PostgresDb.TableName); Expect.equal(remaining, 5, "There should have been 5 documents remaining"); }) diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs index c86cc07..3c6559b 100644 --- a/src/Tests.CSharp/SqliteCSharpTests.cs +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -36,7 +36,7 @@ public static class SqliteCSharpTests }), TestCase("ByField succeeds", () => { - Expect.equal(Sqlite.Query.Patch.ByField("tbl", "Part", Op.NE), + Expect.equal(Sqlite.Query.Patch.ByField("tbl", Field.NE("Part", 0)), "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Part' <> @field", "UPDATE partial by JSON comparison query not correct"); }) @@ -51,7 +51,7 @@ public static class SqliteCSharpTests }), TestCase("ByField succeeds", () => { - Expect.equal(Sqlite.Query.RemoveField.ByField("tbl", "Fly", Op.LT), + Expect.equal(Sqlite.Query.RemoveField.ByField("tbl", Field.LT("Fly", 0)), "UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Fly' < @field", "Remove field by field query not correct"); }) @@ -71,12 +71,19 @@ public static class SqliteCSharpTests Expect.equal(theParam.ParameterName, "@test", "The parameter name is incorrect"); Expect.equal(theParam.Value, "{\"Nice\":\"job\"}", "The parameter value is incorrect"); }), - TestCase("Field succeeds", () => + TestCase("AddField succeeds when adding a parameter", () => { - var theParam = Parameters.Field(99); + var paramList = Parameters.AddField(Field.EQ("it", 99), Enumerable.Empty()).ToList(); + Expect.hasLength(paramList, 1, "There should have been a parameter added"); + var theParam = paramList[0]; Expect.equal(theParam.ParameterName, "@field", "The parameter name is incorrect"); Expect.equal(theParam.Value, 99, "The parameter value is incorrect"); }), + TestCase("AddField succeeds when not adding a parameter", () => + { + var paramSeq = Parameters.AddField(Field.EX("Coffee"), Enumerable.Empty()); + Expect.isEmpty(paramSeq, "There should not have been any parameters added"); + }), TestCase("None succeeds", () => { Expect.isEmpty(Parameters.None, "The parameter list should have been empty"); diff --git a/src/Tests/CommonTests.fs b/src/Tests/CommonTests.fs index b7fcc4a..dd0ac97 100644 --- a/src/Tests/CommonTests.fs +++ b/src/Tests/CommonTests.fs @@ -45,13 +45,13 @@ let all = testList "whereByField" [ test "succeeds when a logical operator is passed" { Expect.equal - (Query.whereByField "theField" GT "@test") + (Query.whereByField (Field.GT "theField" 0) "@test") "data ->> 'theField' > @test" "WHERE clause not correct" } test "succeeds when an existence operator is passed" { Expect.equal - (Query.whereByField "thatField" NEX "") + (Query.whereByField (Field.NEX "thatField") "") "data ->> 'thatField' IS NULL" "WHERE clause not correct" } @@ -107,7 +107,7 @@ let all = } test "byField succeeds" { Expect.equal - (Query.Count.byField tbl "thatField" EQ) + (Query.Count.byField tbl (Field.EQ "thatField" 0)) $"SELECT COUNT(*) AS it FROM {tbl} WHERE data ->> 'thatField' = @field" "JSON field text comparison count query not correct" } @@ -121,7 +121,7 @@ let all = } test "byField succeeds" { Expect.equal - (Query.Exists.byField tbl "Test" LT) + (Query.Exists.byField tbl (Field.LT "Test" 0)) $"SELECT EXISTS (SELECT 1 FROM {tbl} WHERE data ->> 'Test' < @field) AS it" "JSON field text comparison exists query not correct" } @@ -135,7 +135,7 @@ let all = } test "byField succeeds" { Expect.equal - (Query.Find.byField tbl "Golf" GE) + (Query.Find.byField tbl (Field.GE "Golf" 0)) $"SELECT data FROM {tbl} WHERE data ->> 'Golf' >= @field" "SELECT by JSON comparison query not correct" } @@ -149,7 +149,7 @@ let all = } test "byField succeeds" { Expect.equal - (Query.Delete.byField tbl "gone" NEX) + (Query.Delete.byField tbl (Field.NEX "gone")) $"DELETE FROM {tbl} WHERE data ->> 'gone' IS NULL" "DELETE by JSON comparison query not correct" } diff --git a/src/Tests/PostgresExtensionTests.fs b/src/Tests/PostgresExtensionTests.fs index 4d215b2..cfc0135 100644 --- a/src/Tests/PostgresExtensionTests.fs +++ b/src/Tests/PostgresExtensionTests.fs @@ -214,7 +214,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! theCount = conn.countByField PostgresDb.TableName "Value" EQ "purple" + let! theCount = conn.countByField PostgresDb.TableName (Field.EQ "Value" "purple") Expect.equal theCount 2 "There should have been 2 matching documents" } testTask "countByContains succeeds" { @@ -257,7 +257,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! exists = conn.existsByField PostgresDb.TableName "Sub" EX "" + let! exists = conn.existsByField PostgresDb.TableName (Field.EX "Sub") Expect.isTrue exists "There should have been existing documents" } testTask "succeeds when documents do not exist" { @@ -265,7 +265,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! exists = conn.existsByField PostgresDb.TableName "NumValue" EQ "six" + let! exists = conn.existsByField PostgresDb.TableName (Field.EQ "NumValue" "six") Expect.isFalse exists "There should not have been existing documents" } ] @@ -354,7 +354,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! docs = conn.findByField PostgresDb.TableName "Value" EQ "another" + let! docs = conn.findByField PostgresDb.TableName (Field.EQ "Value" "another") Expect.equal (List.length docs) 1 "There should have been one document returned" } testTask "succeeds when documents are not found" { @@ -362,7 +362,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! docs = conn.findByField PostgresDb.TableName "Value" EQ "mauve" + let! docs = conn.findByField PostgresDb.TableName (Field.EQ "Value" "mauve") Expect.isEmpty docs "There should have been no documents returned" } ] @@ -408,7 +408,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! doc = conn.findFirstByField PostgresDb.TableName "Value" EQ "another" + let! doc = conn.findFirstByField PostgresDb.TableName (Field.EQ "Value" "another") Expect.isSome doc "There should have been a document returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned" } @@ -417,7 +417,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! doc = conn.findFirstByField PostgresDb.TableName "Value" EQ "purple" + let! doc = conn.findFirstByField PostgresDb.TableName (Field.EQ "Value" "purple") Expect.isSome doc "There should have been a document returned" Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned" } @@ -426,7 +426,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! doc = conn.findFirstByField PostgresDb.TableName "Value" EQ "absent" + let! doc = conn.findFirstByField PostgresDb.TableName (Field.EQ "Value" "absent") Expect.isNone doc "There should not have been a document returned" } ] @@ -562,8 +562,8 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - do! conn.patchByField PostgresDb.TableName "Value" EQ "purple" {| NumValue = 77 |} - let! after = conn.countByField PostgresDb.TableName "NumValue" EQ "77" + do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} + let! after = conn.countByField PostgresDb.TableName (Field.EQ "NumValue" "77") Expect.equal after 2 "There should have been 2 documents returned" } testTask "succeeds when no document is updated" { @@ -573,7 +573,7 @@ let integrationTests = Expect.equal before 0 "There should have been no documents returned" // This not raising an exception is the test - do! conn.patchByField PostgresDb.TableName "Value" EQ "burgundy" {| Foo = "green" |} + do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |} } ] testList "patchByContains" [ @@ -642,7 +642,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - do! conn.deleteByField PostgresDb.TableName "Value" EQ "purple" + do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "purple") let! remaining = conn.countAll PostgresDb.TableName Expect.equal remaining 3 "There should have been 3 documents remaining" } @@ -651,7 +651,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - do! conn.deleteByField PostgresDb.TableName "Value" EQ "crimson" + do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "crimson") let! remaining = conn.countAll PostgresDb.TableName Expect.equal remaining 5 "There should have been 5 documents remaining" } diff --git a/src/Tests/PostgresTests.fs b/src/Tests/PostgresTests.fs index 66938f6..b0ef091 100644 --- a/src/Tests/PostgresTests.fs +++ b/src/Tests/PostgresTests.fs @@ -18,15 +18,23 @@ let unitTests = ("@test", Sql.jsonb """{"Something":"good"}""") "JSON parameter not constructed correctly" } - test "fieldParam succeeds" { - let it = fieldParam 242 - Expect.equal (fst it) "@field" "Field parameter name not correct" - match snd it with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@field" "Parameter name not correct" - Expect.equal value.Value 242 "Parameter value not correct" - | _ -> Expect.isTrue false "The parameter was not a Parameter type" - } + testList "addFieldParam" [ + test "succeeds when a parameter is added" { + let paramList = addFieldParam (Field.EQ "it" "242") [] + Expect.hasLength paramList 1 "There should have been a parameter added" + let it = paramList[0] + Expect.equal (fst it) "@field" "Field parameter name not correct" + match snd it with + | SqlValue.Parameter value -> + Expect.equal value.ParameterName "@field" "Parameter name not correct" + Expect.equal value.Value "242" "Parameter value not correct" + | _ -> Expect.isTrue false "The parameter was not a Parameter type" + } + test "succeeds when a parameter is not added" { + let paramList = addFieldParam (Field.EX "tacos") [] + Expect.isEmpty paramList "There should not have been any parameters added" + } + ] test "noParams succeeds" { Expect.isEmpty noParams "The no-params sequence should be empty" } @@ -110,7 +118,7 @@ let unitTests = } test "byField succeeds" { Expect.equal - (Query.Patch.byField PostgresDb.TableName "Snail" LT) + (Query.Patch.byField PostgresDb.TableName (Field.LT "Snail" 0)) $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Snail' < @field" "UPDATE partial by ID statement not correct" } @@ -357,7 +365,7 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - let! theCount = Count.byField PostgresDb.TableName "Value" EQ "purple" + let! theCount = Count.byField PostgresDb.TableName (Field.EQ "Value" "purple") Expect.equal theCount 2 "There should have been 2 matching documents" } testTask "byContains succeeds" { @@ -397,14 +405,14 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - let! exists = Exists.byField PostgresDb.TableName "Sub" EX "" + let! exists = Exists.byField PostgresDb.TableName (Field.EX "Sub") Expect.isTrue exists "There should have been existing documents" } testTask "succeeds when documents do not exist" { use db = PostgresDb.BuildDb() do! loadDocs () - let! exists = Exists.byField PostgresDb.TableName "NumValue" EQ "six" + let! exists = Exists.byField PostgresDb.TableName (Field.EQ "NumValue" "six") Expect.isFalse exists "There should not have been existing documents" } ] @@ -486,14 +494,14 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - let! docs = Find.byField PostgresDb.TableName "Value" EQ "another" + let! docs = Find.byField PostgresDb.TableName (Field.EQ "Value" "another") Expect.equal (List.length docs) 1 "There should have been one document returned" } testTask "succeeds when documents are not found" { use db = PostgresDb.BuildDb() do! loadDocs () - let! docs = Find.byField PostgresDb.TableName "Value" EQ "mauve" + let! docs = Find.byField PostgresDb.TableName (Field.EQ "Value" "mauve") Expect.isEmpty docs "There should have been no documents returned" } ] @@ -534,7 +542,7 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - let! doc = Find.firstByField PostgresDb.TableName "Value" EQ "another" + let! doc = Find.firstByField PostgresDb.TableName (Field.EQ "Value" "another") Expect.isSome doc "There should have been a document returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned" } @@ -542,7 +550,7 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - let! doc = Find.firstByField PostgresDb.TableName "Value" EQ "purple" + let! doc = Find.firstByField PostgresDb.TableName (Field.EQ "Value" "purple") Expect.isSome doc "There should have been a document returned" Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned" } @@ -550,7 +558,7 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - let! doc = Find.firstByField PostgresDb.TableName "Value" EQ "absent" + let! doc = Find.firstByField PostgresDb.TableName (Field.EQ "Value" "absent") Expect.isNone doc "There should not have been a document returned" } ] @@ -682,8 +690,8 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - do! Patch.byField PostgresDb.TableName "Value" EQ "purple" {| NumValue = 77 |} - let! after = Count.byField PostgresDb.TableName "NumValue" EQ "77" + do! Patch.byField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} + let! after = Count.byField PostgresDb.TableName (Field.EQ "NumValue" "77") Expect.equal after 2 "There should have been 2 documents returned" } testTask "succeeds when no document is updated" { @@ -693,7 +701,7 @@ let integrationTests = Expect.equal before 0 "There should have been no documents returned" // This not raising an exception is the test - do! Patch.byField PostgresDb.TableName "Value" EQ "burgundy" {| Foo = "green" |} + do! Patch.byField PostgresDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |} } ] testList "byContains" [ @@ -759,7 +767,7 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - do! Delete.byField PostgresDb.TableName "Value" EQ "purple" + do! Delete.byField PostgresDb.TableName (Field.EQ "Value" "purple") let! remaining = Count.all PostgresDb.TableName Expect.equal remaining 3 "There should have been 3 documents remaining" } @@ -767,7 +775,7 @@ let integrationTests = use db = PostgresDb.BuildDb() do! loadDocs () - do! Delete.byField PostgresDb.TableName "Value" EQ "crimson" + do! Delete.byField PostgresDb.TableName (Field.EQ "Value" "crimson") let! remaining = Count.all PostgresDb.TableName Expect.equal remaining 5 "There should have been 5 documents remaining" } diff --git a/src/Tests/SqliteTests.fs b/src/Tests/SqliteTests.fs index 3f09467..a16b37f 100644 --- a/src/Tests/SqliteTests.fs +++ b/src/Tests/SqliteTests.fs @@ -27,7 +27,7 @@ let unitTests = } test "byField succeeds" { Expect.equal - (Query.Patch.byField "tbl" "Part" NE) + (Query.Patch.byField "tbl" (Field.NE "Part" 0)) "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Part' <> @field" "UPDATE partial by JSON comparison query not correct" } @@ -41,7 +41,7 @@ let unitTests = } test "byField succeeds" { Expect.equal - (Query.RemoveField.byField "tbl" "Fly" GT) + (Query.RemoveField.byField "tbl" (Field.GT "Fly" 0)) "UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Fly' > @field" "Remove field by field query not correct" } @@ -58,11 +58,19 @@ let unitTests = Expect.equal theParam.ParameterName "@test" "The parameter name is incorrect" Expect.equal theParam.Value """{"Nice":"job"}""" "The parameter value is incorrect" } - test "fieldParam succeeds" { - let theParam = fieldParam 99 - Expect.equal theParam.ParameterName "@field" "The parameter name is incorrect" - Expect.equal theParam.Value 99 "The parameter value is incorrect" - } + testList "addFieldParam" [ + test "succeeds when adding a parameter" { + let paramList = addFieldParam (Field.EQ "it" 99) [] + Expect.hasLength paramList 1 "There should have been a parameter added" + let theParam = paramList[0] + Expect.equal theParam.ParameterName "@field" "The parameter name is incorrect" + Expect.equal theParam.Value 99 "The parameter value is incorrect" + } + test "succeeds when not adding a parameter" { + let paramList = addFieldParam (Field.NEX "Coffee") [] + Expect.isEmpty paramList "There should not have been any parameters added" + } + ] test "noParams succeeds" { Expect.isEmpty noParams "The parameter list should have been empty" } -- 2.45.1 From 2daa347e77f565fb7ea0acd602f9556a23cd82cc Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 23 Jan 2024 19:18:02 -0500 Subject: [PATCH 5/6] RemoveField -> RemoveFields Implemented for all --- src/Postgres/Extensions.fs | 36 ++++ src/Postgres/Library.fs | 175 ++++++++++++++-- src/Sqlite/Extensions.fs | 22 +- src/Sqlite/Library.fs | 141 ++++++++----- .../PostgresCSharpExtensionTests.cs | 177 ++++++++++++++++ src/Tests.CSharp/PostgresCSharpTests.cs | 198 +++++++++++++++++- .../SqliteCSharpExtensionTests.cs | 19 +- src/Tests.CSharp/SqliteCSharpTests.cs | 37 ++-- src/Tests/PostgresExtensionTests.fs | 156 ++++++++++++++ src/Tests/PostgresTests.fs | 172 ++++++++++++++- src/Tests/SqliteExtensionTests.fs | 16 +- src/Tests/SqliteTests.fs | 31 +-- 12 files changed, 1049 insertions(+), 131 deletions(-) diff --git a/src/Postgres/Extensions.fs b/src/Postgres/Extensions.fs index 9b89b84..c2ae2e5 100644 --- a/src/Postgres/Extensions.fs +++ b/src/Postgres/Extensions.fs @@ -133,6 +133,22 @@ module Extensions = member conn.patchByJsonPath tableName jsonPath (patch: 'TPatch) = WithProps.Patch.byJsonPath tableName jsonPath patch (Sql.existingConnection conn) + /// Remove fields from a document by the document's ID + member conn.removeFieldsById tableName (docId: 'TKey) fieldNames = + WithProps.RemoveFields.byId tableName docId fieldNames (Sql.existingConnection conn) + + /// Remove fields from documents via a comparison on a JSON field in the document + member conn.removeFieldsByField tableName field fieldNames = + WithProps.RemoveFields.byField tableName field fieldNames (Sql.existingConnection conn) + + /// Remove fields from documents via a JSON containment query (@>) + member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames = + WithProps.RemoveFields.byContains tableName criteria fieldNames (Sql.existingConnection conn) + + /// Remove fields from documents via a JSON Path match query (@?) + member conn.removeFieldsByJsonPath tableName jsonPath fieldNames = + WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (Sql.existingConnection conn) + /// Delete a document by its ID member conn.deleteById tableName (docId: 'TKey) = WithProps.Delete.byId tableName docId (Sql.existingConnection conn) @@ -312,6 +328,26 @@ type NpgsqlConnectionCSharpExtensions = static member inline PatchByJsonPath(conn, tableName, jsonPath, patch: 'TPatch) = WithProps.Patch.byJsonPath tableName jsonPath patch (Sql.existingConnection conn) + /// Remove fields from a document by the document's ID + [] + static member inline RemoveFieldsById(conn, tableName, docId: 'TKey, fieldNames) = + WithProps.RemoveFields.ById(tableName, docId, fieldNames, Sql.existingConnection conn) + + /// Remove fields from documents via a comparison on a JSON field in the document + [] + static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) = + WithProps.RemoveFields.ByField(tableName, field, fieldNames, Sql.existingConnection conn) + + /// Remove fields from documents via a JSON containment query (@>) + [] + static member inline RemoveFieldsByContains(conn, tableName, criteria: 'TContains, fieldNames) = + WithProps.RemoveFields.ByContains(tableName, criteria, fieldNames, Sql.existingConnection conn) + + /// Remove fields from documents via a JSON Path match query (@?) + [] + static member inline RemoveFieldsByJsonPath(conn, tableName, jsonPath, fieldNames) = + WithProps.RemoveFields.ByJsonPath(tableName, jsonPath, fieldNames, Sql.existingConnection conn) + /// Delete a document by its ID [] static member inline DeleteById(conn, tableName, docId: 'TKey) = diff --git a/src/Postgres/Library.fs b/src/Postgres/Library.fs index bb496ee..1351af8 100644 --- a/src/Postgres/Library.fs +++ b/src/Postgres/Library.fs @@ -65,18 +65,28 @@ module Parameters = /// Create a JSON field parameter (name "@field") [] - let addFieldParam field parameters = + let addFieldParam name field parameters = match field.Op with | EX | NEX -> parameters - | _ -> ("@field", Sql.parameter (NpgsqlParameter("@field", field.Value))) :: parameters + | _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) :: parameters /// Create a JSON field parameter (name "@field") - let AddField field parameters = + let AddField name field parameters = match field.Op with | EX | NEX -> parameters - | _ -> - ("@field", Sql.parameter (NpgsqlParameter("@field", field.Value))) |> Seq.singleton |> Seq.append parameters + | _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) |> Seq.singleton |> Seq.append parameters + /// Append JSON field name parameters for the given field names to the given parameters + [] + let fieldNameParam (fieldNames: string list) = + if fieldNames.Length = 1 then "@name", Sql.string fieldNames[0] + else "@name", Sql.stringArray (Array.ofList fieldNames) + + /// Append JSON field name parameters for the given field names to the given parameters + let FieldName(fieldNames: string seq) = + if Seq.isEmpty fieldNames then "@name", Sql.string (Seq.head fieldNames) + else "@name", Sql.stringArray (Array.ofSeq fieldNames) + /// An empty parameter sequence [] let noParams = @@ -154,25 +164,56 @@ module Query = /// Queries to patch (partially update) documents module Patch = + /// Create an UPDATE statement to patch documents + let private update tableName whereClause = + $"UPDATE %s{tableName} SET data = data || @data WHERE {whereClause}" + /// Query to patch a document by its ID [] let byId tableName = - $"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereById "@id"}""" + Query.whereById "@id" |> update tableName /// Query to patch documents match a JSON field comparison (->> =) [] let byField tableName field = - $"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereByField field "@field"}""" + Query.whereByField field "@field" |> update tableName /// Query to patch documents matching a JSON containment query (@>) [] let byContains tableName = - $"""UPDATE %s{tableName} SET data = data || @data WHERE {whereDataContains "@criteria"}""" + whereDataContains "@criteria" |> update tableName /// Query to patch documents matching a JSON containment query (@>) [] let byJsonPath tableName = - $"""UPDATE %s{tableName} SET data = data || @data WHERE {whereJsonPathMatches "@path"}""" + whereJsonPathMatches "@path" |> update tableName + + /// Queries to remove fields from documents + module RemoveFields = + + /// Create an UPDATE statement to remove parameters + let private update tableName whereClause = + $"UPDATE %s{tableName} SET data = data - @name WHERE {whereClause}" + + /// Query to remove fields from a document by the document's ID + [] + let byId tableName = + Query.whereById "@id" |> update tableName + + /// Query to remove fields from documents via a comparison on a JSON field within the document + [] + let byField tableName field = + Query.whereByField field "@field" |> update tableName + + /// Query to patch documents matching a JSON containment query (@>) + [] + let byContains tableName = + whereDataContains "@criteria" |> update tableName + + /// Query to patch documents matching a JSON containment query (@>) + [] + let byJsonPath tableName = + whereJsonPathMatches "@path" |> update tableName /// Queries to delete documents module Delete = @@ -312,7 +353,7 @@ module WithProps = /// Count matching documents using a JSON field comparison (->> =) [] let byField tableName field sqlProps = - Custom.scalar (Query.Count.byField tableName field) (addFieldParam field []) toCount sqlProps + Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount sqlProps /// Count matching documents using a JSON containment query (@>) [] @@ -336,7 +377,7 @@ module WithProps = /// Determine if a document exists using a JSON field comparison (->> =) [] let byField tableName field sqlProps = - Custom.scalar (Query.Exists.byField tableName field) (addFieldParam field []) toExists sqlProps + Custom.scalar (Query.Exists.byField tableName field) (addFieldParam "@field" field []) toExists sqlProps /// Determine if a document exists using a JSON containment query (@>) [] @@ -373,11 +414,13 @@ module WithProps = /// Retrieve documents matching a JSON field comparison (->> =) [] let byField<'TDoc> tableName field sqlProps = - Custom.list<'TDoc> (Query.Find.byField tableName field) (addFieldParam field []) fromData<'TDoc> sqlProps + Custom.list<'TDoc> + (Query.Find.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> sqlProps /// Retrieve documents matching a JSON field comparison (->> =) let ByField<'TDoc>(tableName, field, sqlProps) = - Custom.List<'TDoc>(Query.Find.byField tableName field, addFieldParam field [], fromData<'TDoc>, sqlProps) + Custom.List<'TDoc>( + Query.Find.byField tableName field, addFieldParam "@field" field [], fromData<'TDoc>, sqlProps) /// Retrieve documents matching a JSON containment query (@>) [] @@ -405,12 +448,18 @@ module WithProps = [] let firstByField<'TDoc> tableName field sqlProps = Custom.single<'TDoc> - $"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam field []) fromData<'TDoc> sqlProps + $"{Query.Find.byField tableName field} LIMIT 1" + (addFieldParam "@field" field []) + fromData<'TDoc> + sqlProps /// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found let FirstByField<'TDoc when 'TDoc: null>(tableName, field, sqlProps) = Custom.Single<'TDoc>( - $"{Query.Find.byField tableName field} LIMIT 1", addFieldParam field [], fromData<'TDoc>, sqlProps) + $"{Query.Find.byField tableName field} LIMIT 1", + addFieldParam "@field" field [], + fromData<'TDoc>, + sqlProps) /// Retrieve the first document matching a JSON containment query (@>); returns None if not found [] @@ -471,7 +520,9 @@ module WithProps = [] let byField tableName field (patch: 'TPatch) sqlProps = Custom.nonQuery - (Query.Patch.byField tableName field) (addFieldParam field [ jsonParam "@data" patch ]) sqlProps + (Query.Patch.byField tableName field) + (addFieldParam "@field" field [ jsonParam "@data" patch ]) + sqlProps /// Patch documents using a JSON containment query in the WHERE clause (@>) [] @@ -485,6 +536,55 @@ module WithProps = Custom.nonQuery (Query.Patch.byJsonPath tableName) [ jsonParam "@data" patch; "@path", Sql.string jsonPath ] sqlProps + /// Commands to remove fields from documents + [] + module RemoveFields = + + /// Remove fields from a document by the document's ID + [] + let byId tableName (docId: 'TKey) fieldNames sqlProps = + Custom.nonQuery (Query.RemoveFields.byId tableName) [ idParam docId; fieldNameParam fieldNames ] sqlProps + + /// Remove fields from a document by the document's ID + let ById(tableName, docId: 'TKey, fieldNames, sqlProps) = + byId tableName docId (List.ofSeq fieldNames) sqlProps + + /// Remove fields from documents via a comparison on a JSON field in the document + [] + let byField tableName field fieldNames sqlProps = + Custom.nonQuery + (Query.RemoveFields.byField tableName field) + (addFieldParam "@field" field [ fieldNameParam fieldNames ]) + sqlProps + + /// Remove fields from documents via a comparison on a JSON field in the document + let ByField(tableName, field, fieldNames, sqlProps) = + byField tableName field (List.ofSeq fieldNames) sqlProps + + /// Remove fields from documents via a JSON containment query (@>) + [] + let byContains tableName (criteria: 'TContains) fieldNames sqlProps = + Custom.nonQuery + (Query.RemoveFields.byContains tableName) + [ jsonParam "@criteria" criteria; fieldNameParam fieldNames ] + sqlProps + + /// Remove fields from documents via a JSON containment query (@>) + let ByContains(tableName, criteria: 'TContains, fieldNames, sqlProps) = + byContains tableName criteria (List.ofSeq fieldNames) sqlProps + + /// Remove fields from documents via a JSON Path match query (@?) + [] + let byJsonPath tableName jsonPath fieldNames sqlProps = + Custom.nonQuery + (Query.RemoveFields.byJsonPath tableName) + [ "@path", Sql.string jsonPath; fieldNameParam fieldNames ] + sqlProps + + /// Remove fields from documents via a JSON Path match query (@?) + let ByJsonPath(tableName, jsonPath, fieldNames, sqlProps) = + byJsonPath tableName jsonPath (List.ofSeq fieldNames) sqlProps + /// Commands to delete documents [] module Delete = @@ -497,7 +597,7 @@ module WithProps = /// Delete documents by matching a JSON field comparison query (->> =) [] let byField tableName field sqlProps = - Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam field []) sqlProps + Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam "@field" field []) sqlProps /// Delete documents by matching a JSON contains query (@>) [] @@ -753,6 +853,47 @@ module Patch = WithProps.Patch.byJsonPath tableName jsonPath patch (fromDataSource ()) +/// Commands to remove fields from documents +[] +module RemoveFields = + + /// Remove fields from a document by the document's ID + [] + let byId tableName (docId: 'TKey) fieldNames = + WithProps.RemoveFields.byId tableName docId fieldNames (fromDataSource ()) + + /// Remove fields from a document by the document's ID + let ById(tableName, docId: 'TKey, fieldNames) = + WithProps.RemoveFields.ById(tableName, docId, fieldNames, fromDataSource ()) + + /// Remove fields from documents via a comparison on a JSON field in the document + [] + let byField tableName field fieldNames = + WithProps.RemoveFields.byField tableName field fieldNames (fromDataSource ()) + + /// Remove fields from documents via a comparison on a JSON field in the document + let ByField(tableName, field, fieldNames) = + WithProps.RemoveFields.ByField(tableName, field, fieldNames, fromDataSource ()) + + /// Remove fields from documents via a JSON containment query (@>) + [] + let byContains tableName (criteria: 'TContains) fieldNames = + WithProps.RemoveFields.byContains tableName criteria fieldNames (fromDataSource ()) + + /// Remove fields from documents via a JSON containment query (@>) + let ByContains(tableName, criteria: 'TContains, fieldNames) = + WithProps.RemoveFields.ByContains(tableName, criteria, fieldNames, fromDataSource ()) + + /// Remove fields from documents via a JSON Path match query (@?) + [] + let byJsonPath tableName jsonPath fieldNames = + WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (fromDataSource ()) + + /// Remove fields from documents via a JSON Path match query (@?) + let ByJsonPath(tableName, jsonPath, fieldNames) = + WithProps.RemoveFields.ByJsonPath(tableName, jsonPath, fieldNames, fromDataSource ()) + + /// Commands to delete documents [] module Delete = diff --git a/src/Sqlite/Extensions.fs b/src/Sqlite/Extensions.fs index 3416614..7ad8888 100644 --- a/src/Sqlite/Extensions.fs +++ b/src/Sqlite/Extensions.fs @@ -88,13 +88,13 @@ module Extensions = member conn.patchByField tableName field (patch: 'TPatch) = WithConn.Patch.byField tableName field patch conn - /// Remove a field from a document by the document's ID - member conn.removeFieldById tableName (docId: 'TKey) fieldName = - WithConn.RemoveField.byId tableName docId fieldName conn + /// Remove fields from a document by the document's ID + member conn.removeFieldsById tableName (docId: 'TKey) fieldNames = + WithConn.RemoveFields.byId tableName docId fieldNames conn /// Remove a field from a document via a comparison on a JSON field in the document - member conn.removeFieldByField tableName field fieldName = - WithConn.RemoveField.byField tableName field fieldName conn + member conn.removeFieldsByField tableName field fieldNames = + WithConn.RemoveFields.byField tableName field fieldNames conn /// Delete a document by its ID member conn.deleteById tableName (docId: 'TKey) = @@ -212,15 +212,15 @@ type SqliteConnectionCSharpExtensions = static member inline PatchByField<'TPatch>(conn, tableName, field, patch: 'TPatch) = WithConn.Patch.byField tableName field patch conn - /// Remove a field from a document by the document's ID + /// Remove fields from a document by the document's ID [] - static member inline RemoveFieldById<'TKey>(conn, tableName, docId: 'TKey, fieldName) = - WithConn.RemoveField.byId tableName docId fieldName conn + static member inline RemoveFieldsById<'TKey>(conn, tableName, docId: 'TKey, fieldNames) = + WithConn.RemoveFields.ById(tableName, docId, fieldNames, conn) - /// Remove a field from a document via a comparison on a JSON field in the document + /// Remove fields from documents via a comparison on a JSON field in the document [] - static member inline RemoveFieldByField(conn, tableName, field, fieldName) = - WithConn.RemoveField.byField tableName field fieldName conn + static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) = + WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn) /// Delete a document by its ID [] diff --git a/src/Sqlite/Library.fs b/src/Sqlite/Library.fs index 12a72e8..8e7cdb7 100644 --- a/src/Sqlite/Library.fs +++ b/src/Sqlite/Library.fs @@ -42,30 +42,45 @@ module Query = /// Document patching (partial update) queries module Patch = + /// Create an UPDATE statement to patch documents + let internal update tableName whereClause = + $"UPDATE %s{tableName} SET data = json_patch(data, json(@data)) WHERE %s{whereClause}" + /// Query to patch (partially update) a document by its ID [] let byId tableName = - $"""UPDATE %s{tableName} SET data = json_patch(data, json(@data)) WHERE {Query.whereById "@id"}""" + Query.whereById "@id" |> update tableName /// Query to patch (partially update) a document via a comparison on a JSON field [] let byField tableName field = - sprintf - "UPDATE %s SET data = json_patch(data, json(@data)) WHERE %s" - tableName (Query.whereByField field "@field") + Query.whereByField field "@field" |> update tableName - /// Queries to remove a field from a document - module RemoveField = + /// Queries to remove fields from documents + module RemoveFields = - /// Query to remove a field from a document by the document's ID - [] - let byId tableName = - $"""UPDATE %s{tableName} SET data = json_remove(data, @name) WHERE {Query.whereById "@id"}""" + /// Create an UPDATE statement to remove parameters + let internal update tableName (parameters: SqliteParameter list) whereClause = + let paramNames = parameters |> List.map _.ParameterName |> String.concat ", " + $"UPDATE %s{tableName} SET data = json_remove(data, {paramNames}) WHERE {whereClause}" + + /// Query to remove fields from a document by the document's ID + [] + let byId tableName parameters = + Query.whereById "@id" |> update tableName parameters - /// Query to remove a field from a document via a comparison on a JSON field within the document - [] - let byField tableName field = - $"""UPDATE %s{tableName} SET data = json_remove(data, @name) WHERE {Query.whereByField field "@field"}""" + /// Query to remove fields from a document by the document's ID + let ById(tableName, parameters) = + byId tableName (List.ofSeq parameters) + + /// Query to remove fields from documents via a comparison on a JSON field within the document + [] + let byField tableName field parameters = + Query.whereByField field "@field" |> update tableName parameters + + /// Query to remove fields from documents via a comparison on a JSON field within the document + let ByField(tableName, field, parameters) = + byField tableName field (List.ofSeq parameters) /// Parameter handling helpers @@ -84,21 +99,23 @@ module Parameters = /// Create a JSON field parameter (name "@field") [] - let addFieldParam field parameters = - match field.Op with - | EX | NEX -> parameters - | _ -> SqliteParameter("@field", field.Value) :: parameters + let addFieldParam name field parameters = + match field.Op with EX | NEX -> parameters | _ -> SqliteParameter(name, field.Value) :: parameters /// Create a JSON field parameter (name "@field") - let AddField field parameters = + let AddField(name, field, parameters) = match field.Op with | EX | NEX -> parameters - | _ -> SqliteParameter("@field", field.Value) |> Seq.singleton |> Seq.append parameters + | _ -> SqliteParameter(name, field.Value) |> Seq.singleton |> Seq.append parameters - /// Create a JSON field name parameter (name "@name") - [] - let fieldNameParam name = - SqliteParameter("@name", $"$.%s{name}") + /// Append JSON field name parameters for the given field names to the given parameters + [] + let fieldNameParams paramName (fieldNames: string list) = + fieldNames |> List.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}")) + + /// Append JSON field name parameters for the given field names to the given parameters + let FieldNames(paramName, fieldNames: string seq) = + fieldNames |> Seq.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}")) /// An empty parameter sequence [] @@ -250,7 +267,7 @@ module WithConn = /// Count matching documents using a comparison on a JSON field [] let byField tableName field conn = - Custom.scalar (Query.Count.byField tableName field) (addFieldParam field []) toCount conn + Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount conn /// Commands to determine if documents exist [] @@ -264,7 +281,7 @@ module WithConn = /// Determine if a document exists using a comparison on a JSON field [] let byField tableName field conn = - Custom.scalar (Query.Exists.byField tableName field) (addFieldParam field []) toExists conn + Custom.scalar (Query.Exists.byField tableName field) (addFieldParam "@field" field []) toExists conn /// Commands to retrieve documents [] @@ -291,21 +308,24 @@ module WithConn = /// Retrieve documents via a comparison on a JSON field [] let byField<'TDoc> tableName field conn = - Custom.list<'TDoc> (Query.Find.byField tableName field) (addFieldParam field []) fromData<'TDoc> conn + Custom.list<'TDoc> + (Query.Find.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> conn /// Retrieve documents via a comparison on a JSON field let ByField<'TDoc>(tableName, field, conn) = - Custom.List<'TDoc>(Query.Find.byField tableName field, addFieldParam field [], fromData<'TDoc>, conn) + Custom.List<'TDoc>( + Query.Find.byField tableName field, addFieldParam "@field" field [], fromData<'TDoc>, conn) /// Retrieve documents via a comparison on a JSON field, returning only the first result [] let firstByField<'TDoc> tableName field conn = - Custom.single $"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam field []) fromData<'TDoc> conn + Custom.single + $"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam "@field" field []) fromData<'TDoc> conn /// Retrieve documents via a comparison on a JSON field, returning only the first result let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) = Custom.Single( - $"{Query.Find.byField tableName field} LIMIT 1", addFieldParam field [], fromData<'TDoc>, conn) + $"{Query.Find.byField tableName field} LIMIT 1", addFieldParam "@field" field [], fromData<'TDoc>, conn) /// Commands to update documents [] @@ -337,23 +357,34 @@ module WithConn = /// Patch documents using a comparison on a JSON field [] let byField tableName field (patch: 'TPatch) (conn: SqliteConnection) = - Custom.nonQuery (Query.Patch.byField tableName field) (addFieldParam field [ jsonParam "@data" patch ]) conn + Custom.nonQuery + (Query.Patch.byField tableName field) (addFieldParam "@field" field [ jsonParam "@data" patch ]) conn /// Commands to remove fields from documents [] - module RemoveField = + module RemoveFields = - /// Remove a field from a document by the document's ID - [] - let byId tableName (docId: 'TKey) fieldName conn = - Custom.nonQuery (Query.RemoveField.byId tableName) [ idParam docId; fieldNameParam fieldName ] conn + /// Remove fields from a document by the document's ID + [] + let byId tableName (docId: 'TKey) fieldNames conn = + let nameParams = fieldNameParams "@name" fieldNames + Custom.nonQuery (Query.RemoveFields.byId tableName nameParams) (idParam docId :: nameParams) conn - /// Remove a field from a document via a comparison on a JSON field in the document - [] - let byField tableName field fieldName conn = + /// Remove fields from a document by the document's ID + let ById(tableName, docId: 'TKey, fieldNames, conn) = + byId tableName docId (List.ofSeq fieldNames) conn + + /// Remove fields from documents via a comparison on a JSON field in the document + [] + let byField tableName field fieldNames conn = + let nameParams = fieldNameParams "@name" fieldNames Custom.nonQuery - (Query.RemoveField.byField tableName field) (addFieldParam field [ fieldNameParam fieldName ]) conn + (Query.RemoveFields.byField tableName field nameParams) (addFieldParam "@field" field nameParams) conn + /// Remove fields from documents via a comparison on a JSON field in the document + let ByField(tableName, field, fieldNames, conn) = + byField tableName field (List.ofSeq fieldNames) conn + /// Commands to delete documents [] module Delete = @@ -366,7 +397,7 @@ module WithConn = /// Delete documents by matching a comparison on a JSON field [] let byField tableName field conn = - Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam field []) conn + Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam "@field" field []) conn /// Commands to execute custom SQL queries @@ -563,19 +594,29 @@ module Patch = /// Commands to remove fields from documents [] -module RemoveField = +module RemoveFields = - /// Remove a field from a document by the document's ID - [] - let byId tableName (docId: 'TKey) fieldName = + /// Remove fields from a document by the document's ID + [] + let byId tableName (docId: 'TKey) fieldNames = use conn = Configuration.dbConn () - WithConn.RemoveField.byId tableName docId fieldName conn + WithConn.RemoveFields.byId tableName docId fieldNames conn - /// Remove a field from a document via a comparison on a JSON field in the document - [] - let byField tableName field fieldName = + /// Remove fields from a document by the document's ID + let ById(tableName, docId: 'TKey, fieldNames) = use conn = Configuration.dbConn () - WithConn.RemoveField.byField tableName field fieldName conn + WithConn.RemoveFields.ById(tableName, docId, fieldNames, conn) + + /// Remove field from documents via a comparison on a JSON field in the document + [] + let byField tableName field fieldNames = + use conn = Configuration.dbConn () + WithConn.RemoveFields.byField tableName field fieldNames conn + + /// Remove field from documents via a comparison on a JSON field in the document + let ByField(tableName, field, fieldNames) = + use conn = Configuration.dbConn () + WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn) /// Commands to delete documents [] diff --git a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs index 7dede84..fdfe528 100644 --- a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs +++ b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs @@ -711,6 +711,183 @@ public class PostgresCSharpExtensionTests await conn.PatchByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" }); }) }), + TestList("RemoveFieldsById", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "two"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "two"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + // This not raising an exception is the test + await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "AFieldThatIsNotThere" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + + // This not raising an exception is the test + await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "Value" ]); + }) + }), + TestList("RemoveFieldsByField", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + // This not raising an exception is the test + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Nothing" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + + // This not raising an exception is the test + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); + }) + }), + TestList("RemoveFieldsByContains", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + // This not raising an exception is the test + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Nothing" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + + // This not raising an exception is the test + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, [ "Value" ]); + }) + }), + TestList("RemoveFieldsByJsonPath", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + // This not raising an exception is the test + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Nothing" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + + // This not raising an exception is the test + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", + [ "Value" ]); + }) + }), TestList("DeleteById", new[] { TestCase("succeeds when a document is deleted", async () => diff --git a/src/Tests.CSharp/PostgresCSharpTests.cs b/src/Tests.CSharp/PostgresCSharpTests.cs index 84a0080..88b69a2 100644 --- a/src/Tests.CSharp/PostgresCSharpTests.cs +++ b/src/Tests.CSharp/PostgresCSharpTests.cs @@ -11,7 +11,7 @@ using static Runner; /// /// C# tests for the PostgreSQL implementation of BitBadger.Documents /// -public class PostgresCSharpTests +public static class PostgresCSharpTests { /// /// Tests which do not hit the database @@ -36,7 +36,8 @@ public class PostgresCSharpTests { TestCase("succeeds when a parameter is added", () => { - var it = Parameters.AddField(Field.EQ("it", "242"), Enumerable.Empty>()) + var it = Parameters + .AddField("@field", Field.EQ("it", "242"), Enumerable.Empty>()) .ToList(); Expect.hasLength(it, 1, "There should have been a parameter added"); Expect.equal(it[0].Item1, "@field", "Field parameter not constructed correctly"); @@ -44,10 +45,37 @@ public class PostgresCSharpTests }), TestCase("succeeds when a parameter is not added", () => { - var it = Parameters.AddField(Field.EX("It"), Enumerable.Empty>()); + var it = Parameters.AddField("@it", Field.EX("It"), Enumerable.Empty>()); Expect.isEmpty(it, "There should not have been any parameters added"); }) }), + TestList("RemoveFields", new[] + { + TestCase("ById succeeds", () => + { + Expect.equal(Postgres.Query.RemoveFields.ById("tbl"), + "UPDATE tbl SET data = data - @name WHERE data ->> 'Id' = @id", + "Remove field by ID query not correct"); + }), + TestCase("ByField succeeds", () => + { + Expect.equal(Postgres.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0)), + "UPDATE tbl SET data = data - @name WHERE data ->> 'Fly' < @field", + "Remove field by field query not correct"); + }), + TestCase("ByContains succeeds", () => + { + Expect.equal(Postgres.Query.RemoveFields.ByContains("tbl"), + "UPDATE tbl SET data = data - @name WHERE data @> @criteria", + "Remove field by contains query not correct"); + }), + TestCase("ByJsonPath succeeds", () => + { + Expect.equal(Postgres.Query.RemoveFields.ByJsonPath("tbl"), + "UPDATE tbl SET data = data - @name WHERE data @? @path::jsonpath", + "Remove field by JSON path query not correct"); + }) + }), TestCase("None succeeds", () => { Expect.isEmpty(Parameters.None, "The no-params sequence should be empty"); @@ -883,6 +911,170 @@ public class PostgresCSharpTests }) }) }), + TestList("RemoveFields", new[] + { + TestList("ById", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ById(PostgresDb.TableName, "two", [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "two"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ById(PostgresDb.TableName, "two", [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "two"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + // This not raising an exception is the test + await RemoveFields.ById(PostgresDb.TableName, "two", [ "AFieldThatIsNotThere" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + + // This not raising an exception is the test + await RemoveFields.ById(PostgresDb.TableName, "two", [ "Value" ]); + }) + }), + TestList("ByField", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + // This not raising an exception is the test + await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Nothing" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + + // This not raising an exception is the test + await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); + }) + }), + TestList("ByContains", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + // This not raising an exception is the test + await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Nothing" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + + // This not raising an exception is the test + await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, [ "Value" ]); + }) + }), + TestList("ByJsonPath", new[] + { + TestCase("succeeds when multiple fields are removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub", "Value" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a single field is removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub" ]); + var updated = await Find.ById(PostgresDb.TableName, "four"); + Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.notEqual(updated.Value, "", "The string value should not have been removed"); + Expect.isNull(updated.Sub, "The sub-document should have been removed"); + }), + TestCase("succeeds when a field is not removed", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + // This not raising an exception is the test + await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Nothing" ]); + }), + TestCase("succeeds when no document is matched", async () => + { + await using var db = PostgresDb.BuildDb(); + + // This not raising an exception is the test + await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", + [ "Value" ]); + }) + }) + }), TestList("Delete", new[] { TestList("ById", new[] diff --git a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs index cc5061d..7accd98 100644 --- a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs +++ b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs @@ -467,17 +467,18 @@ public static class SqliteCSharpExtensionTests await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); }) }), - TestList("RemoveFieldById", new[] + TestList("RemoveFieldsById", new[] { - TestCase("succeeds when a field is removed", async () => + TestCase("succeeds when fields are removed", async () => { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.RemoveFieldById(SqliteDb.TableName, "two", "Sub"); + await conn.RemoveFieldsById(SqliteDb.TableName, "two", [ "Sub", "Value" ]); var updated = await Find.ById(SqliteDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); }), TestCase("succeeds when a field is not removed", async () => @@ -487,7 +488,7 @@ public static class SqliteCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldById(SqliteDb.TableName, "two", "AFieldThatIsNotThere"); + await conn.RemoveFieldsById(SqliteDb.TableName, "two", [ "AFieldThatIsNotThere" ]); }), TestCase("succeeds when no document is matched", async () => { @@ -495,10 +496,10 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); // This not raising an exception is the test - await conn.RemoveFieldById(SqliteDb.TableName, "two", "Value"); + await conn.RemoveFieldsById(SqliteDb.TableName, "two", [ "Value" ]); }) }), - TestList("RemoveFieldByField", new[] + TestList("RemoveFieldsByField", new[] { TestCase("succeeds when a field is removed", async () => { @@ -506,7 +507,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.RemoveFieldByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Sub"); + await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Sub" ]); var updated = await Find.ById(SqliteDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); @@ -518,7 +519,7 @@ public static class SqliteCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Nothing"); + await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Nothing" ]); }), TestCase("succeeds when no document is matched", async () => { @@ -526,7 +527,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); // This not raising an exception is the test - await conn.RemoveFieldByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), "Value"); + await conn.RemoveFieldsByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); }) }), TestList("DeleteById", new[] diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs index 3c6559b..4e050e2 100644 --- a/src/Tests.CSharp/SqliteCSharpTests.cs +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -41,18 +41,19 @@ public static class SqliteCSharpTests "UPDATE partial by JSON comparison query not correct"); }) }), - TestList("RemoveField", new[] + TestList("RemoveFields", new[] { TestCase("ById succeeds", () => { - Expect.equal(Sqlite.Query.RemoveField.ById("tbl"), + Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", [ new SqliteParameter("@name", "one") ]), "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", Field.LT("Fly", 0)), - "UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Fly' < @field", + Expect.equal(Sqlite.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0), + [ new SqliteParameter("@name0", "one"), new SqliteParameter("@name1", "two") ]), + "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' < @field", "Remove field by field query not correct"); }) }) @@ -73,7 +74,8 @@ public static class SqliteCSharpTests }), TestCase("AddField succeeds when adding a parameter", () => { - var paramList = Parameters.AddField(Field.EQ("it", 99), Enumerable.Empty()).ToList(); + var paramList = Parameters.AddField("@field", Field.EQ("it", 99), Enumerable.Empty()) + .ToList(); Expect.hasLength(paramList, 1, "There should have been a parameter added"); var theParam = paramList[0]; Expect.equal(theParam.ParameterName, "@field", "The parameter name is incorrect"); @@ -81,7 +83,7 @@ public static class SqliteCSharpTests }), TestCase("AddField succeeds when not adding a parameter", () => { - var paramSeq = Parameters.AddField(Field.EX("Coffee"), Enumerable.Empty()); + var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), Enumerable.Empty()); Expect.isEmpty(paramSeq, "There should not have been any parameters added"); }), TestCase("None succeeds", () => @@ -92,14 +94,14 @@ public static class SqliteCSharpTests // Results are exhaustively executed in the context of other tests }); - private static readonly List TestDocuments = new() - { + private static readonly List TestDocuments = + [ new() { Id = "one", Value = "FIRST!", NumValue = 0 }, new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } }, new() { Id = "three", Value = "", NumValue = 4 }, new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, new() { Id = "five", Value = "purple", NumValue = 18 } - }; + ]; /// /// Add the test documents to the database @@ -563,18 +565,19 @@ public static class SqliteCSharpTests }) }) }), - TestList("RemoveField", new[] + TestList("RemoveFields", new[] { TestList("ById", new[] { - TestCase("succeeds when a field is removed", async () => + TestCase("succeeds when fields are removed", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await RemoveField.ById(SqliteDb.TableName, "two", "Sub"); + await RemoveFields.ById(SqliteDb.TableName, "two", [ "Sub", "Value" ]); var updated = await Find.ById(SqliteDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); + Expect.equal(updated.Value, "", "The string value should have been removed"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); }), TestCase("succeeds when a field is not removed", async () => @@ -583,14 +586,14 @@ public static class SqliteCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveField.ById(SqliteDb.TableName, "two", "AFieldThatIsNotThere"); + await RemoveFields.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"); + await RemoveFields.ById(SqliteDb.TableName, "two", [ "Value" ]); }) }), TestList("ByField", new[] @@ -600,7 +603,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await RemoveField.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Sub"); + await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Sub" ]); var updated = await Find.ById(SqliteDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); @@ -611,14 +614,14 @@ public static class SqliteCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveField.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), "Nothing"); + await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Nothing" ]); }), 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.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), "Value"); + await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); }) }) }), diff --git a/src/Tests/PostgresExtensionTests.fs b/src/Tests/PostgresExtensionTests.fs index cfc0135..41b1210 100644 --- a/src/Tests/PostgresExtensionTests.fs +++ b/src/Tests/PostgresExtensionTests.fs @@ -616,6 +616,162 @@ let integrationTests = do! conn.patchByJsonPath PostgresDb.TableName "$.NumValue ? (@ < 0)" {| Foo = "green" |} } ] + testList "removeFieldsById" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub"; "Value" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + // This not raising an exception is the test + do! conn.removeFieldsById PostgresDb.TableName "two" [ "AFieldThatIsNotThere" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + + // This not raising an exception is the test + do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ] + } + ] + testList "removeFieldsByField" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + // This not raising an exception is the test + do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + + // This not raising an exception is the test + do! conn.removeFieldsByField PostgresDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ] + } + ] + testList "removeFieldsByContains" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + // This not raising an exception is the test + do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Nothing" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + + // This not raising an exception is the test + do! conn.removeFieldsByContains PostgresDb.TableName {| Abracadabra = "apple" |} [ "Value" ] + } + ] + testList "removeFieldsByJsonPath" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ] + let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + // This not raising an exception is the test + do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Nothing" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + + // This not raising an exception is the test + do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.Abracadabra ? (@ == \"apple\")" [ "Value" ] + } + ] testList "deleteById" [ testTask "succeeds when a document is deleted" { use db = PostgresDb.BuildDb() diff --git a/src/Tests/PostgresTests.fs b/src/Tests/PostgresTests.fs index b0ef091..29384ec 100644 --- a/src/Tests/PostgresTests.fs +++ b/src/Tests/PostgresTests.fs @@ -20,7 +20,7 @@ let unitTests = } testList "addFieldParam" [ test "succeeds when a parameter is added" { - let paramList = addFieldParam (Field.EQ "it" "242") [] + let paramList = addFieldParam "@field" (Field.EQ "it" "242") [] Expect.hasLength paramList 1 "There should have been a parameter added" let it = paramList[0] Expect.equal (fst it) "@field" "Field parameter name not correct" @@ -31,7 +31,7 @@ let unitTests = | _ -> Expect.isTrue false "The parameter was not a Parameter type" } test "succeeds when a parameter is not added" { - let paramList = addFieldParam (Field.EX "tacos") [] + let paramList = addFieldParam "@field" (Field.EX "tacos") [] Expect.isEmpty paramList "There should not have been any parameters added" } ] @@ -135,6 +135,32 @@ let unitTests = "UPDATE partial by JSON Path statement not correct" } ] + testList "RemoveFields" [ + test "byId succeeds" { + Expect.equal + (Query.RemoveFields.byId "tbl") + "UPDATE tbl SET data = data - @name WHERE data ->> 'Id' = @id" + "Remove field by ID query not correct" + } + test "byField succeeds" { + Expect.equal + (Query.RemoveFields.byField "tbl" (Field.LT "Fly" 0)) + "UPDATE tbl SET data = data - @name WHERE data ->> 'Fly' < @field" + "Remove field by field query not correct" + } + test "byContains succeeds" { + Expect.equal + (Query.RemoveFields.byContains "tbl") + "UPDATE tbl SET data = data - @name WHERE data @> @criteria" + "Remove field by contains query not correct" + } + test "byJsonPath succeeds" { + Expect.equal + (Query.RemoveFields.byJsonPath "tbl") + "UPDATE tbl SET data = data - @name WHERE data @? @path::jsonpath" + "Remove field by JSON path query not correct" + } + ] testList "Delete" [ test "byContains succeeds" { Expect.equal (Query.Delete.byContains PostgresDb.TableName) @@ -743,6 +769,148 @@ let integrationTests = } ] ] + testList "RemoveFields" [ + testList "byId" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub"; "Value" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + // This not raising an exception is the test + do! RemoveFields.byId PostgresDb.TableName "two" [ "AFieldThatIsNotThere" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + + // This not raising an exception is the test + do! RemoveFields.byId PostgresDb.TableName "two" [ "Value" ] + } + ] + testList "byField" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + // This not raising an exception is the test + do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + + // This not raising an exception is the test + do! RemoveFields.byField PostgresDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ] + } + ] + testList "byContains" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + // This not raising an exception is the test + do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Nothing" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + + // This not raising an exception is the test + do! RemoveFields.byContains PostgresDb.TableName {| Abracadabra = "apple" |} [ "Value" ] + } + ] + testList "byJsonPath" [ + testTask "succeeds when multiple fields are removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 1 "There should be 1 document without Value fields" + } + testTask "succeeds when a single field is removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ] + let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub") + Expect.equal noSubs 4 "There should now be 4 documents without Sub fields" + let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value") + Expect.equal noValue 0 "There should be no documents without Value fields" + } + testTask "succeeds when a field is not removed" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + // This not raising an exception is the test + do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Nothing" ] + } + testTask "succeeds when no document is matched" { + use db = PostgresDb.BuildDb() + + // This not raising an exception is the test + do! RemoveFields.byJsonPath PostgresDb.TableName "$.Abracadabra ? (@ == \"apple\")" [ "Value" ] + } + ] + ] testList "Delete" [ testList "byId" [ testTask "succeeds when a document is deleted" { diff --git a/src/Tests/SqliteExtensionTests.fs b/src/Tests/SqliteExtensionTests.fs index ee89a9b..d8a43ff 100644 --- a/src/Tests/SqliteExtensionTests.fs +++ b/src/Tests/SqliteExtensionTests.fs @@ -345,13 +345,13 @@ let integrationTests = do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |} } ] - testList "removeFieldById" [ - testTask "succeeds when a field is removed" { + testList "removeFieldsById" [ + testTask "succeeds when fields are removed" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs () - do! conn.removeFieldById SqliteDb.TableName "two" "Sub" + do! conn.removeFieldsById SqliteDb.TableName "two" [ "Sub"; "Value" ] try let! _ = conn.findById SqliteDb.TableName "two" Expect.isTrue false "The updated document should have failed to parse" @@ -365,14 +365,14 @@ let integrationTests = do! loadDocs () // This not raising an exception is the test - do! conn.removeFieldById SqliteDb.TableName "two" "AFieldThatIsNotThere" + do! conn.removeFieldsById SqliteDb.TableName "two" [ "AFieldThatIsNotThere" ] } testTask "succeeds when no document is matched" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () // This not raising an exception is the test - do! conn.removeFieldById SqliteDb.TableName "two" "Value" + do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ] } ] testList "removeFieldByField" [ @@ -381,7 +381,7 @@ let integrationTests = use conn = Configuration.dbConn () do! loadDocs () - do! conn.removeFieldByField SqliteDb.TableName (Field.EQ "NumValue" 17) "Sub" + do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ] try let! _ = conn.findById SqliteDb.TableName "four" Expect.isTrue false "The updated document should have failed to parse" @@ -395,14 +395,14 @@ let integrationTests = do! loadDocs () // This not raising an exception is the test - do! conn.removeFieldByField SqliteDb.TableName (Field.EQ "NumValue" 17) "Nothing" + do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ] } testTask "succeeds when no document is matched" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () // This not raising an exception is the test - do! conn.removeFieldByField SqliteDb.TableName (Field.NE "Abracadabra" "apple") "Value" + do! conn.removeFieldsByField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ] } ] testList "deleteById" [ diff --git a/src/Tests/SqliteTests.fs b/src/Tests/SqliteTests.fs index a16b37f..9063d2e 100644 --- a/src/Tests/SqliteTests.fs +++ b/src/Tests/SqliteTests.fs @@ -32,17 +32,20 @@ let unitTests = "UPDATE partial by JSON comparison query not correct" } ] - testList "RemoveField" [ + testList "RemoveFields" [ test "byId succeeds" { Expect.equal - (Query.RemoveField.byId "tbl") + (Query.RemoveFields.byId "tbl" [ SqliteParameter("@name", "one") ]) "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" (Field.GT "Fly" 0)) - "UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Fly' > @field" + (Query.RemoveFields.byField + "tbl" + (Field.GT "Fly" 0) + [ SqliteParameter("@name0", "one"); SqliteParameter("@name1", "two") ]) + "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' > @field" "Remove field by field query not correct" } ] @@ -60,14 +63,14 @@ let unitTests = } testList "addFieldParam" [ test "succeeds when adding a parameter" { - let paramList = addFieldParam (Field.EQ "it" 99) [] + let paramList = addFieldParam "@field" (Field.EQ "it" 99) [] Expect.hasLength paramList 1 "There should have been a parameter added" let theParam = paramList[0] Expect.equal theParam.ParameterName "@field" "The parameter name is incorrect" Expect.equal theParam.Value 99 "The parameter value is incorrect" } test "succeeds when not adding a parameter" { - let paramList = addFieldParam (Field.NEX "Coffee") [] + let paramList = addFieldParam "@it" (Field.NEX "Coffee") [] Expect.isEmpty paramList "There should not have been any parameters added" } ] @@ -512,13 +515,13 @@ let integrationTests = } ] ] - testList "RemoveField" [ + testList "RemoveFields" [ testList "byId" [ - testTask "succeeds when a field is removed" { + testTask "succeeds when fields is removed" { use! db = SqliteDb.BuildDb() do! loadDocs () - do! RemoveField.byId SqliteDb.TableName "two" "Sub" + do! RemoveFields.byId SqliteDb.TableName "two" [ "Sub"; "Value" ] try let! _ = Find.byId SqliteDb.TableName "two" Expect.isTrue false "The updated document should have failed to parse" @@ -531,13 +534,13 @@ let integrationTests = do! loadDocs () // This not raising an exception is the test - do! RemoveField.byId SqliteDb.TableName "two" "AFieldThatIsNotThere" + do! RemoveFields.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" + do! RemoveFields.byId SqliteDb.TableName "two" [ "Value" ] } ] testList "byField" [ @@ -545,7 +548,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() do! loadDocs () - do! RemoveField.byField SqliteDb.TableName (Field.EQ "NumValue" 17) "Sub" + do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ] try let! _ = Find.byId SqliteDb.TableName "four" Expect.isTrue false "The updated document should have failed to parse" @@ -558,13 +561,13 @@ let integrationTests = do! loadDocs () // This not raising an exception is the test - do! RemoveField.byField SqliteDb.TableName (Field.EQ "NumValue" 17) "Nothing" + do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ] } testTask "succeeds when no document is matched" { use! db = SqliteDb.BuildDb() // This not raising an exception is the test - do! RemoveField.byField SqliteDb.TableName (Field.NE "Abracadabra" "apple") "Value" + do! RemoveFields.byField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ] } ] ] -- 2.45.1 From e98d13bbc61e096827c79fcfd5325eb885bf1fb0 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Tue, 23 Jan 2024 20:50:09 -0500 Subject: [PATCH 6/6] Downgrade tests to C# 10 - Update version/package release notes --- src/Common/BitBadger.Documents.Common.fsproj | 2 +- src/Directory.Build.props | 2 +- .../BitBadger.Documents.Postgres.fsproj | 2 +- src/Sqlite/BitBadger.Documents.Sqlite.fsproj | 4 +- .../PostgresCSharpExtensionTests.cs | 37 +++++++++++-------- src/Tests.CSharp/PostgresCSharpTests.cs | 37 +++++++++++-------- .../SqliteCSharpExtensionTests.cs | 12 +++--- src/Tests.CSharp/SqliteCSharpTests.cs | 22 +++++------ 8 files changed, 64 insertions(+), 54 deletions(-) diff --git a/src/Common/BitBadger.Documents.Common.fsproj b/src/Common/BitBadger.Documents.Common.fsproj index 2c3f063..f0d42e9 100644 --- a/src/Common/BitBadger.Documents.Common.fsproj +++ b/src/Common/BitBadger.Documents.Common.fsproj @@ -1,7 +1,7 @@  - Initial release (RC 1) + Added Field type for by-field operations JSON Document SQL diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d587c48..635e0d6 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -6,7 +6,7 @@ 3.0.0.0 3.0.0.0 3.0.0 - rc-1 + rc-2 danieljsummers Bit Badger Solutions README.md diff --git a/src/Postgres/BitBadger.Documents.Postgres.fsproj b/src/Postgres/BitBadger.Documents.Postgres.fsproj index 7a7af5b..2df863a 100644 --- a/src/Postgres/BitBadger.Documents.Postgres.fsproj +++ b/src/Postgres/BitBadger.Documents.Postgres.fsproj @@ -1,7 +1,7 @@  - Initial release; migrated from BitBadger.Npgsql.Documents, with field and extension additions (RC 1) + Adds Field type for by-field operations (BREAKING from rc-1); adds RemoveFields* functions JSON Document PostgreSQL Npgsql diff --git a/src/Sqlite/BitBadger.Documents.Sqlite.fsproj b/src/Sqlite/BitBadger.Documents.Sqlite.fsproj index 864b7a4..4c108db 100644 --- a/src/Sqlite/BitBadger.Documents.Sqlite.fsproj +++ b/src/Sqlite/BitBadger.Documents.Sqlite.fsproj @@ -1,7 +1,7 @@  - Initial release; SQLite document implementation similar to BitBadger.Npgsql.Documents (RC 1) + Adds Field type for by-field operations (BREAKING from rc-1); adds RemoveFields* functions JSON Document SQLite @@ -13,7 +13,7 @@ - + diff --git a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs index fdfe528..b4afe7f 100644 --- a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs +++ b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs @@ -719,7 +719,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "Sub", "Value" ]); + await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -731,7 +731,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "Sub" ]); + await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -744,7 +744,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "AFieldThatIsNotThere" ]); + await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" }); }), TestCase("succeeds when no document is matched", async () => { @@ -752,7 +752,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); // This not raising an exception is the test - await conn.RemoveFieldsById(PostgresDb.TableName, "two", [ "Value" ]); + await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Value" }); }) }), TestList("RemoveFieldsByField", new[] @@ -763,7 +763,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub", "Value" ]); + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), + new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -775,7 +776,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub" ]); + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -788,7 +789,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Nothing" ]); + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { @@ -796,7 +797,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); // This not raising an exception is the test - await conn.RemoveFieldsByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); + await conn.RemoveFieldsByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), + new[] { "Value" }); }) }), TestList("RemoveFieldsByContains", new[] @@ -807,7 +809,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub", "Value" ]); + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, + new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -819,7 +822,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub" ]); + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -832,7 +835,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Nothing" ]); + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { @@ -840,7 +843,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); // This not raising an exception is the test - await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, [ "Value" ]); + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, + new[] { "Value" }); }) }), TestList("RemoveFieldsByJsonPath", new[] @@ -851,7 +855,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub", "Value" ]); + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", + new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -863,7 +868,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub" ]); + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -876,7 +881,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Nothing" ]); + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { @@ -885,7 +890,7 @@ public class PostgresCSharpExtensionTests // This not raising an exception is the test await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", - [ "Value" ]); + new[] { "Value" }); }) }), TestList("DeleteById", new[] diff --git a/src/Tests.CSharp/PostgresCSharpTests.cs b/src/Tests.CSharp/PostgresCSharpTests.cs index 88b69a2..1d6c158 100644 --- a/src/Tests.CSharp/PostgresCSharpTests.cs +++ b/src/Tests.CSharp/PostgresCSharpTests.cs @@ -920,7 +920,7 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ById(PostgresDb.TableName, "two", [ "Sub", "Value" ]); + await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -931,7 +931,7 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ById(PostgresDb.TableName, "two", [ "Sub" ]); + await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -943,14 +943,14 @@ public static class PostgresCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveFields.ById(PostgresDb.TableName, "two", [ "AFieldThatIsNotThere" ]); + await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" }); }), TestCase("succeeds when no document is matched", async () => { await using var db = PostgresDb.BuildDb(); // This not raising an exception is the test - await RemoveFields.ById(PostgresDb.TableName, "two", [ "Value" ]); + await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Value" }); }) }), TestList("ByField", new[] @@ -960,7 +960,8 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub", "Value" ]); + await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), + new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -971,7 +972,7 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Sub" ]); + await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -983,14 +984,15 @@ public static class PostgresCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), [ "Nothing" ]); + await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { await using var db = PostgresDb.BuildDb(); // This not raising an exception is the test - await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); + await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), + new[] { "Value" }); }) }), TestList("ByContains", new[] @@ -1000,7 +1002,8 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub", "Value" ]); + await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, + new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -1011,7 +1014,7 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Sub" ]); + await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -1023,14 +1026,15 @@ public static class PostgresCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, [ "Nothing" ]); + await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { await using var db = PostgresDb.BuildDb(); // This not raising an exception is the test - await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, [ "Value" ]); + await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, + new[] { "Value" }); }) }), TestList("ByJsonPath", new[] @@ -1040,7 +1044,8 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub", "Value" ]); + await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", + new[] { "Sub", "Value" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -1051,7 +1056,7 @@ public static class PostgresCSharpTests await using var db = PostgresDb.BuildDb(); await LoadDocs(); - await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Sub" ]); + await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" }); var updated = await Find.ById(PostgresDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.notEqual(updated.Value, "", "The string value should not have been removed"); @@ -1063,7 +1068,7 @@ public static class PostgresCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", [ "Nothing" ]); + await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { @@ -1071,7 +1076,7 @@ public static class PostgresCSharpTests // This not raising an exception is the test await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", - [ "Value" ]); + new[] { "Value" }); }) }) }), diff --git a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs index 7accd98..3501695 100644 --- a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs +++ b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs @@ -475,7 +475,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.RemoveFieldsById(SqliteDb.TableName, "two", [ "Sub", "Value" ]); + await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Sub", "Value" }); var updated = await Find.ById(SqliteDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -488,7 +488,7 @@ public static class SqliteCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsById(SqliteDb.TableName, "two", [ "AFieldThatIsNotThere" ]); + await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" }); }), TestCase("succeeds when no document is matched", async () => { @@ -496,7 +496,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); // This not raising an exception is the test - await conn.RemoveFieldsById(SqliteDb.TableName, "two", [ "Value" ]); + await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Value" }); }) }), TestList("RemoveFieldsByField", new[] @@ -507,7 +507,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); await LoadDocs(); - await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Sub" ]); + await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" }); var updated = await Find.ById(SqliteDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); @@ -519,7 +519,7 @@ public static class SqliteCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Nothing" ]); + await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { @@ -527,7 +527,7 @@ public static class SqliteCSharpExtensionTests await using var conn = Sqlite.Configuration.DbConn(); // This not raising an exception is the test - await conn.RemoveFieldsByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); + await conn.RemoveFieldsByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" }); }) }), TestList("DeleteById", new[] diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs index 4e050e2..be035e8 100644 --- a/src/Tests.CSharp/SqliteCSharpTests.cs +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -45,14 +45,14 @@ public static class SqliteCSharpTests { TestCase("ById succeeds", () => { - Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", [ new SqliteParameter("@name", "one") ]), + Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", new[] { new SqliteParameter("@name", "one") }), "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.RemoveFields.ByField("tbl", Field.LT("Fly", 0), - [ new SqliteParameter("@name0", "one"), new SqliteParameter("@name1", "two") ]), + new[] { new SqliteParameter("@name0", "one"), new SqliteParameter("@name1", "two") }), "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' < @field", "Remove field by field query not correct"); }) @@ -94,14 +94,14 @@ public static class SqliteCSharpTests // Results are exhaustively executed in the context of other tests }); - private static readonly List TestDocuments = - [ + private static readonly List TestDocuments = new() + { new() { Id = "one", Value = "FIRST!", NumValue = 0 }, new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } }, new() { Id = "three", Value = "", NumValue = 4 }, new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, new() { Id = "five", Value = "purple", NumValue = 18 } - ]; + }; /// /// Add the test documents to the database @@ -574,7 +574,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await RemoveFields.ById(SqliteDb.TableName, "two", [ "Sub", "Value" ]); + await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Sub", "Value" }); var updated = await Find.ById(SqliteDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); @@ -586,14 +586,14 @@ public static class SqliteCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveFields.ById(SqliteDb.TableName, "two", [ "AFieldThatIsNotThere" ]); + await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "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 RemoveFields.ById(SqliteDb.TableName, "two", [ "Value" ]); + await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" }); }) }), TestList("ByField", new[] @@ -603,7 +603,7 @@ public static class SqliteCSharpTests await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Sub" ]); + await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" }); var updated = await Find.ById(SqliteDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); @@ -614,14 +614,14 @@ public static class SqliteCSharpTests await LoadDocs(); // This not raising an exception is the test - await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), [ "Nothing" ]); + await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { await using var db = await SqliteDb.BuildDb(); // This not raising an exception is the test - await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), [ "Value" ]); + await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" }); }) }) }), -- 2.45.1