V3 rc2 (#2)
- Implements `Field` type for by-field actions (**BREAKING**) - Adds `RemoveFields*` functions/methods for removing fields from documents
This commit is contained in:
		
							parent
							
								
									68ad874256
								
							
						
					
					
						commit
						06daa4ea5c
					
				@ -1,7 +1,7 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <PackageReleaseNotes>Initial release (RC 1)</PackageReleaseNotes>
 | 
			
		||||
    <PackageReleaseNotes>Added Field type for by-field operations</PackageReleaseNotes>
 | 
			
		||||
    <PackageTags>JSON Document SQL</PackageTags>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,51 @@ type Op =
 | 
			
		||||
        | NEX -> "IS NULL"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Criteria for a field WHERE clause
 | 
			
		||||
type Field = {
 | 
			
		||||
    /// The name of the field
 | 
			
		||||
    Name: string
 | 
			
		||||
    
 | 
			
		||||
    /// The operation by which the field will be compared
 | 
			
		||||
    Op: Op
 | 
			
		||||
    
 | 
			
		||||
    /// The value of the field
 | 
			
		||||
    Value: obj
 | 
			
		||||
} with
 | 
			
		||||
    
 | 
			
		||||
    /// Create an equals (=) field criterion
 | 
			
		||||
    static member EQ name (value: obj) =
 | 
			
		||||
        { Name = name; Op = EQ; Value = value }
 | 
			
		||||
    
 | 
			
		||||
    /// Create a greater than (>) field criterion
 | 
			
		||||
    static member GT name (value: obj) =
 | 
			
		||||
        { Name = name; Op = GT; Value = value }
 | 
			
		||||
    
 | 
			
		||||
    /// Create a greater than or equal to (>=) field criterion
 | 
			
		||||
    static member GE name (value: obj) =
 | 
			
		||||
        { Name = name; Op = GE; Value = value }
 | 
			
		||||
    
 | 
			
		||||
    /// Create a less than (<) field criterion
 | 
			
		||||
    static member LT name (value: obj) =
 | 
			
		||||
        { Name = name; Op = LT; Value = value }
 | 
			
		||||
    
 | 
			
		||||
    /// Create a less than or equal to (<=) field criterion
 | 
			
		||||
    static member LE name (value: obj) =
 | 
			
		||||
        { Name = name; Op = LE; Value = value }
 | 
			
		||||
    
 | 
			
		||||
    /// Create a not equals (<>) field criterion
 | 
			
		||||
    static member NE name (value: obj) =
 | 
			
		||||
        { Name = name; Op = NE; Value = value }
 | 
			
		||||
    
 | 
			
		||||
    /// Create an exists (IS NOT NULL) field criterion
 | 
			
		||||
    static member EX name =
 | 
			
		||||
        { Name = name; Op = EX; Value = obj () }
 | 
			
		||||
    
 | 
			
		||||
    /// Create an not exists (IS NULL) field criterion
 | 
			
		||||
    static member NEX name =
 | 
			
		||||
        { Name = name; Op = NEX; Value = obj () }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// The required document serialization implementation
 | 
			
		||||
type IDocumentSerializer =
 | 
			
		||||
    
 | 
			
		||||
@ -107,17 +152,14 @@ module Query =
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
 | 
			
		||||
    [<CompiledName "WhereByField">]
 | 
			
		||||
    let whereByField fieldName op paramName =
 | 
			
		||||
        let theRest =
 | 
			
		||||
            match op with
 | 
			
		||||
            | EX | NEX -> string op
 | 
			
		||||
            | _ -> $"{op} %s{paramName}"
 | 
			
		||||
        $"data ->> '%s{fieldName}' {theRest}"
 | 
			
		||||
    let whereByField field paramName =
 | 
			
		||||
        let theRest = match field.Op with EX | NEX -> string field.Op | _ -> $"{field.Op} %s{paramName}"
 | 
			
		||||
        $"data ->> '%s{field.Name}' {theRest}"
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement an ID-based query
 | 
			
		||||
    [<CompiledName "WhereById">]
 | 
			
		||||
    let whereById paramName =
 | 
			
		||||
        whereByField (Configuration.idField ()) EQ paramName
 | 
			
		||||
        whereByField (Field.EQ (Configuration.idField ()) 0) paramName
 | 
			
		||||
    
 | 
			
		||||
    /// Queries to define tables and indexes
 | 
			
		||||
    module Definition =
 | 
			
		||||
@ -178,8 +220,8 @@ module Query =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count matching documents using a text comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op =
 | 
			
		||||
            $"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField fieldName op "@field"}"""
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries for determining document existence
 | 
			
		||||
    module Exists =
 | 
			
		||||
@ -191,8 +233,8 @@ module Query =
 | 
			
		||||
 | 
			
		||||
        /// Query to determine if documents exist using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField fieldName op "@field"}) AS it"""
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField field "@field"}) AS it"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries for retrieving documents
 | 
			
		||||
    module Find =
 | 
			
		||||
@ -204,8 +246,8 @@ module Query =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to retrieve documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op =
 | 
			
		||||
            $"""{selectFromTable tableName} WHERE {whereByField fieldName op "@field"}"""
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""{selectFromTable tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries to delete documents
 | 
			
		||||
    module Delete =
 | 
			
		||||
@ -217,5 +259,5 @@ module Query =
 | 
			
		||||
 | 
			
		||||
        /// Query to delete documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereByField fieldName op "@field"}"""
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@
 | 
			
		||||
    <AssemblyVersion>3.0.0.0</AssemblyVersion>
 | 
			
		||||
    <FileVersion>3.0.0.0</FileVersion>
 | 
			
		||||
    <VersionPrefix>3.0.0</VersionPrefix>
 | 
			
		||||
    <VersionSuffix>rc-1</VersionSuffix>
 | 
			
		||||
    <VersionSuffix>rc-2</VersionSuffix>
 | 
			
		||||
    <Authors>danieljsummers</Authors>
 | 
			
		||||
    <Company>Bit Badger Solutions</Company>
 | 
			
		||||
    <PackageReadmeFile>README.md</PackageReadmeFile>
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <PackageReleaseNotes>Initial release; migrated from BitBadger.Npgsql.Documents, with field and extension additions (RC 1)</PackageReleaseNotes>
 | 
			
		||||
    <PackageReleaseNotes>Adds Field type for by-field operations (BREAKING from rc-1); adds RemoveFields* functions</PackageReleaseNotes>
 | 
			
		||||
    <PackageTags>JSON Document PostgreSQL Npgsql</PackageTags>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -50,8 +50,8 @@ module Extensions =
 | 
			
		||||
            WithProps.Count.all tableName (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Count matching documents using a JSON field comparison query (->> =)
 | 
			
		||||
        member conn.countByField tableName fieldName op (value: obj) =
 | 
			
		||||
            WithProps.Count.byField tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
        member conn.countByField tableName field =
 | 
			
		||||
            WithProps.Count.byField tableName field (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Count matching documents using a JSON containment query (@>)
 | 
			
		||||
        member conn.countByContains tableName criteria =
 | 
			
		||||
@ -66,8 +66,8 @@ module Extensions =
 | 
			
		||||
            WithProps.Exists.byId tableName docId (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Determine if documents exist using a JSON field comparison query (->> =)
 | 
			
		||||
        member conn.existsByField tableName fieldName op (value: obj) =
 | 
			
		||||
            WithProps.Exists.byField tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
        member conn.existsByField tableName field =
 | 
			
		||||
            WithProps.Exists.byField tableName field (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Determine if documents exist using a JSON containment query (@>)
 | 
			
		||||
        member conn.existsByContains tableName criteria =
 | 
			
		||||
@ -86,8 +86,8 @@ module Extensions =
 | 
			
		||||
            WithProps.Find.byId<'TKey, 'TDoc> tableName docId (Sql.existingConnection conn)
 | 
			
		||||
 | 
			
		||||
        /// Retrieve documents matching a JSON field comparison query (->> =)
 | 
			
		||||
        member conn.findByField<'TDoc> tableName fieldName op (value: obj) =
 | 
			
		||||
            WithProps.Find.byField<'TDoc> tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
        member conn.findByField<'TDoc> tableName field =
 | 
			
		||||
            WithProps.Find.byField<'TDoc> tableName field (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve documents matching a JSON containment query (@>)
 | 
			
		||||
        member conn.findByContains<'TDoc> tableName (criteria: obj) =
 | 
			
		||||
@ -98,8 +98,8 @@ module Extensions =
 | 
			
		||||
            WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
 | 
			
		||||
        member conn.findFirstByField<'TDoc> tableName fieldName op (value: obj) =
 | 
			
		||||
            WithProps.Find.firstByField<'TDoc> tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
        member conn.findFirstByField<'TDoc> tableName field =
 | 
			
		||||
            WithProps.Find.firstByField<'TDoc> tableName field (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve the first document matching a JSON containment query (@>); returns None if not found
 | 
			
		||||
        member conn.findFirstByContains<'TDoc> tableName (criteria: obj) =
 | 
			
		||||
@ -122,8 +122,8 @@ module Extensions =
 | 
			
		||||
            WithProps.Patch.byId tableName docId patch (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
 | 
			
		||||
        member conn.patchByField tableName fieldName op (value: obj) (patch: 'TPatch) =
 | 
			
		||||
            WithProps.Patch.byField tableName fieldName op value patch (Sql.existingConnection conn)
 | 
			
		||||
        member conn.patchByField tableName field (patch: 'TPatch) =
 | 
			
		||||
            WithProps.Patch.byField tableName field patch (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Patch documents using a JSON containment query in the WHERE clause (@>)
 | 
			
		||||
        member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
 | 
			
		||||
@ -133,13 +133,29 @@ module Extensions =
 | 
			
		||||
        member conn.patchByJsonPath tableName jsonPath (patch: 'TPatch) =
 | 
			
		||||
            WithProps.Patch.byJsonPath tableName jsonPath patch (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from a document by the document's ID
 | 
			
		||||
        member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
 | 
			
		||||
            WithProps.RemoveFields.byId tableName docId fieldNames (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
        member conn.removeFieldsByField tableName field fieldNames =
 | 
			
		||||
            WithProps.RemoveFields.byField tableName field fieldNames (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a JSON containment query (@>)
 | 
			
		||||
        member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames =
 | 
			
		||||
            WithProps.RemoveFields.byContains tableName criteria fieldNames (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a JSON Path match query (@?)
 | 
			
		||||
        member conn.removeFieldsByJsonPath tableName jsonPath fieldNames =
 | 
			
		||||
            WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Delete a document by its ID
 | 
			
		||||
        member conn.deleteById tableName (docId: 'TKey) =
 | 
			
		||||
            WithProps.Delete.byId tableName docId (Sql.existingConnection conn)
 | 
			
		||||
 | 
			
		||||
        /// Delete documents by matching a JSON field comparison query (->> =)
 | 
			
		||||
        member conn.deleteByField tableName fieldName op (value: obj) =
 | 
			
		||||
            WithProps.Delete.byField tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
        member conn.deleteByField tableName field =
 | 
			
		||||
            WithProps.Delete.byField tableName field (Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
        /// Delete documents by matching a JSON containment query (@>)
 | 
			
		||||
        member conn.deleteByContains tableName (criteria: 'TContains) =
 | 
			
		||||
@ -209,8 +225,8 @@ type NpgsqlConnectionCSharpExtensions =
 | 
			
		||||
    
 | 
			
		||||
    /// Count matching documents using a JSON field comparison query (->> =)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline CountByField(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithProps.Count.byField tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
    static member inline CountByField(conn, tableName, field) =
 | 
			
		||||
        WithProps.Count.byField tableName field (Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Count matching documents using a JSON containment query (@>)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -229,8 +245,8 @@ type NpgsqlConnectionCSharpExtensions =
 | 
			
		||||
    
 | 
			
		||||
    /// Determine if documents exist using a JSON field comparison query (->> =)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline ExistsByField(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithProps.Exists.byField tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
    static member inline ExistsByField(conn, tableName, field) =
 | 
			
		||||
        WithProps.Exists.byField tableName field (Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Determine if documents exist using a JSON containment query (@>)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -254,8 +270,8 @@ type NpgsqlConnectionCSharpExtensions =
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents matching a JSON field comparison query (->> =)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline FindByField<'TDoc>(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithProps.Find.ByField<'TDoc>(tableName, fieldName, op, value, Sql.existingConnection conn)
 | 
			
		||||
    static member inline FindByField<'TDoc>(conn, tableName, field) =
 | 
			
		||||
        WithProps.Find.ByField<'TDoc>(tableName, field, Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve documents matching a JSON containment query (@>)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -269,8 +285,8 @@ type NpgsqlConnectionCSharpExtensions =
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithProps.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, Sql.existingConnection conn)
 | 
			
		||||
    static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
 | 
			
		||||
        WithProps.Find.FirstByField<'TDoc>(tableName, field, Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve the first document matching a JSON containment query (@>); returns None if not found
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -299,8 +315,8 @@ type NpgsqlConnectionCSharpExtensions =
 | 
			
		||||
    
 | 
			
		||||
    /// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline PatchByField(conn, tableName, fieldName, op, value: obj, patch: 'TPatch) =
 | 
			
		||||
        WithProps.Patch.byField tableName fieldName op value patch (Sql.existingConnection conn)
 | 
			
		||||
    static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
 | 
			
		||||
        WithProps.Patch.byField tableName field patch (Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Patch documents using a JSON containment query in the WHERE clause (@>)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -312,6 +328,26 @@ type NpgsqlConnectionCSharpExtensions =
 | 
			
		||||
    static member inline PatchByJsonPath(conn, tableName, jsonPath, patch: 'TPatch) =
 | 
			
		||||
        WithProps.Patch.byJsonPath tableName jsonPath patch (Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Remove fields from a document by the document's ID
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline RemoveFieldsById(conn, tableName, docId: 'TKey, fieldNames) =
 | 
			
		||||
        WithProps.RemoveFields.ById(tableName, docId, fieldNames, Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
    /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
 | 
			
		||||
        WithProps.RemoveFields.ByField(tableName, field, fieldNames, Sql.existingConnection conn)
 | 
			
		||||
        
 | 
			
		||||
    /// Remove fields from documents via a JSON containment query (@>)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    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 (@?)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline RemoveFieldsByJsonPath(conn, tableName, jsonPath, fieldNames) =
 | 
			
		||||
        WithProps.RemoveFields.ByJsonPath(tableName, jsonPath, fieldNames, Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Delete a document by its ID
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline DeleteById(conn, tableName, docId: 'TKey) =
 | 
			
		||||
@ -319,8 +355,8 @@ type NpgsqlConnectionCSharpExtensions =
 | 
			
		||||
 | 
			
		||||
    /// Delete documents by matching a JSON field comparison query (->> =)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline DeleteByField(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithProps.Delete.byField tableName fieldName op value (Sql.existingConnection conn)
 | 
			
		||||
    static member inline DeleteByField(conn, tableName, field) =
 | 
			
		||||
        WithProps.Delete.byField tableName field (Sql.existingConnection conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Delete documents by matching a JSON containment query (@>)
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
 | 
			
		||||
@ -64,10 +64,29 @@ module Parameters =
 | 
			
		||||
        name, Sql.jsonb (Configuration.serializer().Serialize it)
 | 
			
		||||
 | 
			
		||||
    /// Create a JSON field parameter (name "@field")
 | 
			
		||||
    [<CompiledName "Field">]
 | 
			
		||||
    let fieldParam (value: obj) =
 | 
			
		||||
        "@field", Sql.parameter (NpgsqlParameter("@field", value))
 | 
			
		||||
    [<CompiledName "FSharpAddField">]
 | 
			
		||||
    let addFieldParam name field parameters =
 | 
			
		||||
        match field.Op with
 | 
			
		||||
        | EX | NEX -> parameters
 | 
			
		||||
        | _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) :: parameters
 | 
			
		||||
 | 
			
		||||
    /// Create a JSON field parameter (name "@field")
 | 
			
		||||
    let AddField name field parameters =
 | 
			
		||||
        match field.Op with
 | 
			
		||||
        | EX | NEX -> parameters
 | 
			
		||||
        | _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) |> Seq.singleton |> Seq.append parameters
 | 
			
		||||
 | 
			
		||||
    /// Append JSON field name parameters for the given field names to the given parameters
 | 
			
		||||
    [<CompiledName "FSharpFieldName">]
 | 
			
		||||
    let fieldNameParam (fieldNames: string list) =
 | 
			
		||||
        if fieldNames.Length = 1 then "@name", Sql.string fieldNames[0]
 | 
			
		||||
        else "@name", Sql.stringArray (Array.ofList fieldNames)
 | 
			
		||||
 | 
			
		||||
    /// Append JSON field name parameters for the given field names to the given parameters
 | 
			
		||||
    let FieldName(fieldNames: string seq) =
 | 
			
		||||
        if Seq.isEmpty fieldNames then "@name", Sql.string (Seq.head fieldNames)
 | 
			
		||||
        else "@name", Sql.stringArray (Array.ofSeq fieldNames)
 | 
			
		||||
    
 | 
			
		||||
    /// An empty parameter sequence
 | 
			
		||||
    [<CompiledName "None">]
 | 
			
		||||
    let noParams =
 | 
			
		||||
@ -145,25 +164,56 @@ module Query =
 | 
			
		||||
    /// Queries to patch (partially update) documents
 | 
			
		||||
    module Patch =
 | 
			
		||||
 | 
			
		||||
        /// Create an UPDATE statement to patch documents
 | 
			
		||||
        let private update tableName whereClause =
 | 
			
		||||
            $"UPDATE %s{tableName} SET data = data || @data WHERE {whereClause}"
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereById "@id"}"""
 | 
			
		||||
            Query.whereById "@id" |> update tableName
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch documents match a JSON field comparison (->> =)
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op =
 | 
			
		||||
            $"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereByField fieldName op "@field"}"""
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            Query.whereByField field "@field" |> update tableName
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch documents matching a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
        let byContains tableName =
 | 
			
		||||
            $"""UPDATE %s{tableName} SET data = data || @data WHERE {whereDataContains "@criteria"}"""
 | 
			
		||||
            whereDataContains "@criteria" |> update tableName
 | 
			
		||||
 | 
			
		||||
        /// Query to patch documents matching a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByJsonPath">]
 | 
			
		||||
        let byJsonPath tableName =
 | 
			
		||||
            $"""UPDATE %s{tableName} SET data = data || @data WHERE {whereJsonPathMatches "@path"}"""
 | 
			
		||||
            whereJsonPathMatches "@path" |> update tableName
 | 
			
		||||
 | 
			
		||||
    /// Queries to remove fields from documents
 | 
			
		||||
    module RemoveFields =
 | 
			
		||||
        
 | 
			
		||||
        /// Create an UPDATE statement to remove parameters
 | 
			
		||||
        let private update tableName whereClause =
 | 
			
		||||
            $"UPDATE %s{tableName} SET data = data - @name WHERE {whereClause}"
 | 
			
		||||
            
 | 
			
		||||
        /// Query to remove fields from a document by the document's ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            Query.whereById "@id" |> update tableName
 | 
			
		||||
        
 | 
			
		||||
        /// Query to remove fields from documents via a comparison on a JSON field within the document
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            Query.whereByField field "@field" |> update tableName
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch documents matching a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
        let byContains tableName =
 | 
			
		||||
            whereDataContains "@criteria" |> update tableName
 | 
			
		||||
 | 
			
		||||
        /// Query to patch documents matching a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByJsonPath">]
 | 
			
		||||
        let byJsonPath tableName =
 | 
			
		||||
            whereJsonPathMatches "@path" |> update tableName
 | 
			
		||||
 | 
			
		||||
    /// Queries to delete documents
 | 
			
		||||
    module Delete =
 | 
			
		||||
@ -302,8 +352,8 @@ module WithProps =
 | 
			
		||||
        
 | 
			
		||||
        /// Count matching documents using a JSON field comparison (->> =)
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) sqlProps =
 | 
			
		||||
            Custom.scalar (Query.Count.byField tableName fieldName op) [ fieldParam value ] toCount sqlProps
 | 
			
		||||
        let byField tableName field sqlProps =
 | 
			
		||||
            Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Count matching documents using a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
@ -326,8 +376,8 @@ module WithProps =
 | 
			
		||||
 | 
			
		||||
        /// Determine if a document exists using a JSON field comparison (->> =)
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) sqlProps =
 | 
			
		||||
            Custom.scalar (Query.Exists.byField tableName fieldName op) [ fieldParam value ] toExists sqlProps
 | 
			
		||||
        let byField tableName field sqlProps =
 | 
			
		||||
            Custom.scalar (Query.Exists.byField tableName field) (addFieldParam "@field" field []) toExists sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Determine if a document exists using a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
@ -363,13 +413,14 @@ module WithProps =
 | 
			
		||||
 | 
			
		||||
        /// Retrieve documents matching a JSON field comparison (->> =)
 | 
			
		||||
        [<CompiledName "FSharpByField">]
 | 
			
		||||
        let byField<'TDoc> tableName fieldName op (value: obj) sqlProps =
 | 
			
		||||
            Custom.list<'TDoc> (Query.Find.byField tableName fieldName op) [ fieldParam value ] fromData<'TDoc> sqlProps
 | 
			
		||||
        let byField<'TDoc> tableName field sqlProps =
 | 
			
		||||
            Custom.list<'TDoc>
 | 
			
		||||
                (Query.Find.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve documents matching a JSON field comparison (->> =)
 | 
			
		||||
        let ByField<'TDoc>(tableName, fieldName, op, value: obj, sqlProps) =
 | 
			
		||||
        let ByField<'TDoc>(tableName, field, sqlProps) =
 | 
			
		||||
            Custom.List<'TDoc>(
 | 
			
		||||
                Query.Find.byField tableName fieldName op, [ fieldParam value ], fromData<'TDoc>, sqlProps)
 | 
			
		||||
                Query.Find.byField tableName field, addFieldParam "@field" field [], fromData<'TDoc>, sqlProps)
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve documents matching a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "FSharpByContains">]
 | 
			
		||||
@ -395,14 +446,20 @@ module WithProps =
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
 | 
			
		||||
        [<CompiledName "FSharpFirstByField">]
 | 
			
		||||
        let firstByField<'TDoc> tableName fieldName op (value: obj) sqlProps =
 | 
			
		||||
        let firstByField<'TDoc> tableName field sqlProps =
 | 
			
		||||
            Custom.single<'TDoc>
 | 
			
		||||
                $"{Query.Find.byField tableName fieldName op} LIMIT 1" [ fieldParam value ] fromData<'TDoc> sqlProps
 | 
			
		||||
                $"{Query.Find.byField tableName field} LIMIT 1"
 | 
			
		||||
                (addFieldParam "@field" field [])
 | 
			
		||||
                fromData<'TDoc>
 | 
			
		||||
                sqlProps
 | 
			
		||||
            
 | 
			
		||||
        /// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
 | 
			
		||||
        let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value: obj, sqlProps) =
 | 
			
		||||
        let FirstByField<'TDoc when 'TDoc: null>(tableName, field, sqlProps) =
 | 
			
		||||
            Custom.Single<'TDoc>(
 | 
			
		||||
                $"{Query.Find.byField tableName fieldName op} LIMIT 1", [ fieldParam value ], fromData<'TDoc>, sqlProps)
 | 
			
		||||
                $"{Query.Find.byField tableName field} LIMIT 1",
 | 
			
		||||
                addFieldParam "@field" field [],
 | 
			
		||||
                fromData<'TDoc>,
 | 
			
		||||
                sqlProps)
 | 
			
		||||
            
 | 
			
		||||
        /// Retrieve the first document matching a JSON containment query (@>); returns None if not found
 | 
			
		||||
        [<CompiledName "FSharpFirstByContains">]
 | 
			
		||||
@ -461,9 +518,11 @@ module WithProps =
 | 
			
		||||
        
 | 
			
		||||
        /// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) (patch: 'TPatch) sqlProps =
 | 
			
		||||
        let byField tableName field (patch: 'TPatch) sqlProps =
 | 
			
		||||
            Custom.nonQuery
 | 
			
		||||
                (Query.Patch.byField tableName fieldName op) [ jsonParam "@data" patch; fieldParam value ] sqlProps
 | 
			
		||||
                (Query.Patch.byField tableName field)
 | 
			
		||||
                (addFieldParam "@field" field [ jsonParam "@data" patch ])
 | 
			
		||||
                sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Patch documents using a JSON containment query in the WHERE clause (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
@ -477,6 +536,55 @@ module WithProps =
 | 
			
		||||
            Custom.nonQuery
 | 
			
		||||
                (Query.Patch.byJsonPath tableName) [ jsonParam "@data" patch; "@path", Sql.string jsonPath ] sqlProps
 | 
			
		||||
 | 
			
		||||
    /// Commands to remove fields from documents
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
    module RemoveFields =
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from a document by the document's ID
 | 
			
		||||
        [<CompiledName "FSharpById">]
 | 
			
		||||
        let byId tableName (docId: 'TKey) fieldNames sqlProps =
 | 
			
		||||
            Custom.nonQuery (Query.RemoveFields.byId tableName) [ idParam docId; fieldNameParam fieldNames ] sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from a document by the document's ID
 | 
			
		||||
        let ById(tableName, docId: 'TKey, fieldNames, sqlProps) =
 | 
			
		||||
            byId tableName docId (List.ofSeq fieldNames) sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
        [<CompiledName "FSharpByField">]
 | 
			
		||||
        let byField tableName field fieldNames sqlProps =
 | 
			
		||||
            Custom.nonQuery
 | 
			
		||||
                (Query.RemoveFields.byField tableName field)
 | 
			
		||||
                (addFieldParam "@field" field [ fieldNameParam fieldNames ])
 | 
			
		||||
                sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
        let ByField(tableName, field, fieldNames, sqlProps) =
 | 
			
		||||
            byField tableName field (List.ofSeq fieldNames) sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "FSharpByContains">]
 | 
			
		||||
        let byContains tableName (criteria: 'TContains) fieldNames sqlProps =
 | 
			
		||||
            Custom.nonQuery
 | 
			
		||||
                (Query.RemoveFields.byContains tableName)
 | 
			
		||||
                [ jsonParam "@criteria" criteria; fieldNameParam fieldNames ]
 | 
			
		||||
                sqlProps
 | 
			
		||||
    
 | 
			
		||||
        /// Remove fields from documents via a JSON containment query (@>)
 | 
			
		||||
        let ByContains(tableName, criteria: 'TContains, fieldNames, sqlProps) =
 | 
			
		||||
            byContains tableName criteria (List.ofSeq fieldNames) sqlProps
 | 
			
		||||
    
 | 
			
		||||
        /// Remove fields from documents via a JSON Path match query (@?)
 | 
			
		||||
        [<CompiledName "FSharpByJsonPath">]
 | 
			
		||||
        let byJsonPath tableName jsonPath fieldNames sqlProps =
 | 
			
		||||
            Custom.nonQuery
 | 
			
		||||
                (Query.RemoveFields.byJsonPath tableName)
 | 
			
		||||
                [ "@path", Sql.string jsonPath; fieldNameParam fieldNames ]
 | 
			
		||||
                sqlProps
 | 
			
		||||
    
 | 
			
		||||
        /// Remove fields from documents via a JSON Path match query (@?)
 | 
			
		||||
        let ByJsonPath(tableName, jsonPath, fieldNames, sqlProps) =
 | 
			
		||||
            byJsonPath tableName jsonPath (List.ofSeq fieldNames) sqlProps
 | 
			
		||||
    
 | 
			
		||||
    /// Commands to delete documents
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
    module Delete =
 | 
			
		||||
@ -488,8 +596,8 @@ module WithProps =
 | 
			
		||||
 | 
			
		||||
        /// Delete documents by matching a JSON field comparison query (->> =)
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) sqlProps =
 | 
			
		||||
            Custom.nonQuery (Query.Delete.byField tableName fieldName op) [ fieldParam value ] sqlProps
 | 
			
		||||
        let byField tableName field sqlProps =
 | 
			
		||||
            Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam "@field" field []) sqlProps
 | 
			
		||||
        
 | 
			
		||||
        /// Delete documents by matching a JSON contains query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
@ -585,8 +693,8 @@ module Count =
 | 
			
		||||
    
 | 
			
		||||
    /// Count matching documents using a JSON field comparison query (->> =)
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) =
 | 
			
		||||
        WithProps.Count.byField tableName fieldName op value (fromDataSource ())
 | 
			
		||||
    let byField tableName field =
 | 
			
		||||
        WithProps.Count.byField tableName field (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Count matching documents using a JSON containment query (@>)
 | 
			
		||||
    [<CompiledName "ByContains">]
 | 
			
		||||
@ -610,8 +718,8 @@ module Exists =
 | 
			
		||||
    
 | 
			
		||||
    /// Determine if documents exist using a JSON field comparison query (->> =)
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) =
 | 
			
		||||
        WithProps.Exists.byField tableName fieldName op value (fromDataSource ())
 | 
			
		||||
    let byField tableName field =
 | 
			
		||||
        WithProps.Exists.byField tableName field (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Determine if documents exist using a JSON containment query (@>)
 | 
			
		||||
    [<CompiledName "ByContains">]
 | 
			
		||||
@ -648,12 +756,12 @@ module Find =
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents matching a JSON field comparison query (->> =)
 | 
			
		||||
    [<CompiledName "FSharpByField">]
 | 
			
		||||
    let byField<'TDoc> tableName fieldName op (value: obj) =
 | 
			
		||||
        WithProps.Find.byField<'TDoc> tableName fieldName op value (fromDataSource ())
 | 
			
		||||
    let byField<'TDoc> tableName field =
 | 
			
		||||
        WithProps.Find.byField<'TDoc> tableName field (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve documents matching a JSON field comparison query (->> =)
 | 
			
		||||
    let ByField<'TDoc>(tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithProps.Find.ByField<'TDoc>(tableName, fieldName, op, value, fromDataSource ())
 | 
			
		||||
    let ByField<'TDoc>(tableName, field) =
 | 
			
		||||
        WithProps.Find.ByField<'TDoc>(tableName, field, fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve documents matching a JSON containment query (@>)
 | 
			
		||||
    [<CompiledName "FSharpByContains">]
 | 
			
		||||
@ -675,12 +783,12 @@ module Find =
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
 | 
			
		||||
    [<CompiledName "FSharpFirstByField">]
 | 
			
		||||
    let firstByField<'TDoc> tableName fieldName op (value: obj) =
 | 
			
		||||
        WithProps.Find.firstByField<'TDoc> tableName fieldName op value (fromDataSource ())
 | 
			
		||||
    let firstByField<'TDoc> tableName field =
 | 
			
		||||
        WithProps.Find.firstByField<'TDoc> tableName field (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
 | 
			
		||||
    let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithProps.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, fromDataSource ())
 | 
			
		||||
    let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
 | 
			
		||||
        WithProps.Find.FirstByField<'TDoc>(tableName, field, fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve the first document matching a JSON containment query (@>); returns None if not found
 | 
			
		||||
    [<CompiledName "FSharpFirstByContains">]
 | 
			
		||||
@ -731,8 +839,8 @@ module Patch =
 | 
			
		||||
    
 | 
			
		||||
    /// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) (patch: 'TPatch) =
 | 
			
		||||
        WithProps.Patch.byField tableName fieldName op value patch (fromDataSource ())
 | 
			
		||||
    let byField tableName field (patch: 'TPatch) =
 | 
			
		||||
        WithProps.Patch.byField tableName field patch (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Patch documents using a JSON containment query in the WHERE clause (@>)
 | 
			
		||||
    [<CompiledName "ByContains">]
 | 
			
		||||
@ -745,6 +853,47 @@ module Patch =
 | 
			
		||||
        WithProps.Patch.byJsonPath tableName jsonPath patch (fromDataSource ())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Commands to remove fields from documents
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
module RemoveFields =
 | 
			
		||||
    
 | 
			
		||||
    /// Remove fields from a document by the document's ID
 | 
			
		||||
    [<CompiledName "FSharpById">]
 | 
			
		||||
    let byId tableName (docId: 'TKey) fieldNames =
 | 
			
		||||
        WithProps.RemoveFields.byId tableName docId fieldNames (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Remove fields from a document by the document's ID
 | 
			
		||||
    let ById(tableName, docId: 'TKey, fieldNames) =
 | 
			
		||||
        WithProps.RemoveFields.ById(tableName, docId, fieldNames, fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
    [<CompiledName "FSharpByField">]
 | 
			
		||||
    let byField tableName field fieldNames =
 | 
			
		||||
        WithProps.RemoveFields.byField tableName field fieldNames (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
    let ByField(tableName, field, fieldNames) =
 | 
			
		||||
        WithProps.RemoveFields.ByField(tableName, field, fieldNames, fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Remove fields from documents via a JSON containment query (@>)
 | 
			
		||||
    [<CompiledName "FSharpByContains">]
 | 
			
		||||
    let byContains tableName (criteria: 'TContains) fieldNames =
 | 
			
		||||
        WithProps.RemoveFields.byContains tableName criteria fieldNames (fromDataSource ())
 | 
			
		||||
 | 
			
		||||
    /// Remove fields from documents via a JSON containment query (@>)
 | 
			
		||||
    let ByContains(tableName, criteria: 'TContains, fieldNames) =
 | 
			
		||||
        WithProps.RemoveFields.ByContains(tableName, criteria, fieldNames, fromDataSource ())
 | 
			
		||||
 | 
			
		||||
    /// Remove fields from documents via a JSON Path match query (@?)
 | 
			
		||||
    [<CompiledName "FSharpByJsonPath">]
 | 
			
		||||
    let byJsonPath tableName jsonPath fieldNames =
 | 
			
		||||
        WithProps.RemoveFields.byJsonPath tableName jsonPath fieldNames (fromDataSource ())
 | 
			
		||||
 | 
			
		||||
    /// Remove fields from documents via a JSON Path match query (@?)
 | 
			
		||||
    let ByJsonPath(tableName, jsonPath, fieldNames) =
 | 
			
		||||
        WithProps.RemoveFields.ByJsonPath(tableName, jsonPath, fieldNames, fromDataSource ())
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
/// Commands to delete documents
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
module Delete =
 | 
			
		||||
@ -756,8 +905,8 @@ module Delete =
 | 
			
		||||
 | 
			
		||||
    /// Delete documents by matching a JSON field comparison query (->> =)
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) =
 | 
			
		||||
        WithProps.Delete.byField tableName fieldName op value (fromDataSource ())
 | 
			
		||||
    let byField tableName field =
 | 
			
		||||
        WithProps.Delete.byField tableName field (fromDataSource ())
 | 
			
		||||
    
 | 
			
		||||
    /// Delete documents by matching a JSON containment query (@>)
 | 
			
		||||
    [<CompiledName "ByContains">]
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <PackageReleaseNotes>Initial release; SQLite document implementation similar to BitBadger.Npgsql.Documents (RC 1)</PackageReleaseNotes>
 | 
			
		||||
    <PackageReleaseNotes>Adds Field type for by-field operations (BREAKING from rc-1); adds RemoveFields* functions</PackageReleaseNotes>
 | 
			
		||||
    <PackageTags>JSON Document SQLite</PackageTags>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.0" />
 | 
			
		||||
    <PackageReference Include="Microsoft.Data.Sqlite" Version="8.0.1" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
 | 
			
		||||
@ -45,16 +45,16 @@ module Extensions =
 | 
			
		||||
            WithConn.Count.all tableName conn
 | 
			
		||||
        
 | 
			
		||||
        /// Count matching documents using a comparison on a JSON field
 | 
			
		||||
        member conn.countByField tableName fieldName op (value: obj) =
 | 
			
		||||
            WithConn.Count.byField tableName fieldName op value conn
 | 
			
		||||
        member conn.countByField tableName field =
 | 
			
		||||
            WithConn.Count.byField tableName field conn
 | 
			
		||||
        
 | 
			
		||||
        /// Determine if a document exists for the given ID
 | 
			
		||||
        member conn.existsById tableName (docId: 'TKey) =
 | 
			
		||||
            WithConn.Exists.byId tableName docId conn
 | 
			
		||||
 | 
			
		||||
        /// Determine if a document exists using a comparison on a JSON field
 | 
			
		||||
        member conn.existsByField tableName fieldName op (value: obj) =
 | 
			
		||||
            WithConn.Exists.byField tableName fieldName op value conn
 | 
			
		||||
        member conn.existsByField tableName field =
 | 
			
		||||
            WithConn.Exists.byField tableName field conn
 | 
			
		||||
 | 
			
		||||
        /// Retrieve all documents in the given table
 | 
			
		||||
        member conn.findAll<'TDoc> tableName =
 | 
			
		||||
@ -65,12 +65,12 @@ module Extensions =
 | 
			
		||||
            WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
 | 
			
		||||
 | 
			
		||||
        /// Retrieve documents via a comparison on a JSON field
 | 
			
		||||
        member conn.findByField<'TDoc> tableName fieldName op (value: obj) =
 | 
			
		||||
            WithConn.Find.byField<'TDoc> tableName fieldName op value conn
 | 
			
		||||
        member conn.findByField<'TDoc> tableName field =
 | 
			
		||||
            WithConn.Find.byField<'TDoc> tableName field conn
 | 
			
		||||
 | 
			
		||||
        /// Retrieve documents via a comparison on a JSON field, returning only the first result
 | 
			
		||||
        member conn.findFirstByField<'TDoc> tableName fieldName op (value: obj) =
 | 
			
		||||
            WithConn.Find.firstByField<'TDoc> tableName fieldName op value conn
 | 
			
		||||
        member conn.findFirstByField<'TDoc> tableName field =
 | 
			
		||||
            WithConn.Find.firstByField<'TDoc> tableName field conn
 | 
			
		||||
 | 
			
		||||
        /// Update an entire document by its ID
 | 
			
		||||
        member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
 | 
			
		||||
@ -85,16 +85,24 @@ module Extensions =
 | 
			
		||||
            WithConn.Patch.byId tableName docId patch conn
 | 
			
		||||
        
 | 
			
		||||
        /// Patch documents using a comparison on a JSON field
 | 
			
		||||
        member conn.patchByField tableName fieldName op (value: obj) (patch: 'TPatch) =
 | 
			
		||||
            WithConn.Patch.byField tableName fieldName op value patch conn
 | 
			
		||||
        member conn.patchByField tableName field (patch: 'TPatch) =
 | 
			
		||||
            WithConn.Patch.byField tableName field patch conn
 | 
			
		||||
 | 
			
		||||
        /// Remove fields from a document by the document's ID
 | 
			
		||||
        member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
 | 
			
		||||
            WithConn.RemoveFields.byId tableName docId fieldNames conn
 | 
			
		||||
        
 | 
			
		||||
        /// Remove a field from a document via a comparison on a JSON field in the document
 | 
			
		||||
        member conn.removeFieldsByField tableName field fieldNames =
 | 
			
		||||
            WithConn.RemoveFields.byField tableName field fieldNames conn
 | 
			
		||||
        
 | 
			
		||||
        /// Delete a document by its ID
 | 
			
		||||
        member conn.deleteById tableName (docId: 'TKey) =
 | 
			
		||||
            WithConn.Delete.byId tableName docId conn
 | 
			
		||||
 | 
			
		||||
        /// Delete documents by matching a comparison on a JSON field
 | 
			
		||||
        member conn.deleteByField tableName fieldName op (value: obj) =
 | 
			
		||||
            WithConn.Delete.byField tableName fieldName op value conn
 | 
			
		||||
        member conn.deleteByField tableName field =
 | 
			
		||||
            WithConn.Delete.byField tableName field conn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
open System.Runtime.CompilerServices
 | 
			
		||||
@ -151,8 +159,8 @@ type SqliteConnectionCSharpExtensions =
 | 
			
		||||
    
 | 
			
		||||
    /// Count matching documents using a comparison on a JSON field
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline CountByField(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithConn.Count.byField tableName fieldName op value conn
 | 
			
		||||
    static member inline CountByField(conn, tableName, field) =
 | 
			
		||||
        WithConn.Count.byField tableName field conn
 | 
			
		||||
 | 
			
		||||
    /// Determine if a document exists for the given ID
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -161,8 +169,8 @@ type SqliteConnectionCSharpExtensions =
 | 
			
		||||
 | 
			
		||||
    /// Determine if a document exists using a comparison on a JSON field
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline ExistsByField(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithConn.Exists.byField tableName fieldName op value conn
 | 
			
		||||
    static member inline ExistsByField(conn, tableName, field) =
 | 
			
		||||
        WithConn.Exists.byField tableName field conn
 | 
			
		||||
    
 | 
			
		||||
    /// Retrieve all documents in the given table
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -176,13 +184,13 @@ type SqliteConnectionCSharpExtensions =
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents via a comparison on a JSON field
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline FindByField<'TDoc>(conn, tableName, fieldName, op, value) =
 | 
			
		||||
        WithConn.Find.ByField<'TDoc>(tableName, fieldName, op, value, conn)
 | 
			
		||||
    static member inline FindByField<'TDoc>(conn, tableName, field) =
 | 
			
		||||
        WithConn.Find.ByField<'TDoc>(tableName, field, conn)
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents via a comparison on a JSON field, returning only the first result
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithConn.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, conn)
 | 
			
		||||
    static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
 | 
			
		||||
        WithConn.Find.FirstByField<'TDoc>(tableName, field, conn)
 | 
			
		||||
 | 
			
		||||
    /// Update an entire document by its ID
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
@ -201,9 +209,19 @@ type SqliteConnectionCSharpExtensions =
 | 
			
		||||
    
 | 
			
		||||
    /// Patch documents using a comparison on a JSON field
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline PatchByField<'TPatch>(conn, tableName, fieldName, op, value: obj, patch: 'TPatch) =
 | 
			
		||||
        WithConn.Patch.byField tableName fieldName op value patch conn
 | 
			
		||||
    static member inline PatchByField<'TPatch>(conn, tableName, field, patch: 'TPatch) =
 | 
			
		||||
        WithConn.Patch.byField tableName field patch conn
 | 
			
		||||
 | 
			
		||||
    /// Remove fields from a document by the document's ID
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline RemoveFieldsById<'TKey>(conn, tableName, docId: 'TKey, fieldNames) =
 | 
			
		||||
        WithConn.RemoveFields.ById(tableName, docId, fieldNames, conn)
 | 
			
		||||
        
 | 
			
		||||
    /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
 | 
			
		||||
        WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Delete a document by its ID
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) =
 | 
			
		||||
@ -211,5 +229,5 @@ type SqliteConnectionCSharpExtensions =
 | 
			
		||||
 | 
			
		||||
    /// Delete documents by matching a comparison on a JSON field
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline DeleteByField(conn, tableName, fieldName, op, value: obj) =
 | 
			
		||||
        WithConn.Delete.byField tableName fieldName op value conn
 | 
			
		||||
    static member inline DeleteByField(conn, tableName, field) =
 | 
			
		||||
        WithConn.Delete.byField tableName field conn
 | 
			
		||||
 | 
			
		||||
@ -12,7 +12,7 @@ module Configuration =
 | 
			
		||||
    /// Register a connection string to use for query execution (enables foreign keys)
 | 
			
		||||
    [<CompiledName "UseConnectionString">]
 | 
			
		||||
    let useConnectionString connStr =
 | 
			
		||||
        let builder = SqliteConnectionStringBuilder(connStr)
 | 
			
		||||
        let builder = SqliteConnectionStringBuilder connStr
 | 
			
		||||
        builder.ForeignKeys <- Option.toNullable (Some true)
 | 
			
		||||
        connectionString <- Some (string builder)
 | 
			
		||||
    
 | 
			
		||||
@ -42,17 +42,45 @@ module Query =
 | 
			
		||||
    /// Document patching (partial update) queries
 | 
			
		||||
    module Patch =
 | 
			
		||||
        
 | 
			
		||||
        /// Create an UPDATE statement to patch documents
 | 
			
		||||
        let internal update tableName whereClause =
 | 
			
		||||
            $"UPDATE %s{tableName} SET data = json_patch(data, json(@data)) WHERE %s{whereClause}"
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch (partially update) a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""UPDATE %s{tableName} SET data = json_patch(data, json(@data)) WHERE {Query.whereById "@id"}"""
 | 
			
		||||
            Query.whereById "@id" |> update tableName
 | 
			
		||||
            
 | 
			
		||||
        /// Query to patch (partially update) a document via a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op =
 | 
			
		||||
            sprintf
 | 
			
		||||
                "UPDATE %s SET data = json_patch(data, json(@data)) WHERE %s"
 | 
			
		||||
                tableName (Query.whereByField fieldName op "@field")
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            Query.whereByField field "@field" |> update tableName
 | 
			
		||||
    
 | 
			
		||||
    /// Queries to remove fields from documents
 | 
			
		||||
    module RemoveFields =
 | 
			
		||||
        
 | 
			
		||||
        /// Create an UPDATE statement to remove parameters
 | 
			
		||||
        let internal update tableName (parameters: SqliteParameter list) whereClause =
 | 
			
		||||
            let paramNames = parameters |> List.map _.ParameterName |> String.concat ", "
 | 
			
		||||
            $"UPDATE %s{tableName} SET data = json_remove(data, {paramNames}) WHERE {whereClause}"
 | 
			
		||||
            
 | 
			
		||||
        /// Query to remove fields from a document by the document's ID
 | 
			
		||||
        [<CompiledName "FSharpById">]
 | 
			
		||||
        let byId tableName parameters =
 | 
			
		||||
            Query.whereById "@id" |> update tableName parameters
 | 
			
		||||
        
 | 
			
		||||
        /// Query to remove fields from a document by the document's ID
 | 
			
		||||
        let ById(tableName, parameters) =
 | 
			
		||||
            byId tableName (List.ofSeq parameters)
 | 
			
		||||
        
 | 
			
		||||
        /// Query to remove fields from documents via a comparison on a JSON field within the document
 | 
			
		||||
        [<CompiledName "FSharpByField">]
 | 
			
		||||
        let byField tableName field parameters =
 | 
			
		||||
            Query.whereByField field "@field" |> update tableName parameters
 | 
			
		||||
        
 | 
			
		||||
        /// Query to remove fields from documents via a comparison on a JSON field within the document
 | 
			
		||||
        let ByField(tableName, field, parameters) =
 | 
			
		||||
            byField tableName field (List.ofSeq parameters)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Parameter handling helpers
 | 
			
		||||
@ -70,9 +98,24 @@ module Parameters =
 | 
			
		||||
        SqliteParameter(name, Configuration.serializer().Serialize it)
 | 
			
		||||
 | 
			
		||||
    /// Create a JSON field parameter (name "@field")
 | 
			
		||||
    [<CompiledName "Field">]
 | 
			
		||||
    let fieldParam (value: obj) =
 | 
			
		||||
        SqliteParameter("@field", value)
 | 
			
		||||
    [<CompiledName "FSharpAddField">]
 | 
			
		||||
    let addFieldParam name field parameters =
 | 
			
		||||
        match field.Op with EX | NEX -> parameters | _ -> SqliteParameter(name, field.Value) :: parameters
 | 
			
		||||
    
 | 
			
		||||
    /// Create a JSON field parameter (name "@field")
 | 
			
		||||
    let AddField(name, field, parameters) =
 | 
			
		||||
        match field.Op with
 | 
			
		||||
        | EX | NEX -> parameters
 | 
			
		||||
        | _ -> SqliteParameter(name, field.Value) |> Seq.singleton |> Seq.append parameters
 | 
			
		||||
 | 
			
		||||
    /// Append JSON field name parameters for the given field names to the given parameters
 | 
			
		||||
    [<CompiledName "FSharpFieldNames">]
 | 
			
		||||
    let fieldNameParams paramName (fieldNames: string list) =
 | 
			
		||||
        fieldNames |> List.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}"))
 | 
			
		||||
 | 
			
		||||
    /// Append JSON field name parameters for the given field names to the given parameters
 | 
			
		||||
    let FieldNames(paramName, fieldNames: string seq) =
 | 
			
		||||
        fieldNames |> Seq.mapi (fun idx name -> SqliteParameter($"%s{paramName}{idx}", $"$.{name}"))
 | 
			
		||||
 | 
			
		||||
    /// An empty parameter sequence
 | 
			
		||||
    [<CompiledName "None">]
 | 
			
		||||
@ -223,8 +266,8 @@ module WithConn =
 | 
			
		||||
        
 | 
			
		||||
        /// Count matching documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) conn =
 | 
			
		||||
            Custom.scalar (Query.Count.byField tableName fieldName op) [  fieldParam value ] toCount conn
 | 
			
		||||
        let byField tableName field conn =
 | 
			
		||||
            Custom.scalar (Query.Count.byField tableName field) (addFieldParam "@field" field []) toCount conn
 | 
			
		||||
 | 
			
		||||
    /// Commands to determine if documents exist
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
@ -237,8 +280,8 @@ module WithConn =
 | 
			
		||||
 | 
			
		||||
        /// Determine if a document exists using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) conn =
 | 
			
		||||
            Custom.scalar (Query.Exists.byField tableName fieldName op) [ fieldParam value ] toExists conn
 | 
			
		||||
        let byField tableName field conn =
 | 
			
		||||
            Custom.scalar (Query.Exists.byField tableName field) (addFieldParam "@field" field []) toExists conn
 | 
			
		||||
    
 | 
			
		||||
    /// Commands to retrieve documents
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
@ -264,23 +307,25 @@ module WithConn =
 | 
			
		||||
 | 
			
		||||
        /// Retrieve documents via a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "FSharpByField">]
 | 
			
		||||
        let byField<'TDoc> tableName fieldName op (value: obj) conn =
 | 
			
		||||
            Custom.list<'TDoc> (Query.Find.byField tableName fieldName op) [ fieldParam value ] fromData<'TDoc> conn 
 | 
			
		||||
        let byField<'TDoc> tableName field conn =
 | 
			
		||||
            Custom.list<'TDoc>
 | 
			
		||||
                (Query.Find.byField tableName field) (addFieldParam "@field" field []) fromData<'TDoc> conn 
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve documents via a comparison on a JSON field
 | 
			
		||||
        let ByField<'TDoc>(tableName, fieldName, op, value: obj, conn) =
 | 
			
		||||
            Custom.List<'TDoc>(Query.Find.byField tableName fieldName op, [ fieldParam value ], fromData<'TDoc>, conn) 
 | 
			
		||||
        let ByField<'TDoc>(tableName, field, conn) =
 | 
			
		||||
            Custom.List<'TDoc>(
 | 
			
		||||
                Query.Find.byField tableName field, addFieldParam "@field" field [], fromData<'TDoc>, conn) 
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve documents via a comparison on a JSON field, returning only the first result
 | 
			
		||||
        [<CompiledName "FSharpFirstByField">]
 | 
			
		||||
        let firstByField<'TDoc> tableName fieldName op (value: obj) conn =
 | 
			
		||||
        let firstByField<'TDoc> tableName field conn =
 | 
			
		||||
            Custom.single
 | 
			
		||||
                $"{Query.Find.byField tableName fieldName op} LIMIT 1" [ fieldParam value ] fromData<'TDoc> conn
 | 
			
		||||
                $"{Query.Find.byField tableName field} LIMIT 1" (addFieldParam "@field" field []) fromData<'TDoc> conn
 | 
			
		||||
        
 | 
			
		||||
        /// Retrieve documents via a comparison on a JSON field, returning only the first result
 | 
			
		||||
        let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value: obj, conn) =
 | 
			
		||||
        let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
 | 
			
		||||
            Custom.Single(
 | 
			
		||||
                $"{Query.Find.byField tableName fieldName op} LIMIT 1", [ fieldParam value ], fromData<'TDoc>, conn)
 | 
			
		||||
                $"{Query.Find.byField tableName field} LIMIT 1", addFieldParam "@field" field [], fromData<'TDoc>, conn)
 | 
			
		||||
    
 | 
			
		||||
    /// Commands to update documents
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
@ -311,10 +356,35 @@ module WithConn =
 | 
			
		||||
        
 | 
			
		||||
        /// Patch documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) (patch: 'TPatch) (conn: SqliteConnection) =
 | 
			
		||||
        let byField tableName field (patch: 'TPatch) (conn: SqliteConnection) =
 | 
			
		||||
            Custom.nonQuery
 | 
			
		||||
                (Query.Patch.byField tableName fieldName op) [ fieldParam value; jsonParam "@data" patch ] conn
 | 
			
		||||
                (Query.Patch.byField tableName field) (addFieldParam "@field" field [ jsonParam "@data" patch ]) conn
 | 
			
		||||
 | 
			
		||||
    /// Commands to remove fields from documents
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
    module RemoveFields =
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from a document by the document's ID
 | 
			
		||||
        [<CompiledName "FSharpById">]
 | 
			
		||||
        let byId tableName (docId: 'TKey) fieldNames conn =
 | 
			
		||||
            let nameParams = fieldNameParams "@name" fieldNames
 | 
			
		||||
            Custom.nonQuery (Query.RemoveFields.byId tableName nameParams) (idParam docId :: nameParams) conn
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from a document by the document's ID
 | 
			
		||||
        let ById(tableName, docId: 'TKey, fieldNames, conn) =
 | 
			
		||||
            byId tableName docId (List.ofSeq fieldNames) conn
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
        [<CompiledName "FSharpByField">]
 | 
			
		||||
        let byField tableName field fieldNames conn =
 | 
			
		||||
            let nameParams = fieldNameParams "@name" fieldNames
 | 
			
		||||
            Custom.nonQuery
 | 
			
		||||
                (Query.RemoveFields.byField tableName field nameParams) (addFieldParam "@field" field nameParams) conn
 | 
			
		||||
        
 | 
			
		||||
        /// Remove fields from documents via a comparison on a JSON field in the document
 | 
			
		||||
        let ByField(tableName, field, fieldNames, conn) =
 | 
			
		||||
            byField tableName field (List.ofSeq fieldNames) conn
 | 
			
		||||
    
 | 
			
		||||
    /// Commands to delete documents
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
    module Delete =
 | 
			
		||||
@ -326,8 +396,8 @@ module WithConn =
 | 
			
		||||
 | 
			
		||||
        /// Delete documents by matching a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName fieldName op (value: obj) conn =
 | 
			
		||||
            Custom.nonQuery (Query.Delete.byField tableName fieldName op) [ fieldParam value ] conn
 | 
			
		||||
        let byField tableName field conn =
 | 
			
		||||
            Custom.nonQuery (Query.Delete.byField tableName field) (addFieldParam "@field" field []) conn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Commands to execute custom SQL queries
 | 
			
		||||
@ -417,9 +487,9 @@ module Count =
 | 
			
		||||
    
 | 
			
		||||
    /// Count matching documents using a comparison on a JSON field
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) =
 | 
			
		||||
    let byField tableName field =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Count.byField tableName fieldName op value conn
 | 
			
		||||
        WithConn.Count.byField tableName field conn
 | 
			
		||||
 | 
			
		||||
/// Commands to determine if documents exist
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
@ -433,9 +503,9 @@ module Exists =
 | 
			
		||||
 | 
			
		||||
    /// Determine if a document exists using a comparison on a JSON field
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) =
 | 
			
		||||
    let byField tableName field =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Exists.byField tableName fieldName op value conn
 | 
			
		||||
        WithConn.Exists.byField tableName field conn
 | 
			
		||||
 | 
			
		||||
/// Commands to determine if documents exist
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
@ -465,25 +535,25 @@ module Find =
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents via a comparison on a JSON field
 | 
			
		||||
    [<CompiledName "FSharpByField">]
 | 
			
		||||
    let byField<'TDoc> tableName fieldName op value =
 | 
			
		||||
    let byField<'TDoc> tableName field =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Find.byField<'TDoc> tableName fieldName op value conn
 | 
			
		||||
        WithConn.Find.byField<'TDoc> tableName field conn
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents via a comparison on a JSON field
 | 
			
		||||
    let ByField<'TDoc>(tableName, fieldName, op, value) =
 | 
			
		||||
    let ByField<'TDoc>(tableName, field) =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Find.ByField<'TDoc>(tableName, fieldName, op, value, conn)
 | 
			
		||||
        WithConn.Find.ByField<'TDoc>(tableName, field, conn)
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents via a comparison on a JSON field, returning only the first result
 | 
			
		||||
    [<CompiledName "FSharpFirstByField">]
 | 
			
		||||
    let firstByField<'TDoc> tableName fieldName op value =
 | 
			
		||||
    let firstByField<'TDoc> tableName field =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Find.firstByField<'TDoc> tableName fieldName op value conn
 | 
			
		||||
        WithConn.Find.firstByField<'TDoc> tableName field conn
 | 
			
		||||
 | 
			
		||||
    /// Retrieve documents via a comparison on a JSON field, returning only the first result
 | 
			
		||||
    let FirstByField<'TDoc when 'TDoc: null>(tableName, fieldName, op, value) =
 | 
			
		||||
    let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Find.FirstByField<'TDoc>(tableName, fieldName, op, value, conn)
 | 
			
		||||
        WithConn.Find.FirstByField<'TDoc>(tableName, field, conn)
 | 
			
		||||
 | 
			
		||||
/// Commands to update documents
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
@ -518,9 +588,35 @@ module Patch =
 | 
			
		||||
    
 | 
			
		||||
    /// Patch documents using a comparison on a JSON field in the WHERE clause
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) (patch: 'TPatch) =
 | 
			
		||||
    let byField tableName field (patch: 'TPatch) =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Patch.byField tableName fieldName op value patch conn
 | 
			
		||||
        WithConn.Patch.byField tableName field patch conn
 | 
			
		||||
 | 
			
		||||
/// Commands to remove fields from documents
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
module RemoveFields =
 | 
			
		||||
    
 | 
			
		||||
    /// Remove fields from a document by the document's ID
 | 
			
		||||
    [<CompiledName "FSharpById">]
 | 
			
		||||
    let byId tableName (docId: 'TKey) fieldNames =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.RemoveFields.byId tableName docId fieldNames conn
 | 
			
		||||
        
 | 
			
		||||
    /// Remove fields from a document by the document's ID
 | 
			
		||||
    let ById(tableName, docId: 'TKey, fieldNames) =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.RemoveFields.ById(tableName, docId, fieldNames, conn)
 | 
			
		||||
        
 | 
			
		||||
    /// Remove field from documents via a comparison on a JSON field in the document
 | 
			
		||||
    [<CompiledName "FSharpByField">]
 | 
			
		||||
    let byField tableName field fieldNames =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.RemoveFields.byField tableName field fieldNames conn
 | 
			
		||||
 | 
			
		||||
    /// Remove field from documents via a comparison on a JSON field in the document
 | 
			
		||||
    let ByField(tableName, field, fieldNames) =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.RemoveFields.ByField(tableName, field, fieldNames, conn)
 | 
			
		||||
 | 
			
		||||
/// Commands to delete documents
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
@ -534,6 +630,6 @@ module Delete =
 | 
			
		||||
 | 
			
		||||
    /// Delete documents by matching a comparison on a JSON field
 | 
			
		||||
    [<CompiledName "ByField">]
 | 
			
		||||
    let byField tableName fieldName op (value: obj) =
 | 
			
		||||
    let byField tableName field =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.Delete.byField tableName fieldName op value conn
 | 
			
		||||
        WithConn.Delete.byField tableName field conn
 | 
			
		||||
 | 
			
		||||
@ -105,6 +105,63 @@ public static class CommonCSharpTests
 | 
			
		||||
                Expect.equal(Op.NEX.ToString(), "IS NULL", "The \"not exists\" operator was not correct");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("Field", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("EQ succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.EQ("Test", 14);
 | 
			
		||||
                Expect.equal(field.Name, "Test", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.EQ, "Operator incorrect");
 | 
			
		||||
                Expect.equal(field.Value, 14, "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("GT succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.GT("Great", "night");
 | 
			
		||||
                Expect.equal(field.Name, "Great", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.GT, "Operator incorrect");
 | 
			
		||||
                Expect.equal(field.Value, "night", "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("GE succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.GE("Nice", 88L);
 | 
			
		||||
                Expect.equal(field.Name, "Nice", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.GE, "Operator incorrect");
 | 
			
		||||
                Expect.equal(field.Value, 88L, "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("LT succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.LT("Lesser", "seven");
 | 
			
		||||
                Expect.equal(field.Name, "Lesser", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.LT, "Operator incorrect");
 | 
			
		||||
                Expect.equal(field.Value, "seven", "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("LE succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.LE("Nobody", "KNOWS");
 | 
			
		||||
                Expect.equal(field.Name, "Nobody", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.LE, "Operator incorrect");
 | 
			
		||||
                Expect.equal(field.Value, "KNOWS", "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("NE succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.NE("Park", "here");
 | 
			
		||||
                Expect.equal(field.Name, "Park", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.NE, "Operator incorrect");
 | 
			
		||||
                Expect.equal(field.Value, "here", "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("EX succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.EX("Groovy");
 | 
			
		||||
                Expect.equal(field.Name, "Groovy", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.EX, "Operator incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("NEX succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.NEX("Rad");
 | 
			
		||||
                Expect.equal(field.Name, "Rad", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.NEX, "Operator incorrect");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("Query", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("SelectFromTable succeeds", () =>
 | 
			
		||||
@ -120,12 +177,12 @@ public static class CommonCSharpTests
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when a logical operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.WhereByField("theField", Op.GT, "@test"), "data ->> 'theField' > @test",
 | 
			
		||||
                    Expect.equal(Query.WhereByField(Field.GT("theField", 0), "@test"), "data ->> 'theField' > @test",
 | 
			
		||||
                        "WHERE clause not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when an existence operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.WhereByField("thatField", Op.NEX, ""), "data ->> 'thatField' IS NULL",
 | 
			
		||||
                    Expect.equal(Query.WhereByField(Field.NEX("thatField"), ""), "data ->> 'thatField' IS NULL",
 | 
			
		||||
                        "WHERE clause not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
@ -185,7 +242,7 @@ public static class CommonCSharpTests
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Count.ByField("tbl", "thatField", Op.EQ),
 | 
			
		||||
                    Expect.equal(Query.Count.ByField("tbl", Field.EQ("thatField", 0)),
 | 
			
		||||
                        "SELECT COUNT(*) AS it FROM tbl WHERE data ->> 'thatField' = @field",
 | 
			
		||||
                        "JSON field text comparison count query not correct");
 | 
			
		||||
                })
 | 
			
		||||
@ -200,7 +257,7 @@ public static class CommonCSharpTests
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Exists.ByField("tbl", "Test", Op.LT),
 | 
			
		||||
                    Expect.equal(Query.Exists.ByField("tbl", Field.LT("Test", 0)),
 | 
			
		||||
                        "SELECT EXISTS (SELECT 1 FROM tbl WHERE data ->> 'Test' < @field) AS it",
 | 
			
		||||
                        "JSON field text comparison exists query not correct");
 | 
			
		||||
                })
 | 
			
		||||
@ -214,7 +271,7 @@ public static class CommonCSharpTests
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Find.ByField("tbl", "Golf", Op.GE),
 | 
			
		||||
                    Expect.equal(Query.Find.ByField("tbl", Field.GE("Golf", 0)),
 | 
			
		||||
                        "SELECT data FROM tbl WHERE data ->> 'Golf' >= @field",
 | 
			
		||||
                        "SELECT by JSON comparison query not correct");
 | 
			
		||||
                })
 | 
			
		||||
@ -228,7 +285,7 @@ public static class CommonCSharpTests
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Delete.ByField("tbl", "gone", Op.NEX),
 | 
			
		||||
                    Expect.equal(Query.Delete.ByField("tbl", Field.NEX("gone")),
 | 
			
		||||
                        "DELETE FROM tbl WHERE data ->> 'gone' IS NULL",
 | 
			
		||||
                        "DELETE by JSON comparison query not correct");
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
@ -246,7 +246,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
            await using var conn = MkConn(db);
 | 
			
		||||
            await LoadDocs();
 | 
			
		||||
 | 
			
		||||
            var theCount = await conn.CountByField(PostgresDb.TableName, "Value", Op.EQ, "purple");
 | 
			
		||||
            var theCount = await conn.CountByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
            Expect.equal(theCount, 2, "There should have been 2 matching documents");
 | 
			
		||||
        }),
 | 
			
		||||
        TestCase("CountByContains succeeds", async () =>
 | 
			
		||||
@ -296,7 +296,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var exists = await conn.ExistsByField(PostgresDb.TableName, "Sub", Op.EX, "");
 | 
			
		||||
                var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EX("Sub"));
 | 
			
		||||
                Expect.isTrue(exists, "There should have been existing documents");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when documents do not exist", async () =>
 | 
			
		||||
@ -305,7 +305,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var exists = await conn.ExistsByField(PostgresDb.TableName, "NumValue", Op.EQ, "six");
 | 
			
		||||
                var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EQ("NumValue", "six"));
 | 
			
		||||
                Expect.isFalse(exists, "There should not have been existing documents");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -403,7 +403,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "another");
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
 | 
			
		||||
                Expect.equal(docs.Count, 1, "There should have been one document returned");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when documents are not found", async () =>
 | 
			
		||||
@ -412,7 +412,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "mauve");
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "mauve"));
 | 
			
		||||
                Expect.isEmpty(docs, "There should have been no documents returned");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -467,7 +467,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "another");
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
 | 
			
		||||
                Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                Expect.equal(doc.Id, "two", "The incorrect document was returned");
 | 
			
		||||
            }),
 | 
			
		||||
@ -477,7 +477,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "purple");
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
                Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                Expect.contains(new[] { "five", "four" }, doc.Id, "An incorrect document was returned");
 | 
			
		||||
            }),
 | 
			
		||||
@ -487,7 +487,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "absent");
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "absent"));
 | 
			
		||||
                Expect.isNull(doc, "There should not have been a document returned");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -650,8 +650,8 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.PatchByField(PostgresDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 });
 | 
			
		||||
                var after = await conn.CountByField(PostgresDb.TableName, "NumValue", Op.EQ, "77");
 | 
			
		||||
                await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
 | 
			
		||||
                var after = await conn.CountByField(PostgresDb.TableName, Field.EQ("NumValue", "77"));
 | 
			
		||||
                Expect.equal(after, 2, "There should have been 2 documents returned");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is updated", async () =>
 | 
			
		||||
@ -662,7 +662,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                Expect.equal(before, 0, "There should have been no documents returned");
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.PatchByField(PostgresDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" });
 | 
			
		||||
                await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("PatchByContains", new[]
 | 
			
		||||
@ -711,6 +711,188 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await conn.PatchByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFieldsById", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub", "Value" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Value" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFieldsByField", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"),
 | 
			
		||||
                    new[] { "Sub", "Value" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" });
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"),
 | 
			
		||||
                    new[] { "Value" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFieldsByContains", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 },
 | 
			
		||||
                    new[] { "Sub", "Value" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" });
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" },
 | 
			
		||||
                    new[] { "Value" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFieldsByJsonPath", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)",
 | 
			
		||||
                    new[] { "Sub", "Value" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" });
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")",
 | 
			
		||||
                    new[] { "Value" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("DeleteById", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("succeeds when a document is deleted", async () =>
 | 
			
		||||
@ -742,7 +924,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.DeleteByField(PostgresDb.TableName, "Value", Op.NE, "purple");
 | 
			
		||||
                await conn.DeleteByField(PostgresDb.TableName, Field.NE("Value", "purple"));
 | 
			
		||||
                var remaining = await conn.CountAll(PostgresDb.TableName);
 | 
			
		||||
                Expect.equal(remaining, 2, "There should have been 2 documents remaining");
 | 
			
		||||
            }),
 | 
			
		||||
@ -752,7 +934,7 @@ public class PostgresCSharpExtensionTests
 | 
			
		||||
                await using var conn = MkConn(db);
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.DeleteByField(PostgresDb.TableName, "Value", Op.EQ, "crimson");
 | 
			
		||||
                await conn.DeleteByField(PostgresDb.TableName, Field.EQ("Value", "crimson"));
 | 
			
		||||
                var remaining = await conn.CountAll(PostgresDb.TableName);
 | 
			
		||||
                Expect.equal(remaining, 5, "There should have been 5 documents remaining");
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
@ -11,7 +11,7 @@ using static Runner;
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// C# tests for the PostgreSQL implementation of <tt>BitBadger.Documents</tt>
 | 
			
		||||
/// </summary>
 | 
			
		||||
public class PostgresCSharpTests
 | 
			
		||||
public static class PostgresCSharpTests
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Tests which do not hit the database
 | 
			
		||||
@ -32,11 +32,49 @@ public class PostgresCSharpTests
 | 
			
		||||
                Expect.equal(it.Item1, "@test", "JSON parameter not constructed correctly");
 | 
			
		||||
                Expect.equal(it.Item2, Sql.jsonb("{\"Something\":\"good\"}"), "JSON parameter value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Field succeeds", () =>
 | 
			
		||||
            TestList("AddField", new []
 | 
			
		||||
            {
 | 
			
		||||
                var it = Parameters.Field(242);
 | 
			
		||||
                Expect.equal(it.Item1, "@field", "Field parameter not constructed correctly");
 | 
			
		||||
                Expect.isTrue(it.Item2.IsParameter, "Field parameter value incorrect");
 | 
			
		||||
                TestCase("succeeds when a parameter is added", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    var it = Parameters
 | 
			
		||||
                        .AddField("@field", Field.EQ("it", "242"), Enumerable.Empty<Tuple<string, SqlValue>>())
 | 
			
		||||
                        .ToList();
 | 
			
		||||
                    Expect.hasLength(it, 1, "There should have been a parameter added");
 | 
			
		||||
                    Expect.equal(it[0].Item1, "@field", "Field parameter not constructed correctly");
 | 
			
		||||
                    Expect.isTrue(it[0].Item2.IsParameter, "Field parameter value incorrect");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a parameter is not added", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    var it = Parameters.AddField("@it", Field.EX("It"), Enumerable.Empty<Tuple<string, SqlValue>>());
 | 
			
		||||
                    Expect.isEmpty(it, "There should not have been any parameters added");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("RemoveFields", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ById("tbl"),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data ->> 'Id' = @id",
 | 
			
		||||
                        "Remove field by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0)),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data ->> 'Fly' < @field",
 | 
			
		||||
                        "Remove field by field query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByContains succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByContains("tbl"),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data @> @criteria",
 | 
			
		||||
                        "Remove field by contains query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByJsonPath succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByJsonPath("tbl"),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data @? @path::jsonpath",
 | 
			
		||||
                        "Remove field by JSON path query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("None succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
@ -134,7 +172,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Patch.ByField(PostgresDb.TableName, "Snail", Op.LT),
 | 
			
		||||
                    Expect.equal(Postgres.Query.Patch.ByField(PostgresDb.TableName, Field.LT("Snail", 0)),
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Snail' < @field",
 | 
			
		||||
                        "UPDATE partial by ID statement not correct");
 | 
			
		||||
                }),
 | 
			
		||||
@ -431,7 +469,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var theCount = await Count.ByField(PostgresDb.TableName, "Value", Op.EQ, "purple");
 | 
			
		||||
                var theCount = await Count.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
                Expect.equal(theCount, 2, "There should have been 2 matching documents");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("ByContains succeeds", async () =>
 | 
			
		||||
@ -479,7 +517,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var exists = await Exists.ByField(PostgresDb.TableName, "Sub", Op.NEX, "");
 | 
			
		||||
                    var exists = await Exists.ByField(PostgresDb.TableName, Field.NEX("Sub"));
 | 
			
		||||
                    Expect.isTrue(exists, "There should have been existing documents");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when documents do not exist", async () =>
 | 
			
		||||
@ -487,7 +525,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var exists = await Exists.ByField(PostgresDb.TableName, "NumValue", Op.EQ, "six");
 | 
			
		||||
                    var exists = await Exists.ByField(PostgresDb.TableName, Field.EQ("NumValue", "six"));
 | 
			
		||||
                    Expect.isFalse(exists, "There should not have been existing documents");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
@ -578,7 +616,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "another");
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
 | 
			
		||||
                    Expect.equal(docs.Count, 1, "There should have been one document returned");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when documents are not found", async () =>
 | 
			
		||||
@ -586,7 +624,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "mauve");
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "mauve"));
 | 
			
		||||
                    Expect.isEmpty(docs, "There should have been no documents returned");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
@ -636,7 +674,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "another");
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
 | 
			
		||||
                    Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                    Expect.equal(doc.Id, "two", "The incorrect document was returned");
 | 
			
		||||
                }),
 | 
			
		||||
@ -645,7 +683,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "purple");
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
                    Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                    Expect.contains(new[] { "five", "four" }, doc.Id, "An incorrect document was returned");
 | 
			
		||||
                }),
 | 
			
		||||
@ -654,7 +692,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, "Value", Op.EQ, "absent");
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "absent"));
 | 
			
		||||
                    Expect.isNull(doc, "There should not have been a document returned");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
@ -813,8 +851,8 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await Patch.ByField(PostgresDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 });
 | 
			
		||||
                    var after = await Count.ByField(PostgresDb.TableName, "NumValue", Op.EQ, "77");
 | 
			
		||||
                    await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
 | 
			
		||||
                    var after = await Count.ByField(PostgresDb.TableName, Field.EQ("NumValue", "77"));
 | 
			
		||||
                    Expect.equal(after, 2, "There should have been 2 documents returned");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is updated", async () =>
 | 
			
		||||
@ -825,7 +863,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    Expect.equal(before, 0, "There should have been no documents returned");
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await Patch.ByField(PostgresDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" });
 | 
			
		||||
                    await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("ByContains", new[]
 | 
			
		||||
@ -873,6 +911,175 @@ public class PostgresCSharpTests
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFields", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestList("ById", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Sub", "Value" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Sub" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Value" });
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("ByField", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"),
 | 
			
		||||
                        new[] { "Sub", "Value" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" });
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"),
 | 
			
		||||
                        new[] { "Value" });
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("ByContains", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 },
 | 
			
		||||
                        new[] { "Sub", "Value" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" });
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" },
 | 
			
		||||
                        new[] { "Value" });
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("ByJsonPath", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when multiple fields are removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)",
 | 
			
		||||
                        new[] { "Sub", "Value" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a single field is removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.notEqual(updated.Value, "", "The string value should not have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" });
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")",
 | 
			
		||||
                        new[] { "Value" });
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("Delete", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestList("ById", new[]
 | 
			
		||||
@ -903,7 +1110,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await Delete.ByField(PostgresDb.TableName, "Value", Op.EQ, "purple");
 | 
			
		||||
                    await Delete.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
                    var remaining = await Count.All(PostgresDb.TableName);
 | 
			
		||||
                    Expect.equal(remaining, 3, "There should have been 3 documents remaining");
 | 
			
		||||
                }),
 | 
			
		||||
@ -912,7 +1119,7 @@ public class PostgresCSharpTests
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await Delete.ByField(PostgresDb.TableName, "Value", Op.EQ, "crimson");
 | 
			
		||||
                    await Delete.ByField(PostgresDb.TableName, Field.EQ("Value", "crimson"));
 | 
			
		||||
                    var remaining = await Count.All(PostgresDb.TableName);
 | 
			
		||||
                    Expect.equal(remaining, 5, "There should have been 5 documents remaining");
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
@ -219,7 +219,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
            await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
            await LoadDocs();
 | 
			
		||||
 | 
			
		||||
            var theCount = await conn.CountByField(SqliteDb.TableName, "Value", Op.EQ, "purple");
 | 
			
		||||
            var theCount = await conn.CountByField(SqliteDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
            Expect.equal(theCount, 2L, "There should have been 2 matching documents");
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("ExistsById", new[]
 | 
			
		||||
@ -251,7 +251,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var exists = await conn.ExistsByField(SqliteDb.TableName, "NumValue", Op.GE, 10);
 | 
			
		||||
                var exists = await conn.ExistsByField(SqliteDb.TableName, Field.GE("NumValue", 10));
 | 
			
		||||
                Expect.isTrue(exists, "There should have been existing documents");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no matching documents exist", async () =>
 | 
			
		||||
@ -260,7 +260,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var exists = await conn.ExistsByField(SqliteDb.TableName, "Nothing", Op.EQ, "none");
 | 
			
		||||
                var exists = await conn.ExistsByField(SqliteDb.TableName, Field.EQ("Nothing", "none"));
 | 
			
		||||
                Expect.isFalse(exists, "There should not have been any existing documents");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -316,7 +316,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, "NumValue", Op.GT, 15);
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, Field.GT("NumValue", 15));
 | 
			
		||||
                Expect.equal(docs.Count, 2, "There should have been two documents returned");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when documents are not found", async () =>
 | 
			
		||||
@ -325,7 +325,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, "Value", Op.EQ, "mauve");
 | 
			
		||||
                var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve"));
 | 
			
		||||
                Expect.isEmpty(docs, "There should have been no documents returned");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -337,7 +337,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, "Value", Op.EQ, "another");
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "another"));
 | 
			
		||||
                Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                Expect.equal(doc!.Id, "two", "The incorrect document was returned");
 | 
			
		||||
            }),
 | 
			
		||||
@ -347,7 +347,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, "Sub.Foo", Op.EQ, "green");
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Sub.Foo", "green"));
 | 
			
		||||
                Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned");
 | 
			
		||||
            }),
 | 
			
		||||
@ -357,7 +357,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, "Value", Op.EQ, "absent");
 | 
			
		||||
                var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent"));
 | 
			
		||||
                Expect.isNull(doc, "There should not have been a document returned");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -452,8 +452,8 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.PatchByField(SqliteDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 });
 | 
			
		||||
                var after = await conn.CountByField(SqliteDb.TableName, "NumValue", Op.EQ, 77);
 | 
			
		||||
                await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
 | 
			
		||||
                var after = await conn.CountByField(SqliteDb.TableName, Field.EQ("NumValue", 77));
 | 
			
		||||
                Expect.equal(after, 2L, "There should have been 2 documents returned");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is updated", async () =>
 | 
			
		||||
@ -464,7 +464,70 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                Expect.isEmpty(before, "There should have been no documents returned");
 | 
			
		||||
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.PatchByField(SqliteDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" });
 | 
			
		||||
                await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFieldsById", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("succeeds when fields are removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Sub", "Value" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Value" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFieldsByField", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestCase("succeeds when a field is removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" });
 | 
			
		||||
                var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
 | 
			
		||||
                Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" });
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                await conn.RemoveFieldsByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("DeleteById", new[]
 | 
			
		||||
@ -498,7 +561,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.DeleteByField(SqliteDb.TableName, "Value", Op.NE, "purple");
 | 
			
		||||
                await conn.DeleteByField(SqliteDb.TableName, Field.NE("Value", "purple"));
 | 
			
		||||
                var remaining = await conn.CountAll(SqliteDb.TableName);
 | 
			
		||||
                Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
 | 
			
		||||
            }),
 | 
			
		||||
@ -508,7 +571,7 @@ public static class SqliteCSharpExtensionTests
 | 
			
		||||
                await using var conn = Sqlite.Configuration.DbConn();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                await conn.DeleteByField(SqliteDb.TableName, "Value", Op.EQ, "crimson");
 | 
			
		||||
                await conn.DeleteByField(SqliteDb.TableName, Field.EQ("Value", "crimson"));
 | 
			
		||||
                var remaining = await conn.CountAll(SqliteDb.TableName);
 | 
			
		||||
                Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
using Expecto.CSharp;
 | 
			
		||||
using System.Text.Json;
 | 
			
		||||
using Expecto.CSharp;
 | 
			
		||||
using Expecto;
 | 
			
		||||
using Microsoft.Data.Sqlite;
 | 
			
		||||
using Microsoft.FSharp.Core;
 | 
			
		||||
@ -35,11 +36,27 @@ public static class SqliteCSharpTests
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Patch.ByField("tbl", "Part", Op.NE),
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Patch.ByField("tbl", Field.NE("Part", 0)),
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Part' <> @field",
 | 
			
		||||
                        "UPDATE partial by JSON comparison query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("RemoveFields", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", new[] { new SqliteParameter("@name", "one") }),
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Id' = @id",
 | 
			
		||||
                        "Remove field by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0),
 | 
			
		||||
                            new[] { new SqliteParameter("@name0", "one"), new SqliteParameter("@name1", "two") }),
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' < @field",
 | 
			
		||||
                        "Remove field by field query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("Parameters", new[]
 | 
			
		||||
        {
 | 
			
		||||
@ -55,12 +72,20 @@ public static class SqliteCSharpTests
 | 
			
		||||
                Expect.equal(theParam.ParameterName, "@test", "The parameter name is incorrect");
 | 
			
		||||
                Expect.equal(theParam.Value, "{\"Nice\":\"job\"}", "The parameter value is incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Field succeeds", () =>
 | 
			
		||||
            TestCase("AddField succeeds when adding a parameter", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var theParam = Parameters.Field(99);
 | 
			
		||||
                var paramList = Parameters.AddField("@field", Field.EQ("it", 99), Enumerable.Empty<SqliteParameter>())
 | 
			
		||||
                    .ToList();
 | 
			
		||||
                Expect.hasLength(paramList, 1, "There should have been a parameter added");
 | 
			
		||||
                var theParam = paramList[0];
 | 
			
		||||
                Expect.equal(theParam.ParameterName, "@field", "The parameter name is incorrect");
 | 
			
		||||
                Expect.equal(theParam.Value, 99, "The parameter value is incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("AddField succeeds when not adding a parameter", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), Enumerable.Empty<SqliteParameter>());
 | 
			
		||||
                Expect.isEmpty(paramSeq, "There should not have been any parameters added");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("None succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.isEmpty(Parameters.None, "The parameter list should have been empty");
 | 
			
		||||
@ -296,7 +321,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var theCount = await Count.ByField(SqliteDb.TableName, "Value", Op.EQ, "purple");
 | 
			
		||||
                var theCount = await Count.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
                Expect.equal(theCount, 2L, "There should have been 2 matching documents");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -328,7 +353,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var exists = await Exists.ByField(SqliteDb.TableName, "NumValue", Op.GE, 10);
 | 
			
		||||
                    var exists = await Exists.ByField(SqliteDb.TableName, Field.GE("NumValue", 10));
 | 
			
		||||
                    Expect.isTrue(exists, "There should have been existing documents");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no matching documents exist", async () =>
 | 
			
		||||
@ -336,7 +361,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var exists = await Exists.ByField(SqliteDb.TableName, "Nothing", Op.EQ, "none");
 | 
			
		||||
                    var exists = await Exists.ByField(SqliteDb.TableName, Field.EQ("Nothing", "none"));
 | 
			
		||||
                    Expect.isFalse(exists, "There should not have been any existing documents");
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
@ -390,7 +415,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, "NumValue", Op.GT, 15);
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.GT("NumValue", 15));
 | 
			
		||||
                    Expect.equal(docs.Count, 2, "There should have been two documents returned");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when documents are not found", async () =>
 | 
			
		||||
@ -398,7 +423,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, "Value", Op.EQ, "mauve");
 | 
			
		||||
                    var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve"));
 | 
			
		||||
                    Expect.isEmpty(docs, "There should have been no documents returned");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
@ -409,7 +434,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, "Value", Op.EQ, "another");
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "another"));
 | 
			
		||||
                    Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                    Expect.equal(doc!.Id, "two", "The incorrect document was returned");
 | 
			
		||||
                }),
 | 
			
		||||
@ -418,7 +443,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, "Sub.Foo", Op.EQ, "green");
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Sub.Foo", "green"));
 | 
			
		||||
                    Expect.isNotNull(doc, "There should have been a document returned");
 | 
			
		||||
                    Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned");
 | 
			
		||||
                }),
 | 
			
		||||
@ -427,7 +452,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, "Value", Op.EQ, "absent");
 | 
			
		||||
                    var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent"));
 | 
			
		||||
                    Expect.isNull(doc, "There should not have been a document returned");
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
@ -524,8 +549,8 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await Patch.ByField(SqliteDb.TableName, "Value", Op.EQ, "purple", new { NumValue = 77 });
 | 
			
		||||
                    var after = await Count.ByField(SqliteDb.TableName, "NumValue", Op.EQ, 77);
 | 
			
		||||
                    await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
 | 
			
		||||
                    var after = await Count.ByField(SqliteDb.TableName, Field.EQ("NumValue", 77));
 | 
			
		||||
                    Expect.equal(after, 2L, "There should have been 2 documents returned");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is updated", async () =>
 | 
			
		||||
@ -536,7 +561,67 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    Expect.isEmpty(before, "There should have been no documents returned");
 | 
			
		||||
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await Patch.ByField(SqliteDb.TableName, "Value", Op.EQ, "burgundy", new { Foo = "green" });
 | 
			
		||||
                    await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("RemoveFields", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestList("ById", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when fields are removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Sub", "Value" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.equal(updated.Value, "", "The string value should have been removed");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" });
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("ByField", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when a field is removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" });
 | 
			
		||||
                    var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
 | 
			
		||||
                    Expect.isNotNull(updated, "The updated document should have been retrieved");
 | 
			
		||||
                    Expect.isNull(updated.Sub, "The sub-document should have been removed");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a field is not removed", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" });
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when no document is matched", async () =>
 | 
			
		||||
                {
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
@ -570,7 +655,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await Delete.ByField(SqliteDb.TableName, "Value", Op.NE, "purple");
 | 
			
		||||
                    await Delete.ByField(SqliteDb.TableName, Field.NE("Value", "purple"));
 | 
			
		||||
                    var remaining = await Count.All(SqliteDb.TableName);
 | 
			
		||||
                    Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
 | 
			
		||||
                }),
 | 
			
		||||
@ -579,7 +664,7 @@ public static class SqliteCSharpTests
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                    await Delete.ByField(SqliteDb.TableName, "Value", Op.EQ, "crimson");
 | 
			
		||||
                    await Delete.ByField(SqliteDb.TableName, Field.EQ("Value", "crimson"));
 | 
			
		||||
                    var remaining = await Count.All(SqliteDb.TableName);
 | 
			
		||||
                    Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
 | 
			
		||||
                })
 | 
			
		||||
 | 
			
		||||
@ -45,13 +45,13 @@ let all =
 | 
			
		||||
            testList "whereByField" [
 | 
			
		||||
                test "succeeds when a logical operator is passed" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.whereByField "theField" GT "@test")
 | 
			
		||||
                        (Query.whereByField (Field.GT "theField" 0) "@test")
 | 
			
		||||
                        "data ->> 'theField' > @test"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when an existence operator is passed" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.whereByField "thatField" NEX "")
 | 
			
		||||
                        (Query.whereByField (Field.NEX "thatField") "")
 | 
			
		||||
                        "data ->> 'thatField' IS NULL"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
@ -107,7 +107,7 @@ let all =
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Count.byField tbl "thatField" EQ)
 | 
			
		||||
                        (Query.Count.byField tbl (Field.EQ "thatField" 0))
 | 
			
		||||
                        $"SELECT COUNT(*) AS it FROM {tbl} WHERE data ->> 'thatField' = @field"
 | 
			
		||||
                        "JSON field text comparison count query not correct"
 | 
			
		||||
                }
 | 
			
		||||
@ -121,7 +121,7 @@ let all =
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Exists.byField tbl "Test" LT)
 | 
			
		||||
                        (Query.Exists.byField tbl (Field.LT "Test" 0))
 | 
			
		||||
                        $"SELECT EXISTS (SELECT 1 FROM {tbl} WHERE data ->> 'Test' < @field) AS it"
 | 
			
		||||
                        "JSON field text comparison exists query not correct"
 | 
			
		||||
                }
 | 
			
		||||
@ -135,7 +135,7 @@ let all =
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Find.byField tbl "Golf" GE)
 | 
			
		||||
                        (Query.Find.byField tbl (Field.GE "Golf" 0))
 | 
			
		||||
                        $"SELECT data FROM {tbl} WHERE data ->> 'Golf' >= @field"
 | 
			
		||||
                        "SELECT by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
@ -149,7 +149,7 @@ let all =
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Delete.byField tbl "gone" NEX)
 | 
			
		||||
                        (Query.Delete.byField tbl (Field.NEX "gone"))
 | 
			
		||||
                        $"DELETE FROM {tbl} WHERE data ->> 'gone' IS NULL"
 | 
			
		||||
                        "DELETE by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -214,7 +214,7 @@ let integrationTests =
 | 
			
		||||
            use conn = mkConn db
 | 
			
		||||
            do! loadDocs conn
 | 
			
		||||
            
 | 
			
		||||
            let! theCount = conn.countByField PostgresDb.TableName "Value" EQ "purple"
 | 
			
		||||
            let! theCount = conn.countByField PostgresDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
            Expect.equal theCount 2 "There should have been 2 matching documents"
 | 
			
		||||
        }
 | 
			
		||||
        testTask "countByContains succeeds" {
 | 
			
		||||
@ -257,7 +257,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                let! exists = conn.existsByField PostgresDb.TableName "Sub" EX ""
 | 
			
		||||
                let! exists = conn.existsByField PostgresDb.TableName (Field.EX "Sub")
 | 
			
		||||
                Expect.isTrue exists "There should have been existing documents"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when documents do not exist" {
 | 
			
		||||
@ -265,7 +265,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                let! exists = conn.existsByField PostgresDb.TableName "NumValue" EQ "six"
 | 
			
		||||
                let! exists = conn.existsByField PostgresDb.TableName (Field.EQ "NumValue" "six")
 | 
			
		||||
                Expect.isFalse exists "There should not have been existing documents"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -354,7 +354,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> PostgresDb.TableName "Value" EQ "another"
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
 | 
			
		||||
                Expect.equal (List.length docs) 1 "There should have been one document returned"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when documents are not found" {
 | 
			
		||||
@ -362,7 +362,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> PostgresDb.TableName "Value" EQ "mauve"
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "mauve")
 | 
			
		||||
                Expect.isEmpty docs "There should have been no documents returned"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -408,7 +408,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName "Value" EQ "another"
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
 | 
			
		||||
                Expect.isSome doc "There should have been a document returned"
 | 
			
		||||
                Expect.equal doc.Value.Id "two" "The incorrect document was returned"
 | 
			
		||||
            }
 | 
			
		||||
@ -417,7 +417,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName "Value" EQ "purple"
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                Expect.isSome doc "There should have been a document returned"
 | 
			
		||||
                Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
 | 
			
		||||
            }
 | 
			
		||||
@ -426,7 +426,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName "Value" EQ "absent"
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "absent")
 | 
			
		||||
                Expect.isNone doc "There should not have been a document returned"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -562,8 +562,8 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
                
 | 
			
		||||
                do! conn.patchByField PostgresDb.TableName "Value" EQ "purple" {| NumValue = 77 |}
 | 
			
		||||
                let! after = conn.countByField PostgresDb.TableName "NumValue" EQ "77"
 | 
			
		||||
                do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
 | 
			
		||||
                let! after = conn.countByField PostgresDb.TableName (Field.EQ "NumValue" "77")
 | 
			
		||||
                Expect.equal after 2 "There should have been 2 documents returned"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is updated" {
 | 
			
		||||
@ -573,7 +573,7 @@ let integrationTests =
 | 
			
		||||
                Expect.equal before 0 "There should have been no documents returned"
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.patchByField PostgresDb.TableName "Value" EQ "burgundy" {| Foo = "green" |}
 | 
			
		||||
                do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "patchByContains" [
 | 
			
		||||
@ -616,6 +616,162 @@ let integrationTests =
 | 
			
		||||
                do! conn.patchByJsonPath PostgresDb.TableName "$.NumValue ? (@ < 0)" {| Foo = "green" |}
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "removeFieldsById" [
 | 
			
		||||
            testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub"; "Value" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a single field is removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a field is not removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsById PostgresDb.TableName "two" [ "AFieldThatIsNotThere" ]
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is matched" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "removeFieldsByField" [
 | 
			
		||||
            testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a single field is removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a field is not removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ]
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is matched" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByField PostgresDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "removeFieldsByContains" [
 | 
			
		||||
            testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a single field is removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a field is not removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Nothing" ]
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is matched" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByContains PostgresDb.TableName {| Abracadabra = "apple" |} [ "Value" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "removeFieldsByJsonPath" [
 | 
			
		||||
            testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a single field is removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ]
 | 
			
		||||
                let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a field is not removed" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
                    
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Nothing" ]
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is matched" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.Abracadabra ? (@ == \"apple\")" [ "Value" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "deleteById" [
 | 
			
		||||
            testTask "succeeds when a document is deleted" {
 | 
			
		||||
                use db   = PostgresDb.BuildDb()
 | 
			
		||||
@ -642,7 +798,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.deleteByField PostgresDb.TableName "Value" EQ "purple"
 | 
			
		||||
                do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                let! remaining = conn.countAll PostgresDb.TableName
 | 
			
		||||
                Expect.equal remaining 3 "There should have been 3 documents remaining"
 | 
			
		||||
            }
 | 
			
		||||
@ -651,7 +807,7 @@ let integrationTests =
 | 
			
		||||
                use conn = mkConn db
 | 
			
		||||
                do! loadDocs conn
 | 
			
		||||
 | 
			
		||||
                do! conn.deleteByField PostgresDb.TableName "Value" EQ "crimson"
 | 
			
		||||
                do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "crimson")
 | 
			
		||||
                let! remaining = conn.countAll PostgresDb.TableName
 | 
			
		||||
                Expect.equal remaining 5 "There should have been 5 documents remaining"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -18,15 +18,23 @@ let unitTests =
 | 
			
		||||
                    ("@test", Sql.jsonb """{"Something":"good"}""")
 | 
			
		||||
                    "JSON parameter not constructed correctly"
 | 
			
		||||
            }
 | 
			
		||||
            test "fieldParam succeeds" {
 | 
			
		||||
                let it = fieldParam 242
 | 
			
		||||
                Expect.equal (fst it) "@field" "Field parameter name not correct"
 | 
			
		||||
                match snd it with
 | 
			
		||||
                | SqlValue.Parameter value ->
 | 
			
		||||
                    Expect.equal value.ParameterName "@field" "Parameter name not correct"
 | 
			
		||||
                    Expect.equal value.Value 242 "Parameter value not correct"
 | 
			
		||||
                | _ -> Expect.isTrue false "The parameter was not a Parameter type"
 | 
			
		||||
            }
 | 
			
		||||
            testList "addFieldParam" [
 | 
			
		||||
                test "succeeds when a parameter is added" {
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.EQ "it" "242") []
 | 
			
		||||
                    Expect.hasLength paramList 1 "There should have been a parameter added"
 | 
			
		||||
                    let it = paramList[0]
 | 
			
		||||
                    Expect.equal (fst it) "@field" "Field parameter name not correct"
 | 
			
		||||
                    match snd it with
 | 
			
		||||
                    | SqlValue.Parameter value ->
 | 
			
		||||
                        Expect.equal value.ParameterName "@field" "Parameter name not correct"
 | 
			
		||||
                        Expect.equal value.Value "242" "Parameter value not correct"
 | 
			
		||||
                    | _ -> Expect.isTrue false "The parameter was not a Parameter type"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when a parameter is not added" {
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.EX "tacos") []
 | 
			
		||||
                    Expect.isEmpty paramList "There should not have been any parameters added"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            test "noParams succeeds" {
 | 
			
		||||
                Expect.isEmpty noParams "The no-params sequence should be empty"
 | 
			
		||||
            }
 | 
			
		||||
@ -110,7 +118,7 @@ let unitTests =
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Patch.byField PostgresDb.TableName "Snail" LT)
 | 
			
		||||
                        (Query.Patch.byField PostgresDb.TableName (Field.LT "Snail" 0))
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Snail' < @field"
 | 
			
		||||
                        "UPDATE partial by ID statement not correct"
 | 
			
		||||
                }
 | 
			
		||||
@ -127,6 +135,32 @@ let unitTests =
 | 
			
		||||
                        "UPDATE partial by JSON Path statement not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "RemoveFields" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byId "tbl")
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        "Remove field by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byField "tbl" (Field.LT "Fly" 0))
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data ->> 'Fly' < @field"
 | 
			
		||||
                        "Remove field by field query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byContains "tbl")
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data @> @criteria"
 | 
			
		||||
                        "Remove field by contains query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byJsonPath succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byJsonPath "tbl")
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data @? @path::jsonpath"
 | 
			
		||||
                        "Remove field by JSON path query not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Delete" [
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
                    Expect.equal (Query.Delete.byContains PostgresDb.TableName)
 | 
			
		||||
@ -357,7 +391,7 @@ let integrationTests =
 | 
			
		||||
                use db = PostgresDb.BuildDb()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
                
 | 
			
		||||
                let! theCount = Count.byField PostgresDb.TableName "Value" EQ "purple"
 | 
			
		||||
                let! theCount = Count.byField PostgresDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                Expect.equal theCount 2 "There should have been 2 matching documents"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "byContains succeeds" {
 | 
			
		||||
@ -397,14 +431,14 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    let! exists = Exists.byField PostgresDb.TableName "Sub" EX ""
 | 
			
		||||
                    let! exists = Exists.byField PostgresDb.TableName (Field.EX "Sub")
 | 
			
		||||
                    Expect.isTrue exists "There should have been existing documents"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when documents do not exist" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    let! exists = Exists.byField PostgresDb.TableName "NumValue" EQ "six"
 | 
			
		||||
                    let! exists = Exists.byField PostgresDb.TableName (Field.EQ "NumValue" "six")
 | 
			
		||||
                    Expect.isFalse exists "There should not have been existing documents"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
@ -486,14 +520,14 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> PostgresDb.TableName "Value" EQ "another"
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
 | 
			
		||||
                    Expect.equal (List.length docs) 1 "There should have been one document returned"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when documents are not found" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> PostgresDb.TableName "Value" EQ "mauve"
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "mauve")
 | 
			
		||||
                    Expect.isEmpty docs "There should have been no documents returned"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
@ -534,7 +568,7 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName "Value" EQ "another"
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
 | 
			
		||||
                    Expect.isSome doc "There should have been a document returned"
 | 
			
		||||
                    Expect.equal doc.Value.Id "two" "The incorrect document was returned"
 | 
			
		||||
                }
 | 
			
		||||
@ -542,7 +576,7 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName "Value" EQ "purple"
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                    Expect.isSome doc "There should have been a document returned"
 | 
			
		||||
                    Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
 | 
			
		||||
                }
 | 
			
		||||
@ -550,7 +584,7 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName "Value" EQ "absent"
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "absent")
 | 
			
		||||
                    Expect.isNone doc "There should not have been a document returned"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
@ -682,8 +716,8 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                    
 | 
			
		||||
                    do! Patch.byField PostgresDb.TableName "Value" EQ "purple" {| NumValue = 77 |}
 | 
			
		||||
                    let! after = Count.byField PostgresDb.TableName "NumValue" EQ "77"
 | 
			
		||||
                    do! Patch.byField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
 | 
			
		||||
                    let! after = Count.byField PostgresDb.TableName (Field.EQ "NumValue" "77")
 | 
			
		||||
                    Expect.equal after 2 "There should have been 2 documents returned"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is updated" {
 | 
			
		||||
@ -693,7 +727,7 @@ let integrationTests =
 | 
			
		||||
                    Expect.equal before 0 "There should have been no documents returned"
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! Patch.byField PostgresDb.TableName "Value" EQ "burgundy" {| Foo = "green" |}
 | 
			
		||||
                    do! Patch.byField PostgresDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "byContains" [
 | 
			
		||||
@ -735,6 +769,148 @@ let integrationTests =
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        testList "RemoveFields" [
 | 
			
		||||
            testList "byId" [
 | 
			
		||||
                testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub"; "Value" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a single field is removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a field is not removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byId PostgresDb.TableName "two" [ "AFieldThatIsNotThere" ]
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is matched" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byId PostgresDb.TableName "two" [ "Value" ]
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "byField" [
 | 
			
		||||
                testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a single field is removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a field is not removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ]
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is matched" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byField PostgresDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "byContains" [
 | 
			
		||||
                testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a single field is removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a field is not removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Nothing" ]
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is matched" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byContains PostgresDb.TableName {| Abracadabra = "apple" |} [ "Value" ]
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "byJsonPath" [
 | 
			
		||||
                testTask "succeeds when multiple fields are removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 1 "There should be 1 document without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a single field is removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ]
 | 
			
		||||
                    let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
 | 
			
		||||
                    Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
 | 
			
		||||
                    let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
 | 
			
		||||
                    Expect.equal noValue 0 "There should be no documents without Value fields"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a field is not removed" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                        
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Nothing" ]
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is matched" {
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byJsonPath PostgresDb.TableName "$.Abracadabra ? (@ == \"apple\")" [ "Value" ]
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        testList "Delete" [
 | 
			
		||||
            testList "byId" [
 | 
			
		||||
                testTask "succeeds when a document is deleted" {
 | 
			
		||||
@ -759,7 +935,7 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! Delete.byField PostgresDb.TableName "Value" EQ "purple"
 | 
			
		||||
                    do! Delete.byField PostgresDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                    let! remaining = Count.all PostgresDb.TableName
 | 
			
		||||
                    Expect.equal remaining 3 "There should have been 3 documents remaining"
 | 
			
		||||
                }
 | 
			
		||||
@ -767,7 +943,7 @@ let integrationTests =
 | 
			
		||||
                    use db = PostgresDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
 | 
			
		||||
                    do! Delete.byField PostgresDb.TableName "Value" EQ "crimson"
 | 
			
		||||
                    do! Delete.byField PostgresDb.TableName (Field.EQ "Value" "crimson")
 | 
			
		||||
                    let! remaining = Count.all PostgresDb.TableName
 | 
			
		||||
                    Expect.equal remaining 5 "There should have been 5 documents remaining"
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
module SqliteExtensionTests
 | 
			
		||||
 | 
			
		||||
open System.Text.Json
 | 
			
		||||
open BitBadger.Documents
 | 
			
		||||
open BitBadger.Documents.Sqlite
 | 
			
		||||
open BitBadger.Documents.Tests
 | 
			
		||||
@ -117,7 +118,7 @@ let integrationTests =
 | 
			
		||||
            use  conn = Configuration.dbConn ()
 | 
			
		||||
            do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
            let! theCount = conn.countByField SqliteDb.TableName "Value" EQ "purple"
 | 
			
		||||
            let! theCount = conn.countByField SqliteDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
            Expect.equal theCount 2L "There should have been 2 matching documents"
 | 
			
		||||
        }
 | 
			
		||||
        testList "existsById" [
 | 
			
		||||
@ -144,7 +145,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                let! exists = conn.existsByField SqliteDb.TableName "NumValue" EQ 10
 | 
			
		||||
                let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "NumValue" 10)
 | 
			
		||||
                Expect.isTrue exists "There should have been existing documents"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no matching documents exist" {
 | 
			
		||||
@ -152,7 +153,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                let! exists = conn.existsByField SqliteDb.TableName "Nothing" EQ "none"
 | 
			
		||||
                let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "Nothing" "none")
 | 
			
		||||
                Expect.isFalse exists "There should not have been any existing documents"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -205,7 +206,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> SqliteDb.TableName "Sub.Foo" EQ "green"
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
 | 
			
		||||
                Expect.equal (List.length docs) 2 "There should have been two documents returned"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when documents are not found" {
 | 
			
		||||
@ -213,7 +214,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> SqliteDb.TableName "Value" EQ "mauve"
 | 
			
		||||
                let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "mauve")
 | 
			
		||||
                Expect.isTrue (List.isEmpty docs) "There should have been no documents returned"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -223,7 +224,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName "Value" EQ "another"
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another")
 | 
			
		||||
                Expect.isTrue (Option.isSome doc) "There should have been a document returned"
 | 
			
		||||
                Expect.equal doc.Value.Id "two" "The incorrect document was returned"
 | 
			
		||||
            }
 | 
			
		||||
@ -232,7 +233,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName "Sub.Foo" EQ "green"
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
 | 
			
		||||
                Expect.isTrue (Option.isSome doc) "There should have been a document returned"
 | 
			
		||||
                Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
 | 
			
		||||
            }
 | 
			
		||||
@ -241,7 +242,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName "Value" EQ "absent"
 | 
			
		||||
                let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent")
 | 
			
		||||
                Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -329,8 +330,8 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
                
 | 
			
		||||
                do! conn.patchByField SqliteDb.TableName "Value" EQ "purple" {| NumValue = 77 |}
 | 
			
		||||
                let! after = conn.countByField SqliteDb.TableName "NumValue" EQ 77
 | 
			
		||||
                do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
 | 
			
		||||
                let! after = conn.countByField SqliteDb.TableName (Field.EQ "NumValue" 77)
 | 
			
		||||
                Expect.equal after 2L "There should have been 2 documents returned"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is updated" {
 | 
			
		||||
@ -341,7 +342,67 @@ let integrationTests =
 | 
			
		||||
                Expect.isEmpty before "There should have been no documents returned"
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.patchByField SqliteDb.TableName "Value" EQ "burgundy" {| Foo = "green" |}
 | 
			
		||||
                do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "removeFieldsById" [
 | 
			
		||||
            testTask "succeeds when fields are removed" {
 | 
			
		||||
                use! db   = SqliteDb.BuildDb()
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
                
 | 
			
		||||
                do! conn.removeFieldsById SqliteDb.TableName "two" [ "Sub"; "Value" ]
 | 
			
		||||
                try
 | 
			
		||||
                    let! _ = conn.findById<string, JsonDocument> SqliteDb.TableName "two"
 | 
			
		||||
                    Expect.isTrue false "The updated document should have failed to parse"
 | 
			
		||||
                with
 | 
			
		||||
                | :? JsonException -> ()
 | 
			
		||||
                | exn as ex -> Expect.isTrue false $"Threw {ex.GetType().Name} ({ex.Message})"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a field is not removed" {
 | 
			
		||||
                use! db   = SqliteDb.BuildDb()
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsById SqliteDb.TableName "two" [ "AFieldThatIsNotThere" ]
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is matched" {
 | 
			
		||||
                use! db   = SqliteDb.BuildDb()
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "removeFieldByField" [
 | 
			
		||||
            testTask "succeeds when a field is removed" {
 | 
			
		||||
                use! db   = SqliteDb.BuildDb()
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
                
 | 
			
		||||
                do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ]
 | 
			
		||||
                try
 | 
			
		||||
                    let! _ = conn.findById<string, JsonDocument> SqliteDb.TableName "four"
 | 
			
		||||
                    Expect.isTrue false "The updated document should have failed to parse"
 | 
			
		||||
                with
 | 
			
		||||
                | :? JsonException -> ()
 | 
			
		||||
                | exn as ex -> Expect.isTrue false $"Threw {ex.GetType().Name} ({ex.Message})"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when a field is not removed" {
 | 
			
		||||
                use! db   = SqliteDb.BuildDb()
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ]
 | 
			
		||||
            }
 | 
			
		||||
            testTask "succeeds when no document is matched" {
 | 
			
		||||
                use! db   = SqliteDb.BuildDb()
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                
 | 
			
		||||
                // This not raising an exception is the test
 | 
			
		||||
                do! conn.removeFieldsByField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "deleteById" [
 | 
			
		||||
@ -370,7 +431,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                do! conn.deleteByField SqliteDb.TableName "Value" NE "purple"
 | 
			
		||||
                do! conn.deleteByField SqliteDb.TableName (Field.NE "Value" "purple")
 | 
			
		||||
                let! remaining = conn.countAll SqliteDb.TableName
 | 
			
		||||
                Expect.equal remaining 2L "There should have been 2 documents remaining"
 | 
			
		||||
            }
 | 
			
		||||
@ -379,7 +440,7 @@ let integrationTests =
 | 
			
		||||
                use  conn = Configuration.dbConn ()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
    
 | 
			
		||||
                do! conn.deleteByField SqliteDb.TableName "Value" EQ "crimson"
 | 
			
		||||
                do! conn.deleteByField SqliteDb.TableName (Field.EQ "Value" "crimson")
 | 
			
		||||
                let! remaining = conn.countAll SqliteDb.TableName
 | 
			
		||||
                Expect.equal remaining 5L "There should have been 5 documents remaining"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
module SqliteTests
 | 
			
		||||
 | 
			
		||||
open System.Text.Json
 | 
			
		||||
open BitBadger.Documents
 | 
			
		||||
open BitBadger.Documents.Sqlite
 | 
			
		||||
open BitBadger.Documents.Tests
 | 
			
		||||
@ -26,11 +27,28 @@ let unitTests =
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Patch.byField "tbl" "Part" NE)
 | 
			
		||||
                        (Query.Patch.byField "tbl" (Field.NE "Part" 0))
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Part' <> @field"
 | 
			
		||||
                        "UPDATE partial by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "RemoveFields" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byId "tbl" [ SqliteParameter("@name", "one") ])
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        "Remove field by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byField
 | 
			
		||||
                             "tbl"
 | 
			
		||||
                             (Field.GT "Fly" 0)
 | 
			
		||||
                             [ SqliteParameter("@name0", "one"); SqliteParameter("@name1", "two") ])
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' > @field"
 | 
			
		||||
                        "Remove field by field query not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        testList "Parameters" [
 | 
			
		||||
            test "idParam succeeds" {
 | 
			
		||||
@ -43,11 +61,19 @@ let unitTests =
 | 
			
		||||
                Expect.equal theParam.ParameterName "@test" "The parameter name is incorrect"
 | 
			
		||||
                Expect.equal theParam.Value """{"Nice":"job"}""" "The parameter value is incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "fieldParam succeeds" {
 | 
			
		||||
                let theParam = fieldParam 99
 | 
			
		||||
                Expect.equal theParam.ParameterName "@field" "The parameter name is incorrect"
 | 
			
		||||
                Expect.equal theParam.Value 99 "The parameter value is incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            testList "addFieldParam" [
 | 
			
		||||
                test "succeeds when adding a parameter" {
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.EQ "it" 99) []
 | 
			
		||||
                    Expect.hasLength paramList 1 "There should have been a parameter added"
 | 
			
		||||
                    let theParam = paramList[0]
 | 
			
		||||
                    Expect.equal theParam.ParameterName "@field" "The parameter name is incorrect"
 | 
			
		||||
                    Expect.equal theParam.Value 99 "The parameter value is incorrect"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when not adding a parameter" {
 | 
			
		||||
                    let paramList = addFieldParam "@it" (Field.NEX "Coffee") []
 | 
			
		||||
                    Expect.isEmpty paramList "There should not have been any parameters added"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            test "noParams succeeds" {
 | 
			
		||||
                Expect.isEmpty noParams "The parameter list should have been empty"
 | 
			
		||||
            }
 | 
			
		||||
@ -277,7 +303,7 @@ let integrationTests =
 | 
			
		||||
                use! db = SqliteDb.BuildDb()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                let! theCount = Count.byField SqliteDb.TableName "Value" EQ "purple"
 | 
			
		||||
                let! theCount = Count.byField SqliteDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                Expect.equal theCount 2L "There should have been 2 matching documents"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
@ -303,14 +329,14 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    let! exists = Exists.byField SqliteDb.TableName "NumValue" EQ 10
 | 
			
		||||
                    let! exists = Exists.byField SqliteDb.TableName (Field.EQ "NumValue" 10)
 | 
			
		||||
                    Expect.isTrue exists "There should have been existing documents"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no matching documents exist" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    let! exists = Exists.byField SqliteDb.TableName "Nothing" LT "none"
 | 
			
		||||
                    let! exists = Exists.byField SqliteDb.TableName (Field.LT "Nothing" "none")
 | 
			
		||||
                    Expect.isFalse exists "There should not have been any existing documents"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
@ -360,14 +386,14 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> SqliteDb.TableName "NumValue" GT 15
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> SqliteDb.TableName (Field.GT "NumValue" 15)
 | 
			
		||||
                    Expect.equal (List.length docs) 2 "There should have been two documents returned"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when documents are not found" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> SqliteDb.TableName "NumValue" GT 100
 | 
			
		||||
                    let! docs = Find.byField<JsonDocument> SqliteDb.TableName (Field.GT "NumValue" 100)
 | 
			
		||||
                    Expect.isTrue (List.isEmpty docs) "There should have been no documents returned"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
@ -376,7 +402,7 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName "Value" EQ "another"
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another")
 | 
			
		||||
                    Expect.isTrue (Option.isSome doc) "There should have been a document returned"
 | 
			
		||||
                    Expect.equal doc.Value.Id "two" "The incorrect document was returned"
 | 
			
		||||
                }
 | 
			
		||||
@ -384,7 +410,7 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName "Sub.Foo" EQ "green"
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
 | 
			
		||||
                    Expect.isTrue (Option.isSome doc) "There should have been a document returned"
 | 
			
		||||
                    Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
 | 
			
		||||
                }
 | 
			
		||||
@ -392,7 +418,7 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName "Value" EQ "absent"
 | 
			
		||||
                    let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent")
 | 
			
		||||
                    Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
@ -474,8 +500,8 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                    
 | 
			
		||||
                    do! Patch.byField SqliteDb.TableName "Value" EQ "purple" {| NumValue = 77 |}
 | 
			
		||||
                    let! after = Count.byField SqliteDb.TableName "NumValue" EQ 77
 | 
			
		||||
                    do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
 | 
			
		||||
                    let! after = Count.byField SqliteDb.TableName (Field.EQ "NumValue" 77)
 | 
			
		||||
                    Expect.equal after 2L "There should have been 2 documents returned"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is updated" {
 | 
			
		||||
@ -485,7 +511,63 @@ let integrationTests =
 | 
			
		||||
                    Expect.isEmpty before "There should have been no documents returned"
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! Patch.byField SqliteDb.TableName "Value" EQ "burgundy" {| Foo = "green" |}
 | 
			
		||||
                    do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        testList "RemoveFields" [
 | 
			
		||||
            testList "byId" [
 | 
			
		||||
                testTask "succeeds when fields is removed" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                    
 | 
			
		||||
                    do! RemoveFields.byId SqliteDb.TableName "two" [ "Sub"; "Value" ]
 | 
			
		||||
                    try
 | 
			
		||||
                        let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "two"
 | 
			
		||||
                        Expect.isTrue false "The updated document should have failed to parse"
 | 
			
		||||
                    with
 | 
			
		||||
                    | :? JsonException -> ()
 | 
			
		||||
                    | exn as ex -> Expect.isTrue false $"Threw {ex.GetType().Name} ({ex.Message})"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a field is not removed" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byId SqliteDb.TableName "two" [ "AFieldThatIsNotThere" ]
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is matched" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byId SqliteDb.TableName "two" [ "Value" ]
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "byField" [
 | 
			
		||||
                testTask "succeeds when a field is removed" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                    
 | 
			
		||||
                    do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ]
 | 
			
		||||
                    try
 | 
			
		||||
                        let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "four"
 | 
			
		||||
                        Expect.isTrue false "The updated document should have failed to parse"
 | 
			
		||||
                    with
 | 
			
		||||
                    | :? JsonException -> ()
 | 
			
		||||
                    | exn as ex -> Expect.isTrue false $"Threw {ex.GetType().Name} ({ex.Message})"
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when a field is not removed" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ]
 | 
			
		||||
                }
 | 
			
		||||
                testTask "succeeds when no document is matched" {
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    
 | 
			
		||||
                    // This not raising an exception is the test
 | 
			
		||||
                    do! RemoveFields.byField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
@ -513,7 +595,7 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    do! Delete.byField SqliteDb.TableName "Value" NE "purple"
 | 
			
		||||
                    do! Delete.byField SqliteDb.TableName (Field.NE "Value" "purple")
 | 
			
		||||
                    let! remaining = Count.all SqliteDb.TableName
 | 
			
		||||
                    Expect.equal remaining 2L "There should have been 2 documents remaining"
 | 
			
		||||
                }
 | 
			
		||||
@ -521,7 +603,7 @@ let integrationTests =
 | 
			
		||||
                    use! db = SqliteDb.BuildDb()
 | 
			
		||||
                    do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                    do! Delete.byField SqliteDb.TableName "Value" EQ "crimson"
 | 
			
		||||
                    do! Delete.byField SqliteDb.TableName (Field.EQ "Value" "crimson")
 | 
			
		||||
                    let! remaining = Count.all SqliteDb.TableName
 | 
			
		||||
                    Expect.equal remaining 5L "There should have been 5 documents remaining"
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user