Tweaks prior to release

This commit is contained in:
Daniel J. Summers 2024-12-16 22:44:59 -05:00
parent 5bb9eec4a8
commit 3b7176005c
9 changed files with 54 additions and 46 deletions

View File

@ -4,26 +4,37 @@ open System.Security.Cryptography
/// The types of comparisons available for JSON fields /// The types of comparisons available for JSON fields
type Comparison = type Comparison =
/// Equals (=) /// Equals (=)
| Equal of Value: obj | Equal of Value: obj
/// Greater Than (>) /// Greater Than (>)
| Greater of Value: obj | Greater of Value: obj
/// Greater Than or Equal To (>=) /// Greater Than or Equal To (>=)
| GreaterOrEqual of Value: obj | GreaterOrEqual of Value: obj
/// Less Than (<) /// Less Than (<)
| Less of Value: obj | Less of Value: obj
/// Less Than or Equal To (<=) /// Less Than or Equal To (<=)
| LessOrEqual of Value: obj | LessOrEqual of Value: obj
/// Not Equal to (<>) /// Not Equal to (<>)
| NotEqual of Value: obj | NotEqual of Value: obj
/// Between (BETWEEN) /// Between (BETWEEN)
| Between of Min: obj * Max: obj | Between of Min: obj * Max: obj
/// In (IN) /// In (IN)
| In of Values: obj seq | In of Values: obj seq
/// In Array (PostgreSQL: |?, SQLite: EXISTS / json_each / IN) /// In Array (PostgreSQL: |?, SQLite: EXISTS / json_each / IN)
| InArray of Table: string * Values: obj seq | InArray of Table: string * Values: obj seq
/// Exists (IS NOT NULL) /// Exists (IS NOT NULL)
| Exists | Exists
/// Does Not Exist (IS NULL) /// Does Not Exist (IS NULL)
| NotExists | NotExists
@ -53,15 +64,18 @@ type Dialect =
/// The format in which an element of a JSON field should be extracted /// The format in which an element of a JSON field should be extracted
[<Struct>] [<Struct>]
type FieldFormat = type FieldFormat =
/// Use ->> or #>>; extracts a text (PostgreSQL) or SQL (SQLite) value /// Use ->> or #>>; extracts a text (PostgreSQL) or SQL (SQLite) value
| AsSql | AsSql
/// Use -> or #>; extracts a JSONB (PostgreSQL) or JSON (SQLite) value /// Use -> or #>; extracts a JSONB (PostgreSQL) or JSON (SQLite) value
| AsJson | AsJson
/// Criteria for a field WHERE clause /// Criteria for a field WHERE clause
type Field = type Field = {
{ /// The name of the field
/// The name of the field
Name: string Name: string
/// The comparison for the field /// The comparison for the field
@ -71,8 +85,8 @@ type Field =
ParameterName: string option ParameterName: string option
/// The table qualifier for this field /// The table qualifier for this field
Qualifier: string option } Qualifier: string option
with } with
/// Create a comparison against a field /// Create a comparison against a field
static member Where name (comparison: Comparison) = static member Where name (comparison: Comparison) =
@ -190,8 +204,10 @@ with
/// How fields should be matched /// How fields should be matched
[<Struct>] [<Struct>]
type FieldMatch = type FieldMatch =
/// Any field matches (OR) /// Any field matches (OR)
| Any | Any
/// All fields match (AND) /// All fields match (AND)
| All | All
@ -202,6 +218,7 @@ type FieldMatch =
/// Derive parameter names (each instance wraps a counter to uniquely name anonymous fields) /// Derive parameter names (each instance wraps a counter to uniquely name anonymous fields)
type ParameterName() = type ParameterName() =
/// The counter for the next field value /// The counter for the next field value
let mutable currentIdx = -1 let mutable currentIdx = -1
@ -213,19 +230,20 @@ type ParameterName() =
currentIdx <- currentIdx + 1 currentIdx <- currentIdx + 1
$"@field{currentIdx}" $"@field{currentIdx}"
#if NET6_0
open System.Text
#endif
/// Automatically-generated document ID strategies /// Automatically-generated document ID strategies
[<Struct>] [<Struct>]
type AutoId = type AutoId =
/// No automatic IDs will be generated /// No automatic IDs will be generated
| Disabled | Disabled
/// Generate a MAX-plus-1 numeric value for documents /// Generate a MAX-plus-1 numeric value for documents
| Number | Number
/// Generate a GUID for each document (as a lowercase, no-dashes, 32-character string) /// Generate a GUID for each document (as a lowercase, no-dashes, 32-character string)
| Guid | Guid
/// Generate a random string of hexadecimal characters for each document /// Generate a random string of hexadecimal characters for each document
| RandomString | RandomString
with with
@ -235,13 +253,7 @@ with
/// Generate a string of random hexadecimal characters /// Generate a string of random hexadecimal characters
static member GenerateRandomString(length: int) = static member GenerateRandomString(length: int) =
#if NET8_0_OR_GREATER
RandomNumberGenerator.GetHexString(length, lowercase = true) RandomNumberGenerator.GetHexString(length, lowercase = true)
#else
RandomNumberGenerator.GetBytes((length / 2) + 1)
|> Array.fold (fun (str: StringBuilder) byt -> str.Append(byt.ToString "x2")) (StringBuilder length)
|> function it -> it.Length <- length; it.ToString()
#endif
/// Does the given document need an automatic ID generated? /// Does the given document need an automatic ID generated?
static member NeedsAutoId<'T> strategy (document: 'T) idProp = static member NeedsAutoId<'T> strategy (document: 'T) idProp =

View File

@ -7,6 +7,7 @@ This package provides common definitions and functionality for `BitBadger.Docume
## Features ## Features
- Select, insert, update, save (upsert), delete, count, and check existence of documents, and create tables and indexes for these documents - 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)
- Addresses documents via ID and via comparison on any field (for PostgreSQL, also via equality on any property by using JSON containment, or via condition on any property using JSON Path queries) - Addresses documents via ID and via comparison on any field (for PostgreSQL, also via equality on any property by using JSON containment, or via condition on any property using JSON Path queries)
- Accesses documents as your domain models (<abbr title="Plain Old CLR Objects">POCO</abbr>s) - Accesses documents as your domain models (<abbr title="Plain Old CLR Objects">POCO</abbr>s)
- Uses `Task`-based async for all data access functions - Uses `Task`-based async for all data access functions

View File

@ -6,17 +6,14 @@
<AssemblyVersion>4.0.0.0</AssemblyVersion> <AssemblyVersion>4.0.0.0</AssemblyVersion>
<FileVersion>4.0.0.0</FileVersion> <FileVersion>4.0.0.0</FileVersion>
<VersionPrefix>4.0.0</VersionPrefix> <VersionPrefix>4.0.0</VersionPrefix>
<VersionSuffix>rc5</VersionSuffix>
<PackageReleaseNotes>From v3.1: (see project site for breaking changes and compatibility) <PackageReleaseNotes>From v3.1: (see project site for breaking changes and compatibility)
- Change ByField to ByFields - Change ByField to ByFields
- Support dot-access to nested document fields - Support dot-access to nested document fields
- Add Find*Ordered functions/methods - Add Find*Ordered functions/methods
- Add case-insensitive ordering (as of rc2)
Release Candidate Changes: - Preserve additional ORDER BY qualifiers (as of rc3)
- from v4-rc4: Field construction functions are now generic. - Add In / InArray comparisons (as of rc4)
- from v4-rc3: Add In/InArray field comparisons, revamp internal comparison handling. - Field construction functions are generic (as of rc5)</PackageReleaseNotes>
- from v4-rc2: preserve additional ORDER BY qualifiers.
- from v4-rc1: add case-insensitive ordering.</PackageReleaseNotes>
<Authors>danieljsummers</Authors> <Authors>danieljsummers</Authors>
<Company>Bit Badger Solutions</Company> <Company>Bit Badger Solutions</Company>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>

View File

@ -14,8 +14,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Npgsql" Version="9.0.1" /> <PackageReference Include="Npgsql" Version="9.0.2" />
<PackageReference Include="Npgsql.FSharp" Version="5.7.0" /> <PackageReference Include="Npgsql.FSharp" Version="8.0.0" />
<PackageReference Update="FSharp.Core" Version="9.0.100" /> <PackageReference Update="FSharp.Core" Version="9.0.100" />
</ItemGroup> </ItemGroup>

View File

@ -3,8 +3,10 @@
/// The type of index to generate for the document /// The type of index to generate for the document
[<Struct>] [<Struct>]
type DocumentIndex = type DocumentIndex =
/// A GIN index with standard operations (all operators supported) /// A GIN index with standard operations (all operators supported)
| Full | Full
/// A GIN index with JSONPath operations (optimized for @>, @?, @@ operators) /// A GIN index with JSONPath operations (optimized for @>, @?, @@ operators)
| Optimized | Optimized
@ -36,6 +38,7 @@ open Npgsql.FSharp
/// Helper functions /// Helper functions
[<AutoOpen>] [<AutoOpen>]
module private Helpers = module private Helpers =
/// Shorthand to retrieve the data source as SqlProps /// Shorthand to retrieve the data source as SqlProps
let internal fromDataSource () = let internal fromDataSource () =
Configuration.dataSource () |> Sql.fromDataSource Configuration.dataSource () |> Sql.fromDataSource

View File

@ -5,6 +5,7 @@ This package provides a lightweight document library backed by [PostgreSQL](http
## Features ## Features
- Select, insert, update, save (upsert), delete, count, and check existence of documents, and create tables and indexes for these documents - 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, via comparison on any field, via equality on any property (using JSON containment, on a likely indexed field), or via condition on any property (using JSON Path queries) - Address documents via ID, via comparison on any field, via equality on any property (using JSON containment, on a likely indexed field), or via condition on any property (using JSON Path queries)
- Access documents as your domain models (<abbr title="Plain Old CLR Objects">POCO</abbr>s) - 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 `Task`-based async for all data access functions

View File

@ -1,6 +1,5 @@
namespace BitBadger.Documents.Sqlite namespace BitBadger.Documents.Sqlite
open BitBadger.Documents
open Microsoft.Data.Sqlite open Microsoft.Data.Sqlite
/// F# extensions for the SqliteConnection type /// F# extensions for the SqliteConnection type
@ -263,9 +262,3 @@ type SqliteConnectionCSharpExtensions =
[<Extension>] [<Extension>]
static member inline DeleteByFields(conn, tableName, howMatched, fields) = static member inline DeleteByFields(conn, tableName, howMatched, fields) =
WithConn.Delete.byFields tableName howMatched fields conn 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 ])

View File

@ -153,7 +153,7 @@ module Results =
/// Create a domain item from a document, specifying the field in which the document is found /// Create a domain item from a document, specifying the field in which the document is found
[<CompiledName "FromDocument">] [<CompiledName "FromDocument">]
let fromDocument<'TDoc> field (rdr: SqliteDataReader) : 'TDoc = 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 /// Create a domain item from a document
[<CompiledName "FromData">] [<CompiledName "FromData">]

View File

@ -5,6 +5,7 @@ This package provides a lightweight document library backed by [SQLite](https://
## Features ## Features
- Select, insert, update, save (upsert), delete, count, and check existence of documents, and create tables and indexes for these documents - 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 - 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) - 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 `Task`-based async for all data access functions
@ -72,28 +73,28 @@ Count customers in Atlanta:
```csharp ```csharp
// C#; parameters are table name, field, operator, and value // C#; parameters are table name, field, operator, and value
// Count.ByField type signature is Func<string, Field, Task<long>> // Count.ByFields type signature is Func<string, FieldMatch, IEnumerable<Field>, Task<long>>
var customerCount = await Count.ByField("customer", Field.Equal("City", "Atlanta")); var customerCount = await Count.ByFields("customer", FieldMatch.Any, [Field.Equal("City", "Atlanta")]);
``` ```
```fsharp ```fsharp
// F# // F#
// Count.byField type signature is string -> Field -> Task<int64> // Count.byFields type signature is string -> FieldMatch -> Field seq -> Task<int64>
let! customerCount = Count.byField "customer" (Field.Equal "City" "Atlanta") let! customerCount = Count.byFields "customer" Any [ Field.Equal "City" "Atlanta" ]
``` ```
Delete customers in Chicago: _(no offense, Second City; just an example...)_ Delete customers in Chicago: _(no offense, Second City; just an example...)_
```csharp ```csharp
// C#; parameters are same as above, except return is void // C#; parameters are same as above, except return is void
// Delete.ByField type signature is Func<string, Field, Task> // Delete.ByFields type signature is Func<string, FieldMatch, IEnumerable<Field>, Task>
await Delete.ByField("customer", Field.Equal("City", "Chicago")); await Delete.ByFields("customer", FieldMatch.Any, [Field.Equal("City", "Chicago")]);
``` ```
```fsharp ```fsharp
// F# // F#
// Delete.byField type signature is string -> string -> Op -> obj -> Task<unit> // Delete.byFields type signature is string -> FieldMatch -> Field seq -> Task<unit>
do! Delete.byField "customer" (Field.Equal "City" "Chicago") do! Delete.byFields "customer" Any [ Field.Equal "City" "Chicago" ]
``` ```
## More Information ## More Information