diff --git a/src/Postgres/Extensions.fs b/src/Postgres/Extensions.fs index 6492c1a..2b3b63a 100644 --- a/src/Postgres/Extensions.fs +++ b/src/Postgres/Extensions.fs @@ -1,6 +1,5 @@ namespace BitBadger.Documents.Postgres -open BitBadger.Documents open Npgsql open Npgsql.FSharp @@ -54,11 +53,6 @@ module Extensions = member conn.countByFields tableName howMatched fields = WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn) - /// Count matching documents using a JSON field comparison query (->> =) - [] - member conn.countByField tableName field = - conn.countByFields tableName Any [ field ] - /// Count matching documents using a JSON containment query (@>) member conn.countByContains tableName criteria = WithProps.Count.byContains tableName criteria (Sql.existingConnection conn) @@ -75,11 +69,6 @@ module Extensions = member conn.existsByFields tableName howMatched fields = WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn) - /// Determine if documents exist using a JSON field comparison query (->> =) - [] - member conn.existsByField tableName field = - conn.existsByFields tableName Any [ field ] - /// Determine if documents exist using a JSON containment query (@>) member conn.existsByContains tableName criteria = WithProps.Exists.byContains tableName criteria (Sql.existingConnection conn) @@ -110,11 +99,6 @@ module Extensions = WithProps.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields (Sql.existingConnection conn) - /// Retrieve documents matching a JSON field comparison query (->> =) - [] - member conn.findByField<'TDoc> tableName field = - conn.findByFields<'TDoc> tableName Any [ field ] - /// Retrieve documents matching a JSON containment query (@>) member conn.findByContains<'TDoc> tableName (criteria: obj) = WithProps.Find.byContains<'TDoc> tableName criteria (Sql.existingConnection conn) @@ -141,11 +125,6 @@ module Extensions = WithProps.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields (Sql.existingConnection conn) - /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found - [] - member conn.findFirstByField<'TDoc> tableName field = - conn.findFirstByFields<'TDoc> tableName Any [ field ] - /// Retrieve the first document matching a JSON containment query (@>); returns None if not found member conn.findFirstByContains<'TDoc> tableName (criteria: obj) = WithProps.Find.firstByContains<'TDoc> tableName criteria (Sql.existingConnection conn) @@ -180,11 +159,6 @@ module Extensions = member conn.patchByFields tableName howMatched fields (patch: 'TPatch) = WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn) - /// Patch documents using a JSON field comparison query in the WHERE clause (->> =) - [] - member conn.patchByField tableName field (patch: 'TPatch) = - conn.patchByFields tableName Any [ field ] patch - /// Patch documents using a JSON containment query in the WHERE clause (@>) member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) = WithProps.Patch.byContains tableName criteria patch (Sql.existingConnection conn) @@ -201,11 +175,6 @@ module Extensions = member conn.removeFieldsByFields tableName howMatched fields fieldNames = WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (Sql.existingConnection conn) - /// Remove fields from documents via a comparison on a JSON field in the document - [] - member conn.removeFieldsByField tableName field fieldNames = - conn.removeFieldsByFields tableName Any [ field ] fieldNames - /// 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) @@ -221,11 +190,6 @@ module Extensions = member conn.deleteByFields tableName howMatched fields = WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn) - /// Delete documents by matching a JSON field comparison query (->> =) - [] - member conn.deleteByField tableName field = - conn.deleteByFields tableName Any [ field ] - /// Delete documents by matching a JSON containment query (@>) member conn.deleteByContains tableName (criteria: 'TContains) = WithProps.Delete.byContains tableName criteria (Sql.existingConnection conn) @@ -297,12 +261,6 @@ type NpgsqlConnectionCSharpExtensions = static member inline CountByFields(conn, tableName, howMatched, fields) = WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn) - /// Count matching documents using a JSON field comparison query (->> =) - [] - [] - static member inline CountByField(conn, tableName, field) = - conn.CountByFields(tableName, Any, [ field ]) - /// Count matching documents using a JSON containment query (@>) [] static member inline CountByContains(conn, tableName, criteria: 'TCriteria) = @@ -323,12 +281,6 @@ type NpgsqlConnectionCSharpExtensions = static member inline ExistsByFields(conn, tableName, howMatched, fields) = WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn) - /// Determine if documents exist using a JSON field comparison query (->> =) - [] - [] - static member inline ExistsByField(conn, tableName, field) = - conn.ExistsByFields(tableName, Any, [ field ]) - /// Determine if documents exist using a JSON containment query (@>) [] static member inline ExistsByContains(conn, tableName, criteria: 'TCriteria) = @@ -365,12 +317,6 @@ type NpgsqlConnectionCSharpExtensions = WithProps.Find.ByFieldsOrdered<'TDoc>( tableName, howMatched, queryFields, orderFields, Sql.existingConnection conn) - /// Retrieve documents matching a JSON field comparison query (->> =) - [] - [] - static member inline FindByField<'TDoc>(conn, tableName, field) = - conn.FindByFields<'TDoc>(tableName, Any, [ field ]) - /// Retrieve documents matching a JSON containment query (@>) [] static member inline FindByContains<'TDoc>(conn, tableName, criteria: obj) = @@ -404,12 +350,6 @@ type NpgsqlConnectionCSharpExtensions = WithProps.Find.FirstByFieldsOrdered<'TDoc>( tableName, howMatched, queryFields, orderFields, Sql.existingConnection conn) - /// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found - [] - [] - static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) = - conn.FindFirstByFields<'TDoc>(tableName, Any, [ field ]) - /// Retrieve the first document matching a JSON containment query (@>); returns None if not found [] static member inline FindFirstByContains<'TDoc when 'TDoc: null>(conn, tableName, criteria: obj) = @@ -453,12 +393,6 @@ type NpgsqlConnectionCSharpExtensions = static member inline PatchByFields(conn, tableName, howMatched, fields, patch: 'TPatch) = WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn) - /// Patch documents using a JSON field comparison query in the WHERE clause (->> =) - [] - [] - static member inline PatchByField(conn, tableName, field, patch: 'TPatch) = - conn.PatchByFields(tableName, Any, [ field ], patch) - /// Patch documents using a JSON containment query in the WHERE clause (@>) [] static member inline PatchByContains(conn, tableName, criteria: 'TCriteria, patch: 'TPatch) = @@ -479,17 +413,11 @@ type NpgsqlConnectionCSharpExtensions = static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) = WithProps.RemoveFields.byFields tableName howMatched fields 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) = - conn.RemoveFieldsByFields(tableName, Any, [ field ], fieldNames) - /// 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) = @@ -505,12 +433,6 @@ type NpgsqlConnectionCSharpExtensions = static member inline DeleteByFields(conn, tableName, howMatched, fields) = WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn) - /// Delete documents by matching a JSON field comparison query (->> =) - [] - [] - static member inline DeleteByField(conn, tableName, field) = - conn.DeleteByFields(tableName, Any, [ field ]) - /// Delete documents by matching a JSON containment query (@>) [] static member inline DeleteByContains(conn, tableName, criteria: 'TContains) = diff --git a/src/Tests.CSharp/CommonCSharpTests.cs b/src/Tests.CSharp/CommonCSharpTests.cs index 547e300..0dda978 100644 --- a/src/Tests.CSharp/CommonCSharpTests.cs +++ b/src/Tests.CSharp/CommonCSharpTests.cs @@ -330,7 +330,7 @@ public static class CommonCSharpTests { Expect.equal( Query.Definition.EnsureIndexOn("test.table", "gibberish", - new[] { "taco", "guac DESC", "salsa ASC" }, Dialect.SQLite), + ["taco", "guac DESC", "salsa ASC"], Dialect.SQLite), "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table " + "((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)", "CREATE INDEX for multiple field statement incorrect"); diff --git a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs index d23f6d3..054143a 100644 --- a/src/Tests.CSharp/PostgresCSharpExtensionTests.cs +++ b/src/Tests.CSharp/PostgresCSharpExtensionTests.cs @@ -53,7 +53,7 @@ public class PostgresCSharpExtensionTests var docs = await conn.CustomList( $"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", - new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) }, + [Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))], Results.FromData); Expect.isEmpty(docs, "There should have been no documents returned"); }) @@ -67,7 +67,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id", - new[] { Tuple.Create("@id", Sql.@string("one")) }, Results.FromData); + [Tuple.Create("@id", Sql.@string("one"))], Results.FromData); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc.Id, "one", "The incorrect document was returned"); }), @@ -78,7 +78,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id", - new[] { Tuple.Create("@id", Sql.@string("eighty")) }, Results.FromData); + [Tuple.Create("@id", Sql.@string("eighty"))], Results.FromData); Expect.isNull(doc, "There should not have been a document returned"); }) ]), @@ -102,7 +102,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); await conn.CustomNonQuery($"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", - new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) }); + [Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))]); var remaining = await conn.CountAll(PostgresDb.TableName); Expect.equal(remaining, 5, "There should be 5 documents remaining in the table"); @@ -119,55 +119,61 @@ public class PostgresCSharpExtensionTests { await using var db = PostgresDb.BuildDb(); await using var conn = MkConn(db); - var tableExists = () => conn.CustomScalar( - "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None, - Results.ToExists); - var keyExists = () => conn.CustomScalar( - "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None, - Results.ToExists); - var exists = await tableExists(); - var alsoExists = await keyExists(); + var exists = await TableExists(); + var alsoExists = await KeyExists(); Expect.isFalse(exists, "The table should not exist already"); Expect.isFalse(alsoExists, "The key index should not exist already"); await conn.EnsureTable("ensured"); - exists = await tableExists(); - alsoExists = await keyExists(); + exists = await TableExists(); + alsoExists = await KeyExists(); Expect.isTrue(exists, "The table should now exist"); Expect.isTrue(alsoExists, "The key index should now exist"); + return; + + Task KeyExists() => + conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", + Parameters.None, Results.ToExists); + Task TableExists() => + conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", + Parameters.None, Results.ToExists); }), TestCase("EnsureDocumentIndex succeeds", async () => { await using var db = PostgresDb.BuildDb(); await using var conn = MkConn(db); - var indexExists = () => conn.CustomScalar( - "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it", Parameters.None, - Results.ToExists); - var exists = await indexExists(); + var exists = await IndexExists(); Expect.isFalse(exists, "The index should not exist already"); await conn.EnsureTable("ensured"); await conn.EnsureDocumentIndex("ensured", DocumentIndex.Optimized); - exists = await indexExists(); + exists = await IndexExists(); Expect.isTrue(exists, "The index should now exist"); + return; + + Task IndexExists() => + conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it", + Parameters.None, Results.ToExists); }), TestCase("EnsureFieldIndex succeeds", async () => { await using var db = PostgresDb.BuildDb(); await using var conn = MkConn(db); - var indexExists = () => conn.CustomScalar( - "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", Parameters.None, - Results.ToExists); - var exists = await indexExists(); + var exists = await IndexExists(); Expect.isFalse(exists, "The index should not exist already"); await conn.EnsureTable("ensured"); - await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); - exists = await indexExists(); + await conn.EnsureFieldIndex("ensured", "test", ["Id", "Category"]); + exists = await IndexExists(); Expect.isTrue(exists, "The index should now exist"); + return; + + Task IndexExists() => + conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", + Parameters.None, Results.ToExists); }), TestList("Insert", [ @@ -240,17 +246,16 @@ public class PostgresCSharpExtensionTests var theCount = await conn.CountAll(PostgresDb.TableName); Expect.equal(theCount, 5, "There should have been 5 matching documents"); }), -#pragma warning disable CS0618 - TestCase("CountByField succeeds", async () => + TestCase("CountByFields succeeds", async () => { await using var db = PostgresDb.BuildDb(); await using var conn = MkConn(db); await LoadDocs(); - var theCount = await conn.CountByField(PostgresDb.TableName, Field.EQ("Value", "purple")); + var theCount = await conn.CountByFields(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "purple")]); Expect.equal(theCount, 2, "There should have been 2 matching documents"); }), -#pragma warning restore CS0618 TestCase("CountByContains succeeds", async () => { await using var db = PostgresDb.BuildDb(); @@ -290,7 +295,6 @@ public class PostgresCSharpExtensionTests Expect.isFalse(exists, "There should not have been an existing document"); }) ]), -#pragma warning disable CS0618 TestList("ExistsByField", [ TestCase("succeeds when documents exist", async () => @@ -299,7 +303,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EX("Sub")); + var exists = await conn.ExistsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EX("Sub")]); Expect.isTrue(exists, "There should have been existing documents"); }), TestCase("succeeds when documents do not exist", async () => @@ -308,11 +312,11 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EQ("NumValue", "six")); + var exists = + await conn.ExistsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "six")]); Expect.isFalse(exists, "There should not have been existing documents"); }) ]), -#pragma warning restore CS0618 TestList("ExistsByContains", [ TestCase("succeeds when documents exist", async () => @@ -377,6 +381,44 @@ public class PostgresCSharpExtensionTests Expect.isEmpty(results, "There should have been no documents returned"); }) ]), + TestList("FindAllOrdered", + [ + TestCase("succeeds when ordering numerically", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var results = + await conn.FindAllOrdered(PostgresDb.TableName, [Field.Named("n:NumValue")]); + Expect.hasLength(results, 5, "There should have been 5 documents returned"); + Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering numerically descending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var results = + await conn.FindAllOrdered(PostgresDb.TableName, [Field.Named("n:NumValue DESC")]); + Expect.hasLength(results, 5, "There should have been 5 documents returned"); + Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering alphabetically", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var results = await conn.FindAllOrdered(PostgresDb.TableName, [Field.Named("Id DESC")]); + Expect.hasLength(results, 5, "There should have been 5 documents returned"); + Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five", + "The documents were not ordered correctly"); + }) + ]), TestList("FindById", [ TestCase("succeeds when a document is found", async () => @@ -399,8 +441,7 @@ public class PostgresCSharpExtensionTests Expect.isNull(doc, "There should not have been a document returned"); }) ]), -#pragma warning disable CS0618 - TestList("FindByField", + TestList("FindByFields", [ TestCase("succeeds when documents are found", async () => { @@ -408,7 +449,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var docs = await conn.FindByField(PostgresDb.TableName, Field.EQ("Value", "another")); + var docs = await conn.FindByFields(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "another")]); Expect.equal(docs.Count, 1, "There should have been one document returned"); }), TestCase("succeeds when documents are not found", async () => @@ -417,11 +459,38 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var docs = await conn.FindByField(PostgresDb.TableName, Field.EQ("Value", "mauve")); + var docs = await conn.FindByFields(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "mauve")]); Expect.isEmpty(docs, "There should have been no documents returned"); }) ]), -#pragma warning restore CS0618 + TestList("FindByFieldsOrdered", + [ + TestCase("succeeds when documents are found", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var docs = await conn.FindByFieldsOrdered(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "purple")], [Field.Named("Id")]); + Expect.hasLength(docs, 2, "There should have been two document returned"); + Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when documents are not found", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var docs = await conn.FindByFieldsOrdered(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "purple")], [Field.Named("Id DESC")]); + Expect.hasLength(docs, 2, "There should have been two document returned"); + Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five", + "The documents were not ordered correctly"); + }) + ]), TestList("FindByContains", [ TestCase("succeeds when documents are found", async () => @@ -444,6 +513,34 @@ public class PostgresCSharpExtensionTests Expect.isEmpty(docs, "There should have been no documents returned"); }) ]), + TestList("FindByContainsOrdered", + [ + // Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red + TestCase("succeeds when sorting ascending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var docs = await conn.FindByContainsOrdered(PostgresDb.TableName, + new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar")]); + Expect.hasLength(docs, 2, "There should have been two documents returned"); + Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|four", + "Documents not ordered correctly"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var docs = await conn.FindByContainsOrdered(PostgresDb.TableName, + new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar DESC")]); + Expect.hasLength(docs, 2, "There should have been two documents returned"); + Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|two", + "Documents not ordered correctly"); + }) + ]), TestList("FindByJsonPath", [ TestCase("succeeds when documents are found", async () => @@ -465,8 +562,35 @@ public class PostgresCSharpExtensionTests Expect.isEmpty(docs, "There should have been no documents returned"); }) ]), -#pragma warning disable CS0618 - TestList("FindFirstByField", + TestList("FindByJsonPathOrdered", + [ + // Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4 + TestCase("succeeds when sorting ascending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var docs = await conn.FindByJsonPathOrdered(PostgresDb.TableName, "$.NumValue ? (@ < 15)", + [Field.Named("n:NumValue")]); + Expect.hasLength(docs, 3, "There should have been 3 documents returned"); + Expect.equal(string.Join('|', docs.Select(x => x.Id)), "one|three|two", + "Documents not ordered correctly"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var docs = await conn.FindByJsonPathOrdered(PostgresDb.TableName, "$.NumValue ? (@ < 15)", + [Field.Named("n:NumValue DESC")]); + Expect.hasLength(docs, 3, "There should have been 3 documents returned"); + Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|three|one", + "Documents not ordered correctly"); + }) + ]), + TestList("FindFirstByFields", [ TestCase("succeeds when a document is found", async () => { @@ -474,7 +598,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var doc = await conn.FindFirstByField(PostgresDb.TableName, Field.EQ("Value", "another")); + var doc = await conn.FindFirstByFields(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "another")]); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc.Id, "two", "The incorrect document was returned"); }), @@ -484,9 +609,10 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var doc = await conn.FindFirstByField(PostgresDb.TableName, Field.EQ("Value", "purple")); + var doc = await conn.FindFirstByFields(PostgresDb.TableName, FieldMatch.Any, + [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"); + Expect.contains(["five", "four"], doc.Id, "An incorrect document was returned"); }), TestCase("succeeds when a document is not found", async () => { @@ -494,11 +620,36 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - var doc = await conn.FindFirstByField(PostgresDb.TableName, Field.EQ("Value", "absent")); + var doc = await conn.FindFirstByFields(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "absent")]); Expect.isNull(doc, "There should not have been a document returned"); }) ]), -#pragma warning restore CS0618 + TestList("FindFirstByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var doc = await conn.FindFirstByFieldsOrdered(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "purple")], [Field.Named("Id")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("five", doc.Id, "An incorrect document was returned"); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var doc = await conn.FindFirstByFieldsOrdered(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("Value", "purple")], [Field.Named("Id DESC")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("four", doc.Id, "An incorrect document was returned"); + }) + ]), TestList("FindFirstByContains", [ TestCase("succeeds when a document is found", async () => @@ -520,7 +671,7 @@ public class PostgresCSharpExtensionTests var doc = await conn.FindFirstByContains(PostgresDb.TableName, new { Sub = new { Foo = "green" } }); Expect.isNotNull(doc, "There should have been a document returned"); - Expect.contains(new[] { "two", "four" }, doc.Id, "An incorrect document was returned"); + Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned"); }), TestCase("succeeds when a document is not found", async () => { @@ -532,6 +683,31 @@ public class PostgresCSharpExtensionTests Expect.isNull(doc, "There should not have been a document returned"); }) ]), + TestList("FindFirstByContainsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var doc = await conn.FindFirstByContainsOrdered(PostgresDb.TableName, + new { Sub = new { Foo = "green" } }, [Field.Named("Value")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("two", doc.Id, "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var doc = await conn.FindFirstByContainsOrdered(PostgresDb.TableName, + new { Sub = new { Foo = "green" } }, [Field.Named("Value DESC")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("four", doc.Id, "An incorrect document was returned"); + }) + ]), TestList("FindFirstByJsonPath", [ TestCase("succeeds when a document is found", async () => @@ -554,7 +730,7 @@ public class PostgresCSharpExtensionTests var doc = await conn.FindFirstByJsonPath(PostgresDb.TableName, "$.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"); + Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned"); }), TestCase("succeeds when a document is not found", async () => { @@ -566,6 +742,31 @@ public class PostgresCSharpExtensionTests Expect.isNull(doc, "There should not have been a document returned"); }) ]), + TestList("FindFirstByJsonPathOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var doc = await conn.FindFirstByJsonPathOrdered(PostgresDb.TableName, + "$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("two", doc.Id, "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = PostgresDb.BuildDb(); + await using var conn = MkConn(db); + await LoadDocs(); + + var doc = await conn.FindFirstByJsonPathOrdered(PostgresDb.TableName, + "$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar DESC")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("four", doc.Id, "An incorrect document was returned"); + }) + ]), TestList("UpdateById", [ TestCase("succeeds when a document is updated", async () => @@ -650,8 +851,7 @@ public class PostgresCSharpExtensionTests await conn.PatchById(PostgresDb.TableName, "test", new { Foo = "green" }); }) ]), -#pragma warning disable CS0618 - TestList("PatchByField", + TestList("PatchByFields", [ TestCase("succeeds when a document is updated", async () => { @@ -659,8 +859,10 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); - var after = await conn.CountByField(PostgresDb.TableName, Field.EQ("NumValue", "77")); + await conn.PatchByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")], + new { NumValue = 77 }); + var after = await conn.CountByFields(PostgresDb.TableName, FieldMatch.Any, + [Field.EQ("NumValue", "77")]); Expect.equal(after, 2, "There should have been 2 documents returned"); }), TestCase("succeeds when no document is updated", async () => @@ -671,10 +873,10 @@ 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, Field.EQ("Value", "burgundy"), new { Foo = "green" }); + await conn.PatchByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")], + new { Foo = "green" }); }) ]), -#pragma warning restore CS0618 TestList("PatchByContains", [ TestCase("succeeds when a document is updated", async () => @@ -729,7 +931,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub", "Value" }); + 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"); @@ -741,7 +943,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub" }); + 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"); @@ -754,7 +956,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" }); + await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["AFieldThatIsNotThere"]); }), TestCase("succeeds when no document is matched", async () => { @@ -762,11 +964,10 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); // This not raising an exception is the test - await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Value" }); + await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["Value"]); }) ]), -#pragma warning disable CS0618 - TestList("RemoveFieldsByField", + TestList("RemoveFieldsByFields", [ TestCase("succeeds when multiple fields are removed", async () => { @@ -774,8 +975,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), - new[] { "Sub", "Value" }); + await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [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"); @@ -787,7 +988,8 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" }); + await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [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"); @@ -800,7 +1002,8 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" }); + await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")], + ["Nothing"]); }), TestCase("succeeds when no document is matched", async () => { @@ -808,11 +1011,10 @@ 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"), - new[] { "Value" }); + await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, + [Field.NE("Abracadabra", "apple")], ["Value"]); }) ]), -#pragma warning restore CS0618 TestList("RemoveFieldsByContains", [ TestCase("succeeds when multiple fields are removed", async () => @@ -821,8 +1023,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, - new[] { "Sub", "Value" }); + 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"); @@ -834,7 +1035,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" }); + 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"); @@ -847,7 +1048,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" }); + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Nothing"]); }), TestCase("succeeds when no document is matched", async () => { @@ -855,8 +1056,7 @@ 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" }, - new[] { "Value" }); + await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, ["Value"]); }) ]), TestList("RemoveFieldsByJsonPath", @@ -867,8 +1067,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", - new[] { "Sub", "Value" }); + 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"); @@ -880,7 +1079,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" }); + 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"); @@ -893,7 +1092,7 @@ public class PostgresCSharpExtensionTests await LoadDocs(); // This not raising an exception is the test - await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" }); + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Nothing"]); }), TestCase("succeeds when no document is matched", async () => { @@ -901,8 +1100,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); // This not raising an exception is the test - await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", - new[] { "Value" }); + await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", ["Value"]); }) ]), TestList("DeleteById", @@ -928,8 +1126,7 @@ public class PostgresCSharpExtensionTests Expect.equal(remaining, 5, "There should have been 5 documents remaining"); }) ]), -#pragma warning disable CS0618 - TestList("DeleteByField", + TestList("DeleteByFields", [ TestCase("succeeds when documents are deleted", async () => { @@ -937,7 +1134,7 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.DeleteByField(PostgresDb.TableName, Field.NE("Value", "purple")); + await conn.DeleteByFields(PostgresDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]); var remaining = await conn.CountAll(PostgresDb.TableName); Expect.equal(remaining, 2, "There should have been 2 documents remaining"); }), @@ -947,12 +1144,11 @@ public class PostgresCSharpExtensionTests await using var conn = MkConn(db); await LoadDocs(); - await conn.DeleteByField(PostgresDb.TableName, Field.EQ("Value", "crimson")); + await conn.DeleteByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "crimson")]); var remaining = await conn.CountAll(PostgresDb.TableName); Expect.equal(remaining, 5, "There should have been 5 documents remaining"); }) ]), -#pragma warning restore CS0618 TestList("DeleteByContains", [ TestCase("succeeds when documents are deleted", async () => diff --git a/src/Tests.CSharp/PostgresCSharpTests.cs b/src/Tests.CSharp/PostgresCSharpTests.cs index 75335ae..93c3b4d 100644 --- a/src/Tests.CSharp/PostgresCSharpTests.cs +++ b/src/Tests.CSharp/PostgresCSharpTests.cs @@ -529,7 +529,7 @@ public static class PostgresCSharpTests Expect.isFalse(exists, "The index should not exist already"); await Definition.EnsureTable("ensured"); - await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); + await Definition.EnsureFieldIndex("ensured", "test", ["Id", "Category"]); exists = await IndexExists(); Expect.isTrue(exists, "The index should now exist"); return; @@ -1043,6 +1043,29 @@ public static class PostgresCSharpTests Expect.isNull(doc, "There should not have been a document returned"); }) ]), + TestList("FirstByContainsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + var doc = await Find.FirstByContainsOrdered(PostgresDb.TableName, + new { Sub = new { Foo = "green" } }, [Field.Named("Value")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("two", doc.Id, "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + var doc = await Find.FirstByContainsOrdered(PostgresDb.TableName, + new { Sub = new { Foo = "green" } }, [Field.Named("Value DESC")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("four", doc.Id, "An incorrect document was returned"); + }) + ]), TestList("FirstByJsonPath", [ TestCase("succeeds when a document is found", async () => @@ -1073,6 +1096,29 @@ public static class PostgresCSharpTests var doc = await Find.FirstByJsonPath(PostgresDb.TableName, "$.Id ? (@ == \"nope\")"); Expect.isNull(doc, "There should not have been a document returned"); }) + ]), + TestList("FirstByJsonPathOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + var doc = await Find.FirstByJsonPathOrdered(PostgresDb.TableName, + "$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("two", doc.Id, "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = PostgresDb.BuildDb(); + await LoadDocs(); + + var doc = await Find.FirstByJsonPathOrdered(PostgresDb.TableName, + "$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar DESC")]); + Expect.isNotNull(doc, "There should have been a document returned"); + Expect.equal("four", doc.Id, "An incorrect document was returned"); + }) ]) ]); @@ -1507,11 +1553,7 @@ public static class PostgresCSharpTests [Tests] public static readonly Test All = TestList("Postgres.C#", [ - TestList("Unit", - [ - ParametersTests, - QueryTests - ]), + TestList("Unit", [ParametersTests, QueryTests]), TestSequenced(TestList("Integration", [ ConfigurationTests, diff --git a/src/Tests/PostgresExtensionTests.fs b/src/Tests/PostgresExtensionTests.fs index b895289..533cf12 100644 --- a/src/Tests/PostgresExtensionTests.fs +++ b/src/Tests/PostgresExtensionTests.fs @@ -7,8 +7,6 @@ open Expecto open Npgsql open Types -#nowarn "0044" - /// Open a connection to the throwaway database let private mkConn (db: ThrowawayPostgresDb) = let conn = new NpgsqlConnection(db.ConnectionString) @@ -27,7 +25,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! docs = conn.customList (Query.selectFromTable PostgresDb.TableName) [] fromData + let! docs = conn.customList (Query.find PostgresDb.TableName) [] fromData Expect.equal (List.length docs) 5 "There should have been 5 documents returned" } testTask "succeeds when data is not found" { @@ -211,12 +209,12 @@ let integrationTests = let! theCount = conn.countAll PostgresDb.TableName Expect.equal theCount 5 "There should have been 5 matching documents" } - testTask "countByField succeeds" { + testTask "countByFields succeeds" { use db = PostgresDb.BuildDb() use conn = mkConn db do! loadDocs conn - let! theCount = conn.countByField PostgresDb.TableName (Field.EQ "Value" "purple") + let! theCount = conn.countByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] Expect.equal theCount 2 "There should have been 2 matching documents" } testTask "countByContains succeeds" { @@ -253,13 +251,13 @@ let integrationTests = Expect.isFalse exists "There should not have been an existing document" } ] - testList "existsByField" [ + testList "existsByFields" [ testTask "succeeds when documents exist" { use db = PostgresDb.BuildDb() use conn = mkConn db do! loadDocs conn - let! exists = conn.existsByField PostgresDb.TableName (Field.EX "Sub") + let! exists = conn.existsByFields PostgresDb.TableName Any [ Field.EX "Sub" ] Expect.isTrue exists "There should have been existing documents" } testTask "succeeds when documents do not exist" { @@ -267,7 +265,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! exists = conn.existsByField PostgresDb.TableName (Field.EQ "NumValue" "six") + let! exists = conn.existsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "six" ] Expect.isFalse exists "There should not have been existing documents" } ] @@ -317,12 +315,10 @@ let integrationTests = do! conn.insert PostgresDb.TableName { Foo = "five"; Bar = "six" } let! results = conn.findAll PostgresDb.TableName - let expected = [ - { Foo = "one"; Bar = "two" } - { Foo = "three"; Bar = "four" } - { Foo = "five"; Bar = "six" } - ] - Expect.equal results expected "There should have been 3 documents returned" + Expect.equal + results + [ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ] + "There should have been 3 documents returned" } testTask "succeeds when there is no data" { use db = PostgresDb.BuildDb() @@ -331,6 +327,44 @@ let integrationTests = Expect.equal results [] "There should have been no documents returned" } ] + testList "findAllOrdered" [ + testTask "succeeds when ordering numerically" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! results = conn.findAllOrdered PostgresDb.TableName [ Field.Named "n:NumValue" ] + Expect.hasLength results 5 "There should have been 5 documents returned" + Expect.equal + (results |> List.map _.Id |> String.concat "|") + "one|three|two|four|five" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering numerically descending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! results = conn.findAllOrdered PostgresDb.TableName [ Field.Named "n:NumValue DESC" ] + Expect.hasLength results 5 "There should have been 5 documents returned" + Expect.equal + (results |> List.map _.Id |> String.concat "|") + "five|four|two|three|one" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering alphabetically" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! results = conn.findAllOrdered PostgresDb.TableName [ Field.Named "Id DESC" ] + Expect.hasLength results 5 "There should have been 5 documents returned" + Expect.equal + (results |> List.map _.Id |> String.concat "|") + "two|three|one|four|five" + "The documents were not ordered correctly" + } + ] testList "findById" [ testTask "succeeds when a document is found" { use db = PostgresDb.BuildDb() @@ -350,13 +384,13 @@ let integrationTests = Expect.isNone doc "There should not have been a document returned" } ] - testList "findByField" [ + testList "findByFields" [ testTask "succeeds when documents are found" { use db = PostgresDb.BuildDb() use conn = mkConn db do! loadDocs conn - let! docs = conn.findByField PostgresDb.TableName (Field.EQ "Value" "another") + let! docs = conn.findByFields PostgresDb.TableName Any [ Field.EQ "Value" "another" ] Expect.equal (List.length docs) 1 "There should have been one document returned" } testTask "succeeds when documents are not found" { @@ -364,10 +398,36 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! docs = conn.findByField PostgresDb.TableName (Field.EQ "Value" "mauve") + let! docs = conn.findByFields PostgresDb.TableName Any [ Field.EQ "Value" "mauve" ] Expect.isEmpty docs "There should have been no documents returned" } ] + testList "findByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! docs = + conn.findByFieldsOrdered + PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ] + Expect.hasLength docs 2 "There should have been two documents returned" + Expect.equal + (docs |> List.map _.Id |> String.concat "|") "five|four" "Documents not ordered correctly" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! docs = + conn.findByFieldsOrdered + PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ] + Expect.hasLength docs 2 "There should have been two documents returned" + Expect.equal + (docs |> List.map _.Id |> String.concat "|") "four|five" "Documents not ordered correctly" + } + ] testList "findByContains" [ testTask "succeeds when documents are found" { use db = PostgresDb.BuildDb() @@ -386,6 +446,33 @@ let integrationTests = Expect.isEmpty docs "There should have been no documents returned" } ] + testList "findByContainsOrdered" [ + // Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! docs = + conn.findByContainsOrdered + PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar" ] + Expect.hasLength docs 2 "There should have been two documents returned" + Expect.equal + (docs |> List.map _.Id |> String.concat "|") "two|four" "Documents not ordered correctly" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! docs = + conn.findByContainsOrdered + PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar DESC" ] + Expect.hasLength docs 2 "There should have been two documents returned" + Expect.equal + (docs |> List.map _.Id |> String.concat "|") "four|two" "Documents not ordered correctly" + } + ] testList "findByJsonPath" [ testTask "succeeds when documents are found" { use db = PostgresDb.BuildDb() @@ -404,13 +491,40 @@ let integrationTests = Expect.isEmpty docs "There should have been no documents returned" } ] - testList "findFirstByField" [ + testList "findByJsonPathOrdered" [ + // Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4 + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! docs = + conn.findByJsonPathOrdered + PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue" ] + Expect.hasLength docs 3 "There should have been 3 documents returned" + Expect.equal + (docs |> List.map _.Id |> String.concat "|") "one|three|two" "Documents not ordered correctly" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! docs = + conn.findByJsonPathOrdered + PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue DESC" ] + Expect.hasLength docs 3 "There should have been 3 documents returned" + Expect.equal + (docs |> List.map _.Id |> String.concat "|") "two|three|one" "Documents not ordered correctly" + } + ] + testList "findFirstByFields" [ testTask "succeeds when a document is found" { use db = PostgresDb.BuildDb() use conn = mkConn db do! loadDocs conn - let! doc = conn.findFirstByField PostgresDb.TableName (Field.EQ "Value" "another") + let! doc = conn.findFirstByFields PostgresDb.TableName Any [ 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" } @@ -419,7 +533,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! doc = conn.findFirstByField PostgresDb.TableName (Field.EQ "Value" "purple") + let! doc = conn.findFirstByFields PostgresDb.TableName Any [ 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" } @@ -428,10 +542,34 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - let! doc = conn.findFirstByField PostgresDb.TableName (Field.EQ "Value" "absent") + let! doc = conn.findFirstByFields PostgresDb.TableName Any [ Field.EQ "Value" "absent" ] Expect.isNone doc "There should not have been a document returned" } ] + testList "findFirstByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! doc = + conn.findFirstByFieldsOrdered + PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "five" doc.Value.Id "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! doc = + conn.findFirstByFieldsOrdered + PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "four" doc.Value.Id "An incorrect document was returned" + } + ] testList "findFirstByContains" [ testTask "succeeds when a document is found" { use db = PostgresDb.BuildDb() @@ -460,6 +598,30 @@ let integrationTests = Expect.isNone doc "There should not have been a document returned" } ] + testList "findFirstByContainsOrdered" [ + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! doc = + conn.findFirstByContainsOrdered + PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "two" doc.Value.Id "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! doc = + conn.findFirstByContainsOrdered + PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value DESC" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "four" doc.Value.Id "An incorrect document was returned" + } + ] testList "findFirstByJsonPath" [ testTask "succeeds when a document is found" { use db = PostgresDb.BuildDb() @@ -488,6 +650,30 @@ let integrationTests = Expect.isNone doc "There should not have been a document returned" } ] + testList "findFirstByJsonPathOrdered" [ + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! doc = + conn.findFirstByJsonPathOrdered + PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "two" doc.Value.Id "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + use conn = mkConn db + do! loadDocs conn + + let! doc = + conn.findFirstByJsonPathOrdered + PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar DESC" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "four" doc.Value.Id "An incorrect document was returned" + } + ] testList "updateById" [ testTask "succeeds when a document is updated" { use db = PostgresDb.BuildDb() @@ -558,14 +744,14 @@ let integrationTests = do! conn.patchById PostgresDb.TableName "test" {| Foo = "green" |} } ] - testList "patchByField" [ + testList "patchByFields" [ testTask "succeeds when a document is updated" { use db = PostgresDb.BuildDb() use conn = mkConn db do! loadDocs conn - do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} - let! after = conn.countByField PostgresDb.TableName (Field.EQ "NumValue" "77") + do! conn.patchByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |} + let! after = conn.countByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "77" ] Expect.equal after 2 "There should have been 2 documents returned" } testTask "succeeds when no document is updated" { @@ -575,7 +761,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 (Field.EQ "Value" "burgundy") {| Foo = "green" |} + do! conn.patchByFields PostgresDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |} } ] testList "patchByContains" [ @@ -625,9 +811,9 @@ let integrationTests = do! loadDocs conn do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub"; "Value" ] - let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 1 "There should be 1 document without Value fields" } testTask "succeeds when a single field is removed" { @@ -636,9 +822,9 @@ let integrationTests = do! loadDocs conn do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub" ] - let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 0 "There should be no documents without Value fields" } testTask "succeeds when a field is not removed" { @@ -657,16 +843,16 @@ let integrationTests = do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ] } ] - testList "removeFieldsByField" [ + testList "removeFieldsByFields" [ 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") + do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub"; "Value" ] + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 1 "There should be 1 document without Value fields" } testTask "succeeds when a single field is removed" { @@ -674,10 +860,10 @@ let integrationTests = 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") + do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub" ] + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 0 "There should be no documents without Value fields" } testTask "succeeds when a field is not removed" { @@ -686,14 +872,14 @@ let integrationTests = do! loadDocs conn // This not raising an exception is the test - do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ] + do! conn.removeFieldsByFields PostgresDb.TableName Any [ 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" ] + do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ] } ] testList "removeFieldsByContains" [ @@ -703,9 +889,9 @@ let integrationTests = do! loadDocs conn do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ] - let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 1 "There should be 1 document without Value fields" } testTask "succeeds when a single field is removed" { @@ -714,9 +900,9 @@ let integrationTests = do! loadDocs conn do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ] - let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 0 "There should be no documents without Value fields" } testTask "succeeds when a field is not removed" { @@ -742,9 +928,9 @@ let integrationTests = do! loadDocs conn do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ] - let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 1 "There should be 1 document without Value fields" } testTask "succeeds when a single field is removed" { @@ -753,9 +939,9 @@ let integrationTests = do! loadDocs conn do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ] - let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub") + let! noSubs = conn.countByFields PostgresDb.TableName Any [ 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") + let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ] Expect.equal noValue 0 "There should be no documents without Value fields" } testTask "succeeds when a field is not removed" { @@ -794,13 +980,13 @@ let integrationTests = Expect.equal remaining 5 "There should have been 5 documents remaining" } ] - testList "deleteByField" [ + testList "deleteByFields" [ testTask "succeeds when documents are deleted" { use db = PostgresDb.BuildDb() use conn = mkConn db do! loadDocs conn - do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "purple") + do! conn.deleteByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] let! remaining = conn.countAll PostgresDb.TableName Expect.equal remaining 3 "There should have been 3 documents remaining" } @@ -809,7 +995,7 @@ let integrationTests = use conn = mkConn db do! loadDocs conn - do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "crimson") + do! conn.deleteByFields PostgresDb.TableName Any [ 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 1c04aaa..676c8ef 100644 --- a/src/Tests/PostgresTests.fs +++ b/src/Tests/PostgresTests.fs @@ -307,7 +307,7 @@ let customTests = testList "Custom" [ use db = PostgresDb.BuildDb() do! loadDocs () - let! docs = Custom.list (Query.selectFromTable PostgresDb.TableName) [] fromData + let! docs = Custom.list (Query.find PostgresDb.TableName) [] fromData Expect.hasLength docs 5 "There should have been 5 documents returned" } testTask "succeeds when data is not found" { @@ -859,6 +859,28 @@ let findTests = testList "Find" [ Expect.isNone doc "There should not have been a document returned" } ] + testList "firstByContainsOrdered" [ + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + let! doc = + Find.firstByContainsOrdered + PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "two" doc.Value.Id "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + let! doc = + Find.firstByContainsOrdered + PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value DESC" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "four" doc.Value.Id "An incorrect document was returned" + } + ] testList "firstByJsonPath" [ testTask "succeeds when a document is found" { use db = PostgresDb.BuildDb() @@ -884,9 +906,31 @@ let findTests = testList "Find" [ Expect.isNone doc "There should not have been a document returned" } ] + testList "firstByJsonPathOrdered" [ + testTask "succeeds when sorting ascending" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + let! doc = + Find.firstByJsonPathOrdered + PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "two" doc.Value.Id "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use db = PostgresDb.BuildDb() + do! loadDocs () + + let! doc = + Find.firstByJsonPathOrdered + PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar DESC" ] + Expect.isSome doc "There should have been a document returned" + Expect.equal "four" doc.Value.Id "An incorrect document was returned" + } + ] ] -/// Integration tests for the Update module of the PostegreSQL library +/// Integration tests for the Update module of the PostgreSQL library let updateTests = testList "Update" [ testList "byId" [ testTask "succeeds when a document is updated" {