Final tweaks for v4 (#9)
- Add .NET 9, PostgreSQL 17 support - Drop .NET 6, PostgreSQL 12 support - Finalize READMEs Reviewed-on: #9
This commit was merged in pull request #9.
This commit is contained in:
@@ -14,8 +14,8 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.6" />
|
||||
<PackageReference Update="FSharp.Core" Version="8.0.300" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
|
||||
<PackageReference Update="FSharp.Core" Version="9.0.100" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -71,7 +71,7 @@ module WithConn =
|
||||
|
||||
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
|
||||
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
|
||||
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
|
||||
let FirstByField<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, field, conn) =
|
||||
WithConn.Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field, conn)
|
||||
|
||||
[<RequireQualifiedAccess>]
|
||||
@@ -144,7 +144,7 @@ module Find =
|
||||
|
||||
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
|
||||
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
|
||||
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
|
||||
let FirstByField<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, field) =
|
||||
Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field)
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ type SqliteConnectionCSharpCompatExtensions =
|
||||
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
|
||||
[<Extension>]
|
||||
[<System.Obsolete "Use FindFirstByFields instead ~ will be removed in v4.1">]
|
||||
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
|
||||
static member inline FindFirstByField<'TDoc when 'TDoc: null and 'TDoc: not struct>(conn, tableName, field) =
|
||||
WithConn.Find.FirstByFields<'TDoc>(tableName, Any, [ field ], conn)
|
||||
|
||||
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
namespace BitBadger.Documents.Sqlite
|
||||
|
||||
open BitBadger.Documents
|
||||
open Microsoft.Data.Sqlite
|
||||
|
||||
/// F# extensions for the SqliteConnection type
|
||||
@@ -131,7 +130,7 @@ type SqliteConnectionCSharpExtensions =
|
||||
|
||||
/// Execute a query that returns one or no results
|
||||
[<Extension>]
|
||||
static member inline CustomSingle<'TDoc when 'TDoc: null>(
|
||||
static member inline CustomSingle<'TDoc when 'TDoc: null and 'TDoc: not struct>(
|
||||
conn, query, parameters, mapFunc: System.Func<SqliteDataReader, 'TDoc>) =
|
||||
WithConn.Custom.Single<'TDoc>(query, parameters, mapFunc, conn)
|
||||
|
||||
@@ -198,7 +197,7 @@ type SqliteConnectionCSharpExtensions =
|
||||
|
||||
/// Retrieve a document by its ID
|
||||
[<Extension>]
|
||||
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
|
||||
static member inline FindById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(conn, tableName, docId: 'TKey) =
|
||||
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields
|
||||
@@ -213,13 +212,14 @@ type SqliteConnectionCSharpExtensions =
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||
[<Extension>]
|
||||
static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) =
|
||||
static member inline FindFirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>(
|
||||
conn, tableName, howMatched, fields) =
|
||||
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
|
||||
/// the first result
|
||||
[<Extension>]
|
||||
static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null>(
|
||||
static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(
|
||||
conn, tableName, howMatched, queryFields, orderFields) =
|
||||
WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
|
||||
|
||||
@@ -262,9 +262,3 @@ type SqliteConnectionCSharpExtensions =
|
||||
[<Extension>]
|
||||
static member inline DeleteByFields(conn, tableName, howMatched, fields) =
|
||||
WithConn.Delete.byFields tableName howMatched fields conn
|
||||
|
||||
/// Delete documents by matching a comparison on a JSON field
|
||||
[<Extension>]
|
||||
[<System.Obsolete "Use DeleteByFields instead; will be removed in v4">]
|
||||
static member inline DeleteByField(conn, tableName, field) =
|
||||
conn.DeleteByFields(tableName, Any, [ field ])
|
||||
|
||||
@@ -153,7 +153,7 @@ module Results =
|
||||
/// Create a domain item from a document, specifying the field in which the document is found
|
||||
[<CompiledName "FromDocument">]
|
||||
let fromDocument<'TDoc> field (rdr: SqliteDataReader) : 'TDoc =
|
||||
Configuration.serializer().Deserialize<'TDoc>(rdr.GetString(rdr.GetOrdinal(field)))
|
||||
Configuration.serializer().Deserialize<'TDoc>(rdr.GetString(rdr.GetOrdinal field))
|
||||
|
||||
/// Create a domain item from a document
|
||||
[<CompiledName "FromData">]
|
||||
@@ -221,7 +221,7 @@ module WithConn =
|
||||
}
|
||||
|
||||
/// Execute a query that returns one or no results (returns null if not found)
|
||||
let Single<'TDoc when 'TDoc: null>(
|
||||
let Single<'TDoc when 'TDoc: null and 'TDoc: not struct>(
|
||||
query, parameters, mapFunc: System.Func<SqliteDataReader, 'TDoc>, conn
|
||||
) = backgroundTask {
|
||||
let! result = single<'TDoc> query parameters mapFunc.Invoke conn
|
||||
@@ -358,7 +358,7 @@ module WithConn =
|
||||
Custom.single<'TDoc> (Query.byId (Query.find tableName) docId) [ idParam docId ] fromData<'TDoc> conn
|
||||
|
||||
/// Retrieve a document by its ID (returns null if not found)
|
||||
let ById<'TKey, 'TDoc when 'TDoc: null>(tableName, docId: 'TKey, conn) =
|
||||
let ById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, docId: 'TKey, conn) =
|
||||
Custom.Single<'TDoc>(Query.byId (Query.find tableName) docId, [ idParam docId ], fromData<'TDoc>, conn)
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields
|
||||
@@ -405,7 +405,7 @@ module WithConn =
|
||||
conn
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, conn) =
|
||||
let FirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, howMatched, fields, conn) =
|
||||
Custom.Single(
|
||||
$"{Query.byFields (Query.find tableName) howMatched fields} LIMIT 1",
|
||||
addFieldParams fields [],
|
||||
@@ -424,7 +424,8 @@ module WithConn =
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
|
||||
/// only the first result
|
||||
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields, conn) =
|
||||
let FirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(
|
||||
tableName, howMatched, queryFields, orderFields, conn) =
|
||||
Custom.Single(
|
||||
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields SQLite} LIMIT 1",
|
||||
addFieldParams queryFields [],
|
||||
@@ -529,7 +530,8 @@ module Custom =
|
||||
WithConn.Custom.single<'TDoc> query parameters mapFunc conn
|
||||
|
||||
/// Execute a query that returns one or no results (returns null if not found)
|
||||
let Single<'TDoc when 'TDoc: null>(query, parameters, mapFunc: System.Func<SqliteDataReader, 'TDoc>) =
|
||||
let Single<'TDoc when 'TDoc: null and 'TDoc: not struct>(
|
||||
query, parameters, mapFunc: System.Func<SqliteDataReader, 'TDoc>) =
|
||||
use conn = Configuration.dbConn ()
|
||||
WithConn.Custom.Single<'TDoc>(query, parameters, mapFunc, conn)
|
||||
|
||||
@@ -652,7 +654,7 @@ module Find =
|
||||
WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
|
||||
|
||||
/// Retrieve a document by its ID (returns null if not found)
|
||||
let ById<'TKey, 'TDoc when 'TDoc: null>(tableName, docId) =
|
||||
let ById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, docId) =
|
||||
use conn = Configuration.dbConn ()
|
||||
WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn)
|
||||
|
||||
@@ -685,7 +687,7 @@ module Find =
|
||||
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) =
|
||||
let FirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>(tableName, howMatched, fields) =
|
||||
use conn = Configuration.dbConn ()
|
||||
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
|
||||
|
||||
@@ -698,7 +700,8 @@ module Find =
|
||||
|
||||
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
|
||||
/// the first result
|
||||
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields) =
|
||||
let FirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>(
|
||||
tableName, howMatched, queryFields, orderFields) =
|
||||
use conn = Configuration.dbConn ()
|
||||
WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
|
||||
|
||||
|
||||
@@ -5,11 +5,16 @@ This package provides a lightweight document library backed by [SQLite](https://
|
||||
## Features
|
||||
|
||||
- Select, insert, update, save (upsert), delete, count, and check existence of documents, and create tables and indexes for these documents
|
||||
- Automatically generate IDs for documents (numeric IDs, GUIDs, or random strings)
|
||||
- Address documents via ID or via comparison on any field
|
||||
- Access documents as your domain models (<abbr title="Plain Old CLR Objects">POCO</abbr>s)
|
||||
- Use `Task`-based async for all data access functions
|
||||
- Use building blocks for more complex queries
|
||||
|
||||
## Upgrading from v3
|
||||
|
||||
There is a breaking API change for `ByField` (C#) / `byField` (F#), along with a compatibility namespace that can mitigate the impact of these changes. See [the migration guide](https://bitbadger.solutions/open-source/relational-documents/upgrade-from-v3-to-v4.html) for full details.
|
||||
|
||||
## Getting Started
|
||||
|
||||
Once the package is installed, the library needs a connection string. Once it has been obtained / constructed, provide it to the library:
|
||||
@@ -72,28 +77,28 @@ Count customers in Atlanta:
|
||||
|
||||
```csharp
|
||||
// C#; parameters are table name, field, operator, and value
|
||||
// Count.ByField type signature is Func<string, Field, Task<long>>
|
||||
var customerCount = await Count.ByField("customer", Field.Equal("City", "Atlanta"));
|
||||
// Count.ByFields type signature is Func<string, FieldMatch, IEnumerable<Field>, Task<long>>
|
||||
var customerCount = await Count.ByFields("customer", FieldMatch.Any, [Field.Equal("City", "Atlanta")]);
|
||||
```
|
||||
|
||||
```fsharp
|
||||
// F#
|
||||
// Count.byField type signature is string -> Field -> Task<int64>
|
||||
let! customerCount = Count.byField "customer" (Field.Equal "City" "Atlanta")
|
||||
// Count.byFields type signature is string -> FieldMatch -> Field seq -> Task<int64>
|
||||
let! customerCount = Count.byFields "customer" Any [ Field.Equal "City" "Atlanta" ]
|
||||
```
|
||||
|
||||
Delete customers in Chicago: _(no offense, Second City; just an example...)_
|
||||
|
||||
```csharp
|
||||
// C#; parameters are same as above, except return is void
|
||||
// Delete.ByField type signature is Func<string, Field, Task>
|
||||
await Delete.ByField("customer", Field.Equal("City", "Chicago"));
|
||||
// Delete.ByFields type signature is Func<string, FieldMatch, IEnumerable<Field>, Task>
|
||||
await Delete.ByFields("customer", FieldMatch.Any, [Field.Equal("City", "Chicago")]);
|
||||
```
|
||||
|
||||
```fsharp
|
||||
// F#
|
||||
// Delete.byField type signature is string -> string -> Op -> obj -> Task<unit>
|
||||
do! Delete.byField "customer" (Field.Equal "City" "Chicago")
|
||||
// Delete.byFields type signature is string -> FieldMatch -> Field seq -> Task<unit>
|
||||
do! Delete.byFields "customer" Any [ Field.Equal "City" "Chicago" ]
|
||||
```
|
||||
|
||||
## More Information
|
||||
|
||||
Reference in New Issue
Block a user