First cut of BT operator (#3)
This commit is contained in:
		
							parent
							
								
									7d7214e9f2
								
							
						
					
					
						commit
						1707d3ce63
					
				@ -15,6 +15,8 @@ type Op =
 | 
			
		||||
    | LE
 | 
			
		||||
    /// Not Equal to (<>)
 | 
			
		||||
    | NE
 | 
			
		||||
    /// Between (BETWEEN)
 | 
			
		||||
    | BT
 | 
			
		||||
    /// Exists (IS NOT NULL)
 | 
			
		||||
    | EX
 | 
			
		||||
    /// Does Not Exist (IS NULL)
 | 
			
		||||
@ -28,6 +30,7 @@ type Op =
 | 
			
		||||
        | LT -> "<"
 | 
			
		||||
        | LE -> "<="
 | 
			
		||||
        | NE -> "<>"
 | 
			
		||||
        | BT -> "BETWEEN"
 | 
			
		||||
        | EX -> "IS NOT NULL"
 | 
			
		||||
        | NEX -> "IS NULL"
 | 
			
		||||
 | 
			
		||||
@ -68,11 +71,15 @@ type Field = {
 | 
			
		||||
    static member NE name (value: obj) =
 | 
			
		||||
        { Name = name; Op = NE; Value = value }
 | 
			
		||||
    
 | 
			
		||||
    /// Create a BETWEEN field criterion
 | 
			
		||||
    static member BT name (min: obj) (max: obj) =
 | 
			
		||||
        { Name = name; Op = BT; Value = [ min; max ] }
 | 
			
		||||
    
 | 
			
		||||
    /// 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
 | 
			
		||||
    /// Create a not exists (IS NULL) field criterion
 | 
			
		||||
    static member NEX name =
 | 
			
		||||
        { Name = name; Op = NEX; Value = obj () }
 | 
			
		||||
 | 
			
		||||
@ -150,17 +157,6 @@ module Query =
 | 
			
		||||
    let selectFromTable tableName =
 | 
			
		||||
        $"SELECT data FROM %s{tableName}"
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
 | 
			
		||||
    [<CompiledName "WhereByField">]
 | 
			
		||||
    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 (Field.EQ (Configuration.idField ()) 0) paramName
 | 
			
		||||
    
 | 
			
		||||
    /// Queries to define tables and indexes
 | 
			
		||||
    module Definition =
 | 
			
		||||
        
 | 
			
		||||
@ -202,62 +198,6 @@ module Query =
 | 
			
		||||
    [<CompiledName "Save">]
 | 
			
		||||
    let save tableName =
 | 
			
		||||
        sprintf
 | 
			
		||||
            "INSERT INTO %s VALUES (@data) ON CONFLICT ((data ->> '%s')) DO UPDATE SET data = EXCLUDED.data"
 | 
			
		||||
            "INSERT INTO %s VALUES (@data) ON CONFLICT ((data->>'%s')) DO UPDATE SET data = EXCLUDED.data"
 | 
			
		||||
            tableName (Configuration.idField ()) 
 | 
			
		||||
    
 | 
			
		||||
    /// Query to update a document
 | 
			
		||||
    [<CompiledName "Update">]
 | 
			
		||||
    let update tableName =
 | 
			
		||||
        $"""UPDATE %s{tableName} SET data = @data WHERE {whereById "@id"}"""
 | 
			
		||||
 | 
			
		||||
    /// Queries for counting documents
 | 
			
		||||
    module Count =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count all documents in a table
 | 
			
		||||
        [<CompiledName "All">]
 | 
			
		||||
        let all tableName =
 | 
			
		||||
            $"SELECT COUNT(*) AS it FROM %s{tableName}"
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count matching documents using a text comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries for determining document existence
 | 
			
		||||
    module Exists =
 | 
			
		||||
 | 
			
		||||
        /// Query to determine if a document exists for the given ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereById "@id"}) AS it"""
 | 
			
		||||
 | 
			
		||||
        /// Query to determine if documents exist using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField field "@field"}) AS it"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries for retrieving documents
 | 
			
		||||
    module Find =
 | 
			
		||||
 | 
			
		||||
        /// Query to retrieve a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""{selectFromTable tableName} WHERE {whereById "@id"}"""
 | 
			
		||||
        
 | 
			
		||||
        /// Query to retrieve documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""{selectFromTable tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries to delete documents
 | 
			
		||||
    module Delete =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to delete a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereById "@id"}"""
 | 
			
		||||
 | 
			
		||||
        /// Query to delete documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
 | 
			
		||||
@ -63,17 +63,29 @@ module Parameters =
 | 
			
		||||
    let jsonParam (name: string) (it: 'TJson) =
 | 
			
		||||
        name, Sql.jsonb (Configuration.serializer().Serialize it)
 | 
			
		||||
 | 
			
		||||
    /// Create a JSON field parameter (name "@field")
 | 
			
		||||
    /// Create a JSON field parameter
 | 
			
		||||
    [<CompiledName "FSharpAddField">]
 | 
			
		||||
    let addFieldParam name field parameters =
 | 
			
		||||
        match field.Op with
 | 
			
		||||
        | EX | NEX -> parameters
 | 
			
		||||
        | BT ->
 | 
			
		||||
            let values = field.Value :?> obj list
 | 
			
		||||
            List.concat
 | 
			
		||||
                [ parameters
 | 
			
		||||
                  [ ($"{name}min", Sql.parameter (NpgsqlParameter($"{name}min", List.head values)))
 | 
			
		||||
                    ($"{name}max", Sql.parameter (NpgsqlParameter($"{name}max", List.last values))) ] ]
 | 
			
		||||
        | _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) :: parameters
 | 
			
		||||
 | 
			
		||||
    /// Create a JSON field parameter (name "@field")
 | 
			
		||||
    /// Create a JSON field parameter
 | 
			
		||||
    let AddField name field parameters =
 | 
			
		||||
        match field.Op with
 | 
			
		||||
        | EX | NEX -> parameters
 | 
			
		||||
        | BT ->
 | 
			
		||||
            let values = field.Value :?> obj list
 | 
			
		||||
            ResizeArray
 | 
			
		||||
                [ ($"{name}min", Sql.parameter (NpgsqlParameter($"{name}min", List.head values)))
 | 
			
		||||
                  ($"{name}max", Sql.parameter (NpgsqlParameter($"{name}max", List.last values))) ]
 | 
			
		||||
            |> Seq.append parameters
 | 
			
		||||
        | _ -> (name, Sql.parameter (NpgsqlParameter(name, field.Value))) |> Seq.singleton |> Seq.append parameters
 | 
			
		||||
 | 
			
		||||
    /// Append JSON field name parameters for the given field names to the given parameters
 | 
			
		||||
@ -97,6 +109,25 @@ module Parameters =
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
module Query =
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
 | 
			
		||||
    [<CompiledName "WhereByField">]
 | 
			
		||||
    let whereByField field paramName =
 | 
			
		||||
        match field.Op with
 | 
			
		||||
        | EX | NEX -> $"data->>'{field.Name}' {field.Op}"
 | 
			
		||||
        | BT ->
 | 
			
		||||
            let names  = $"{paramName}min AND {paramName}max"
 | 
			
		||||
            let values = field.Value :?> obj list
 | 
			
		||||
            match values[0] with
 | 
			
		||||
            | :? int8    | :? uint8  | :? int16  | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64
 | 
			
		||||
            | :? decimal | :? single | :? double -> $"(data->>'{field.Name}')::numeric {field.Op} {names}"
 | 
			
		||||
            | _ -> $"data->>'{field.Name}' {field.Op} {names}"
 | 
			
		||||
        | _ -> $"data->>'{field.Name}' {field.Op} %s{paramName}"
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement an ID-based query
 | 
			
		||||
    [<CompiledName "WhereById">]
 | 
			
		||||
    let whereById paramName =
 | 
			
		||||
        whereByField (Field.EQ (Configuration.idField ()) 0) paramName
 | 
			
		||||
    
 | 
			
		||||
    /// Table and index definition queries
 | 
			
		||||
    module Definition =
 | 
			
		||||
        
 | 
			
		||||
@ -112,6 +143,11 @@ module Query =
 | 
			
		||||
            let tableName = name.Split '.' |> Array.last
 | 
			
		||||
            $"CREATE INDEX IF NOT EXISTS idx_{tableName}_document ON {name} USING GIN (data{extraOps})"
 | 
			
		||||
 | 
			
		||||
    /// Query to update a document
 | 
			
		||||
    [<CompiledName "Update">]
 | 
			
		||||
    let update tableName =
 | 
			
		||||
        $"""UPDATE %s{tableName} SET data = @data WHERE {whereById "@id"}"""
 | 
			
		||||
 | 
			
		||||
    /// Create a WHERE clause fragment to implement a @> (JSON contains) condition
 | 
			
		||||
    [<CompiledName "WhereDataContains">]
 | 
			
		||||
    let whereDataContains paramName =
 | 
			
		||||
@ -125,6 +161,16 @@ module Query =
 | 
			
		||||
    /// Queries for counting documents
 | 
			
		||||
    module Count =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count all documents in a table
 | 
			
		||||
        [<CompiledName "All">]
 | 
			
		||||
        let all tableName =
 | 
			
		||||
            $"SELECT COUNT(*) AS it FROM %s{tableName}"
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count matching documents using a text comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count matching documents using a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
        let byContains tableName =
 | 
			
		||||
@ -138,6 +184,16 @@ module Query =
 | 
			
		||||
    /// Queries for determining document existence
 | 
			
		||||
    module Exists =
 | 
			
		||||
 | 
			
		||||
        /// Query to determine if a document exists for the given ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereById "@id"}) AS it"""
 | 
			
		||||
 | 
			
		||||
        /// Query to determine if documents exist using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField field "@field"}) AS it"""
 | 
			
		||||
        
 | 
			
		||||
        /// Query to determine if documents exist using a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
        let byContains tableName =
 | 
			
		||||
@ -151,6 +207,16 @@ module Query =
 | 
			
		||||
    /// Queries for retrieving documents
 | 
			
		||||
    module Find =
 | 
			
		||||
 | 
			
		||||
        /// Query to retrieve a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""{Query.selectFromTable tableName} WHERE {whereById "@id"}"""
 | 
			
		||||
        
 | 
			
		||||
        /// Query to retrieve documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""{Query.selectFromTable tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
        /// Query to retrieve documents using a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
        let byContains tableName =
 | 
			
		||||
@ -171,12 +237,12 @@ module Query =
 | 
			
		||||
        /// Query to patch a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            Query.whereById "@id" |> update tableName
 | 
			
		||||
            whereById "@id" |> update tableName
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch documents match a JSON field comparison (->> =)
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            Query.whereByField field "@field" |> update tableName
 | 
			
		||||
            whereByField field "@field" |> update tableName
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch documents matching a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
@ -198,12 +264,12 @@ module Query =
 | 
			
		||||
        /// Query to remove fields from a document by the document's ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            Query.whereById "@id" |> update tableName
 | 
			
		||||
            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
 | 
			
		||||
            whereByField field "@field" |> update tableName
 | 
			
		||||
        
 | 
			
		||||
        /// Query to patch documents matching a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
@ -218,6 +284,16 @@ module Query =
 | 
			
		||||
    /// Queries to delete documents
 | 
			
		||||
    module Delete =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to delete a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereById "@id"}"""
 | 
			
		||||
 | 
			
		||||
        /// Query to delete documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
        /// Query to delete documents using a JSON containment query (@>)
 | 
			
		||||
        [<CompiledName "ByContains">]
 | 
			
		||||
        let byContains tableName =
 | 
			
		||||
 | 
			
		||||
@ -31,6 +31,21 @@ module Configuration =
 | 
			
		||||
[<RequireQualifiedAccess>]
 | 
			
		||||
module Query =
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
 | 
			
		||||
    [<CompiledName "WhereByField">]
 | 
			
		||||
    let whereByField field paramName =
 | 
			
		||||
        let theRest =
 | 
			
		||||
            match field.Op with
 | 
			
		||||
            | EX | NEX -> ""
 | 
			
		||||
            | BT -> $" {paramName}min AND {paramName}max"
 | 
			
		||||
            | _ -> $" %s{paramName}"
 | 
			
		||||
        $"data->>'{field.Name}' {field.Op}{theRest}"
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement an ID-based query
 | 
			
		||||
    [<CompiledName "WhereById">]
 | 
			
		||||
    let whereById paramName =
 | 
			
		||||
        whereByField (Field.EQ (Configuration.idField ()) 0) paramName
 | 
			
		||||
    
 | 
			
		||||
    /// Data definition
 | 
			
		||||
    module Definition =
 | 
			
		||||
 | 
			
		||||
@ -39,6 +54,50 @@ module Query =
 | 
			
		||||
        let ensureTable name =
 | 
			
		||||
            Query.Definition.ensureTableFor name "TEXT"
 | 
			
		||||
    
 | 
			
		||||
    /// Query to update a document
 | 
			
		||||
    [<CompiledName "Update">]
 | 
			
		||||
    let update tableName =
 | 
			
		||||
        $"""UPDATE %s{tableName} SET data = @data WHERE {whereById "@id"}"""
 | 
			
		||||
 | 
			
		||||
    /// Queries for counting documents
 | 
			
		||||
    module Count =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count all documents in a table
 | 
			
		||||
        [<CompiledName "All">]
 | 
			
		||||
        let all tableName =
 | 
			
		||||
            $"SELECT COUNT(*) AS it FROM %s{tableName}"
 | 
			
		||||
        
 | 
			
		||||
        /// Query to count matching documents using a text comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries for determining document existence
 | 
			
		||||
    module Exists =
 | 
			
		||||
 | 
			
		||||
        /// Query to determine if a document exists for the given ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereById "@id"}) AS it"""
 | 
			
		||||
 | 
			
		||||
        /// Query to determine if documents exist using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereByField field "@field"}) AS it"""
 | 
			
		||||
        
 | 
			
		||||
    /// Queries for retrieving documents
 | 
			
		||||
    module Find =
 | 
			
		||||
 | 
			
		||||
        /// Query to retrieve a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""{Query.selectFromTable tableName} WHERE {whereById "@id"}"""
 | 
			
		||||
        
 | 
			
		||||
        /// Query to retrieve documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""{Query.selectFromTable tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
    /// Document patching (partial update) queries
 | 
			
		||||
    module Patch =
 | 
			
		||||
        
 | 
			
		||||
@ -49,12 +108,12 @@ module Query =
 | 
			
		||||
        /// Query to patch (partially update) a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            Query.whereById "@id" |> update tableName
 | 
			
		||||
            whereById "@id" |> update tableName
 | 
			
		||||
            
 | 
			
		||||
        /// Query to patch (partially update) a document via a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            Query.whereByField field "@field" |> update tableName
 | 
			
		||||
            whereByField field "@field" |> update tableName
 | 
			
		||||
    
 | 
			
		||||
    /// Queries to remove fields from documents
 | 
			
		||||
    module RemoveFields =
 | 
			
		||||
@ -67,7 +126,7 @@ module Query =
 | 
			
		||||
        /// Query to remove fields from a document by the document's ID
 | 
			
		||||
        [<CompiledName "FSharpById">]
 | 
			
		||||
        let byId tableName parameters =
 | 
			
		||||
            Query.whereById "@id" |> update tableName parameters
 | 
			
		||||
            whereById "@id" |> update tableName parameters
 | 
			
		||||
        
 | 
			
		||||
        /// Query to remove fields from a document by the document's ID
 | 
			
		||||
        let ById(tableName, parameters) =
 | 
			
		||||
@ -76,12 +135,25 @@ module Query =
 | 
			
		||||
        /// 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
 | 
			
		||||
            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)
 | 
			
		||||
 | 
			
		||||
    /// Queries to delete documents
 | 
			
		||||
    module Delete =
 | 
			
		||||
        
 | 
			
		||||
        /// Query to delete a document by its ID
 | 
			
		||||
        [<CompiledName "ById">]
 | 
			
		||||
        let byId tableName =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereById "@id"}"""
 | 
			
		||||
 | 
			
		||||
        /// Query to delete documents using a comparison on a JSON field
 | 
			
		||||
        [<CompiledName "ByField">]
 | 
			
		||||
        let byField tableName field =
 | 
			
		||||
            $"""DELETE FROM %s{tableName} WHERE {whereByField field "@field"}"""
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
/// Parameter handling helpers
 | 
			
		||||
[<AutoOpen>]
 | 
			
		||||
@ -100,12 +172,26 @@ module Parameters =
 | 
			
		||||
    /// Create a JSON field parameter (name "@field")
 | 
			
		||||
    [<CompiledName "FSharpAddField">]
 | 
			
		||||
    let addFieldParam name field parameters =
 | 
			
		||||
        match field.Op with EX | NEX -> parameters | _ -> SqliteParameter(name, field.Value) :: parameters
 | 
			
		||||
        match field.Op with
 | 
			
		||||
        | EX | NEX -> parameters
 | 
			
		||||
        | BT ->
 | 
			
		||||
            let values = field.Value :?> obj list
 | 
			
		||||
            SqliteParameter($"{name}min", values[0]) :: SqliteParameter($"{name}max", values[1]) :: 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
 | 
			
		||||
        | BT ->
 | 
			
		||||
            let values = field.Value :?> obj list
 | 
			
		||||
            // let min = SqliteParameter($"{name}min", SqliteType.Integer)
 | 
			
		||||
            // min.Value <- values[0]
 | 
			
		||||
            // let max = SqliteParameter($"{name}max", SqliteType.Integer)
 | 
			
		||||
            // max.Value <- values[1]
 | 
			
		||||
            [ SqliteParameter($"{name}min", values[0]); SqliteParameter($"{name}max", values[1]) ]
 | 
			
		||||
            // [min; max]
 | 
			
		||||
            |> Seq.append parameters
 | 
			
		||||
        | _ -> SqliteParameter(name, field.Value) |> Seq.singleton |> Seq.append parameters
 | 
			
		||||
 | 
			
		||||
    /// Append JSON field name parameters for the given field names to the given parameters
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
using Expecto.CSharp;
 | 
			
		||||
using Expecto;
 | 
			
		||||
using Microsoft.FSharp.Collections;
 | 
			
		||||
 | 
			
		||||
namespace BitBadger.Documents.Tests.CSharp;
 | 
			
		||||
 | 
			
		||||
@ -96,6 +97,10 @@ public static class CommonCSharpTests
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Op.NE.ToString(), "<>", "The not equal to operator was not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("BT succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Op.BT.ToString(), "BETWEEN", "The \"between\" operator was not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("EX succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Op.EX.ToString(), "IS NOT NULL", "The \"exists\" operator was not correct");
 | 
			
		||||
@ -149,6 +154,13 @@ public static class CommonCSharpTests
 | 
			
		||||
                Expect.equal(field.Op, Op.NE, "Operator incorrect");
 | 
			
		||||
                Expect.equal(field.Value, "here", "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("BT succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.BT("Age", 18, 49);
 | 
			
		||||
                Expect.equal(field.Name, "Age", "Field name incorrect");
 | 
			
		||||
                Expect.equal(field.Op, Op.BT, "Operator incorrect");
 | 
			
		||||
                Expect.equal(((FSharpList<object>)field.Value).ToArray(), new object[] { 18, 49 }, "Value incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("EX succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                var field = Field.EX("Groovy");
 | 
			
		||||
@ -169,23 +181,6 @@ public static class CommonCSharpTests
 | 
			
		||||
                Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table",
 | 
			
		||||
                    "SELECT statement not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("WhereById succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Query.WhereById("@id"), "data ->> 'Id' = @id", "WHERE clause not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("WhereByField", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when a logical operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    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(Field.NEX("thatField"), ""), "data ->> 'thatField' IS NULL",
 | 
			
		||||
                        "WHERE clause not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Definition", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("EnsureTableFor succeeds", () =>
 | 
			
		||||
@ -226,69 +221,8 @@ public static class CommonCSharpTests
 | 
			
		||||
            TestCase("Save succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Query.Save("tbl"),
 | 
			
		||||
                    $"INSERT INTO tbl VALUES (@data) ON CONFLICT ((data ->> 'Id')) DO UPDATE SET data = EXCLUDED.data",
 | 
			
		||||
                    "INSERT INTO tbl VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data",
 | 
			
		||||
                    "INSERT ON CONFLICT UPDATE statement not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Update succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data ->> 'Id' = @id",
 | 
			
		||||
                    "UPDATE full statement not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Count", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("All succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl", "Count query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    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");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Exists", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Exists.ById("tbl"),
 | 
			
		||||
                        "SELECT EXISTS (SELECT 1 FROM tbl WHERE data ->> 'Id' = @id) AS it",
 | 
			
		||||
                        "ID existence query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    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");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Find", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data ->> 'Id' = @id",
 | 
			
		||||
                        "SELECT by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    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");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Delete", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data ->> 'Id' = @id",
 | 
			
		||||
                        "DELETE by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Query.Delete.ByField("tbl", Field.NEX("gone")),
 | 
			
		||||
                        "DELETE FROM tbl WHERE data ->> 'gone' IS NULL",
 | 
			
		||||
                        "DELETE by JSON comparison query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -47,42 +47,48 @@ public static class PostgresCSharpTests
 | 
			
		||||
                {
 | 
			
		||||
                    var it = Parameters.AddField("@it", Field.EX("It"), Enumerable.Empty<Tuple<string, SqlValue>>());
 | 
			
		||||
                    Expect.isEmpty(it, "There should not have been any parameters added");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when two parameters are added", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    var it = Parameters.AddField("@field", Field.BT("that", "eh", "zed"),
 | 
			
		||||
                        Enumerable.Empty<Tuple<string, SqlValue>>()).ToList();
 | 
			
		||||
                    Expect.hasLength(it, 2, "There should have been 2 parameters added");
 | 
			
		||||
                    Expect.equal(it[0].Item1, "@fieldmin", "Minimum field name not correct");
 | 
			
		||||
                    Expect.isTrue(it[0].Item2.IsParameter, "Minimum field parameter value incorrect");
 | 
			
		||||
                    Expect.equal(it[1].Item1, "@fieldmax", "Maximum field name not correct");
 | 
			
		||||
                    Expect.isTrue(it[1].Item2.IsParameter, "Maximum field parameter value incorrect");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("RemoveFields", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ById("tbl"),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data ->> 'Id' = @id",
 | 
			
		||||
                        "Remove field by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0)),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data ->> 'Fly' < @field",
 | 
			
		||||
                        "Remove field by field query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByContains succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByContains("tbl"),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data @> @criteria",
 | 
			
		||||
                        "Remove field by contains query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByJsonPath succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByJsonPath("tbl"),
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data @? @path::jsonpath",
 | 
			
		||||
                        "Remove field by JSON path query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("None succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.isEmpty(Parameters.None, "The no-params sequence should be empty");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("Query", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestList("WhereByField", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when a logical operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.WhereByField(Field.GT("theField", 0), "@test"),
 | 
			
		||||
                        "data->>'theField' > @test", "WHERE clause not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when an existence operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.WhereByField(Field.NEX("thatField"), ""), "data->>'thatField' IS NULL",
 | 
			
		||||
                        "WHERE clause not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a between operator is passed with numeric values", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.WhereByField(Field.BT("aField", 50, 99), "@range"),
 | 
			
		||||
                        "(data->>'aField')::numeric BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a between operator is passed with non-numeric values", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.WhereByField(Field.BT("field0", "a", "b"), "@alpha"),
 | 
			
		||||
                        "data->>'field0' BETWEEN @alphamin AND @alphamax", "WHERE clause not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("WhereById succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Definition", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("EnsureTable succeeds", () =>
 | 
			
		||||
@ -107,6 +113,11 @@ public static class PostgresCSharpTests
 | 
			
		||||
                        "CREATE INDEX statement not constructed correctly");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Update succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id",
 | 
			
		||||
                    "UPDATE full statement not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("WhereDataContains succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test",
 | 
			
		||||
@ -119,6 +130,17 @@ public static class PostgresCSharpTests
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Count", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("All succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Count.All(PostgresDb.TableName),
 | 
			
		||||
                        $"SELECT COUNT(*) AS it FROM {PostgresDb.TableName}", "Count query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Count.ByField(PostgresDb.TableName, Field.EQ("thatField", 0)),
 | 
			
		||||
                        $"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data->>'thatField' = @field",
 | 
			
		||||
                        "JSON field text comparison count query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByContains succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Count.ByContains(PostgresDb.TableName),
 | 
			
		||||
@ -134,6 +156,18 @@ public static class PostgresCSharpTests
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Exists", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Exists.ById(PostgresDb.TableName),
 | 
			
		||||
                        $"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Id' = @id) AS it",
 | 
			
		||||
                        "ID existence query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Exists.ByField(PostgresDb.TableName, Field.LT("Test", 0)),
 | 
			
		||||
                        $"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Test' < @field) AS it",
 | 
			
		||||
                        "JSON field text comparison exists query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByContains succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Exists.ByContains(PostgresDb.TableName),
 | 
			
		||||
@ -149,6 +183,18 @@ public static class PostgresCSharpTests
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Find", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Find.ById(PostgresDb.TableName),
 | 
			
		||||
                        $"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Id' = @id",
 | 
			
		||||
                        "SELECT by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Find.ByField(PostgresDb.TableName, Field.GE("Golf", 0)),
 | 
			
		||||
                        $"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Golf' >= @field",
 | 
			
		||||
                        "SELECT by JSON comparison query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("byContains succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Find.ByContains(PostgresDb.TableName),
 | 
			
		||||
@ -167,13 +213,13 @@ public static class PostgresCSharpTests
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Patch.ById(PostgresDb.TableName),
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Id' = @id",
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data->>'Id' = @id",
 | 
			
		||||
                        "UPDATE partial by ID statement not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Patch.ByField(PostgresDb.TableName, Field.LT("Snail", 0)),
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Snail' < @field",
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data->>'Snail' < @field",
 | 
			
		||||
                        "UPDATE partial by ID statement not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByContains succeeds", () =>
 | 
			
		||||
@ -189,8 +235,47 @@ public static class PostgresCSharpTests
 | 
			
		||||
                        "UPDATE partial by JSON Path statement not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("RemoveFields", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ById(PostgresDb.TableName),
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data->>'Id' = @id",
 | 
			
		||||
                        "Remove field by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByField(PostgresDb.TableName, Field.LT("Fly", 0)),
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data->>'Fly' < @field",
 | 
			
		||||
                        "Remove field by field query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByContains succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByContains(PostgresDb.TableName),
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data @> @criteria",
 | 
			
		||||
                        "Remove field by contains query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByJsonPath succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.RemoveFields.ByJsonPath(PostgresDb.TableName),
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data @? @path::jsonpath",
 | 
			
		||||
                        "Remove field by JSON path query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Delete", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Delete.ById(PostgresDb.TableName),
 | 
			
		||||
                        $"DELETE FROM {PostgresDb.TableName} WHERE data->>'Id' = @id",
 | 
			
		||||
                        "DELETE by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Delete.ByField(PostgresDb.TableName, Field.NEX("gone")),
 | 
			
		||||
                        $"DELETE FROM {PostgresDb.TableName} WHERE data->>'gone' IS NULL",
 | 
			
		||||
                        "DELETE by JSON comparison query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("byContains succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Postgres.Query.Delete.ByContains(PostgresDb.TableName),
 | 
			
		||||
@ -469,8 +554,8 @@ public static class PostgresCSharpTests
 | 
			
		||||
                await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var theCount = await Count.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
                Expect.equal(theCount, 2, "There should have been 2 matching documents");
 | 
			
		||||
                var theCount = await Count.ByField(PostgresDb.TableName, Field.BT("NumValue", 10, 20));
 | 
			
		||||
                Expect.equal(theCount, 3, "There should have been 3 matching documents");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("ByContains succeeds", async () =>
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -21,23 +21,93 @@ public static class SqliteCSharpTests
 | 
			
		||||
    {
 | 
			
		||||
        TestList("Query", new[]
 | 
			
		||||
        {
 | 
			
		||||
            TestList("WhereByField", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("succeeds when a logical operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.WhereByField(Field.GT("theField", 0), "@test"),
 | 
			
		||||
                        "data->>'theField' > @test", "WHERE clause not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when an existence operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.WhereByField(Field.NEX("thatField"), ""), "data->>'thatField' IS NULL",
 | 
			
		||||
                        "WHERE clause not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when the between operator is passed", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.WhereByField(Field.BT("aField", 50, 99), "@range"),
 | 
			
		||||
                        "data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("WhereById succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Definition.EnsureTable succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"),
 | 
			
		||||
                    "CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)", "CREATE TABLE statement not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Update succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id",
 | 
			
		||||
                    "UPDATE full statement not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Count", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("All succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl",
 | 
			
		||||
                        "Count query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.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");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Exists", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Exists.ById("tbl"),
 | 
			
		||||
                        "SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Id' = @id) AS it",
 | 
			
		||||
                        "ID existence query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.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");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Find", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data->>'Id' = @id",
 | 
			
		||||
                        "SELECT by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Find.ByField("tbl", Field.GE("Golf", 0)),
 | 
			
		||||
                        "SELECT data FROM tbl WHERE data->>'Golf' >= @field",
 | 
			
		||||
                        "SELECT by JSON comparison query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Patch", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Patch.ById("tbl"),
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Id' = @id",
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Id' = @id",
 | 
			
		||||
                        "UPDATE partial by ID statement not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    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 tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field",
 | 
			
		||||
                        "UPDATE partial by JSON comparison query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
@ -46,16 +116,29 @@ public static class SqliteCSharpTests
 | 
			
		||||
                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",
 | 
			
		||||
                        "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",
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' < @field",
 | 
			
		||||
                        "Remove field by field query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            }),
 | 
			
		||||
            TestList("Delete", new[]
 | 
			
		||||
            {
 | 
			
		||||
                TestCase("ById succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data->>'Id' = @id",
 | 
			
		||||
                        "DELETE by ID query not correct");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("ByField succeeds", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    Expect.equal(Sqlite.Query.Delete.ByField("tbl", Field.NEX("gone")),
 | 
			
		||||
                        "DELETE FROM tbl WHERE data->>'gone' IS NULL", "DELETE by JSON comparison query not correct");
 | 
			
		||||
                })
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("Parameters", new[]
 | 
			
		||||
@ -321,8 +404,8 @@ public static class SqliteCSharpTests
 | 
			
		||||
                await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                await LoadDocs();
 | 
			
		||||
 | 
			
		||||
                var theCount = await Count.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"));
 | 
			
		||||
                Expect.equal(theCount, 2L, "There should have been 2 matching documents");
 | 
			
		||||
                var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("NumValue", 10, 20));
 | 
			
		||||
                Expect.equal(theCount, 3L, "There should have been 3 matching documents");
 | 
			
		||||
            })
 | 
			
		||||
        }),
 | 
			
		||||
        TestList("Exists", new[]
 | 
			
		||||
 | 
			
		||||
@ -28,6 +28,9 @@ let all =
 | 
			
		||||
            test "NE succeeds" {
 | 
			
		||||
                Expect.equal (string NE) "<>" "The not equal to operator was not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "BT succeeds" {
 | 
			
		||||
                Expect.equal (string BT) "BETWEEN" """The "between" operator was not correct"""
 | 
			
		||||
            }
 | 
			
		||||
            test "EX succeeds" {
 | 
			
		||||
                Expect.equal (string EX) "IS NOT NULL" """The "exists" operator was not correct"""
 | 
			
		||||
            }
 | 
			
		||||
@ -35,27 +38,64 @@ let all =
 | 
			
		||||
                Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct"""
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "Field" [
 | 
			
		||||
            test "EQ succeeds" {
 | 
			
		||||
                let field = Field.EQ "Test" 14
 | 
			
		||||
                Expect.equal field.Name "Test" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op EQ "Operator incorrect"
 | 
			
		||||
                Expect.equal field.Value 14 "Value incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "GT succeeds" {
 | 
			
		||||
                let field = Field.GT "Great" "night"
 | 
			
		||||
                Expect.equal field.Name "Great" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op GT "Operator incorrect"
 | 
			
		||||
                Expect.equal field.Value "night" "Value incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "GE succeeds" {
 | 
			
		||||
                let field = Field.GE "Nice" 88L
 | 
			
		||||
                Expect.equal field.Name "Nice" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op GE "Operator incorrect"
 | 
			
		||||
                Expect.equal field.Value 88L "Value incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "LT succeeds" {
 | 
			
		||||
                let field = Field.LT "Lesser" "seven"
 | 
			
		||||
                Expect.equal field.Name "Lesser" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op LT "Operator incorrect"
 | 
			
		||||
                Expect.equal field.Value "seven" "Value incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "LE succeeds" {
 | 
			
		||||
                let field = Field.LE "Nobody" "KNOWS";
 | 
			
		||||
                Expect.equal field.Name "Nobody" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op LE "Operator incorrect"
 | 
			
		||||
                Expect.equal field.Value "KNOWS" "Value incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "NE succeeds" {
 | 
			
		||||
                let field = Field.NE "Park" "here"
 | 
			
		||||
                Expect.equal field.Name "Park" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op NE "Operator incorrect"
 | 
			
		||||
                Expect.equal field.Value "here" "Value incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "BT succeeds" {
 | 
			
		||||
                let field = Field.BT "Age" 18 49
 | 
			
		||||
                Expect.equal field.Name "Age" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op BT "Operator incorrect"
 | 
			
		||||
                Expect.sequenceEqual (field.Value :?> obj list) [ 18; 49 ] "Value incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "EX succeeds" {
 | 
			
		||||
                let field = Field.EX "Groovy"
 | 
			
		||||
                Expect.equal field.Name "Groovy" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op EX "Operator incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "NEX succeeds" {
 | 
			
		||||
                let field = Field.NEX "Rad"
 | 
			
		||||
                Expect.equal field.Name "Rad" "Field name incorrect"
 | 
			
		||||
                Expect.equal field.Op NEX "Operator incorrect"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "Query" [
 | 
			
		||||
            test "selectFromTable succeeds" {
 | 
			
		||||
                Expect.equal (Query.selectFromTable tbl) $"SELECT data FROM {tbl}" "SELECT statement not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "whereById succeeds" {
 | 
			
		||||
                Expect.equal (Query.whereById "@id") "data ->> 'Id' = @id" "WHERE clause not correct"
 | 
			
		||||
            }
 | 
			
		||||
            testList "whereByField" [
 | 
			
		||||
                test "succeeds when a logical operator is passed" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (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 (Field.NEX "thatField") "")
 | 
			
		||||
                        "data ->> 'thatField' IS NULL"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Definition" [
 | 
			
		||||
                test "ensureTableFor succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
@ -92,68 +132,9 @@ let all =
 | 
			
		||||
            test "save succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.save tbl)
 | 
			
		||||
                    $"INSERT INTO {tbl} VALUES (@data) ON CONFLICT ((data ->> 'Id')) DO UPDATE SET data = EXCLUDED.data"
 | 
			
		||||
                    $"INSERT INTO {tbl} VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data"
 | 
			
		||||
                    "INSERT ON CONFLICT UPDATE statement not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "update succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.update tbl)
 | 
			
		||||
                    $"UPDATE {tbl} SET data = @data WHERE data ->> 'Id' = @id"
 | 
			
		||||
                    "UPDATE full statement not correct"
 | 
			
		||||
            }
 | 
			
		||||
            testList "Count" [
 | 
			
		||||
                test "all succeeds" {
 | 
			
		||||
                    Expect.equal (Query.Count.all tbl) $"SELECT COUNT(*) AS it FROM {tbl}" "Count query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    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"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Exists" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Exists.byId tbl)
 | 
			
		||||
                        $"SELECT EXISTS (SELECT 1 FROM {tbl} WHERE data ->> 'Id' = @id) AS it"
 | 
			
		||||
                        "ID existence query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    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"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Find" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Find.byId tbl)
 | 
			
		||||
                        $"SELECT data FROM {tbl} WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        "SELECT by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    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"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Delete" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Delete.byId tbl)
 | 
			
		||||
                        $"DELETE FROM {tbl} WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        "DELETE by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Delete.byField tbl (Field.NEX "gone"))
 | 
			
		||||
                        $"DELETE FROM {tbl} WHERE data ->> 'gone' IS NULL"
 | 
			
		||||
                        "DELETE by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -34,12 +34,59 @@ let unitTests =
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.EX "tacos") []
 | 
			
		||||
                    Expect.isEmpty paramList "There should not have been any parameters added"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when two parameters are added" {
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.BT "that" "eh" "zed") []
 | 
			
		||||
                    Expect.hasLength paramList 2 "There should have been 2 parameters added"
 | 
			
		||||
                    let min = paramList[0]
 | 
			
		||||
                    Expect.equal (fst min) "@fieldmin" "Minimum field name not correct"
 | 
			
		||||
                    match snd min with
 | 
			
		||||
                    | SqlValue.Parameter value ->
 | 
			
		||||
                        Expect.equal value.ParameterName "@fieldmin" "Minimum parameter name not correct"
 | 
			
		||||
                        Expect.equal value.Value "eh" "Minimum parameter value not correct"
 | 
			
		||||
                    | _ -> Expect.isTrue false "Minimum parameter was not a Parameter type"
 | 
			
		||||
                    let max = paramList[1]
 | 
			
		||||
                    Expect.equal (fst max) "@fieldmax" "Maximum field name not correct"
 | 
			
		||||
                    match snd max with
 | 
			
		||||
                    | SqlValue.Parameter value ->
 | 
			
		||||
                        Expect.equal value.ParameterName "@fieldmax" "Maximum parameter name not correct"
 | 
			
		||||
                        Expect.equal value.Value "zed" "Maximum parameter value not correct"
 | 
			
		||||
                    | _ -> Expect.isTrue false "Maximum parameter was not a Parameter type"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            test "noParams succeeds" {
 | 
			
		||||
                Expect.isEmpty noParams "The no-params sequence should be empty"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "Query" [
 | 
			
		||||
            testList "whereByField" [
 | 
			
		||||
                test "succeeds when a logical operator is passed" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (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 (Field.NEX "thatField") "")
 | 
			
		||||
                        "data->>'thatField' IS NULL"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when a between operator is passed with numeric values" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.whereByField (Field.BT "aField" 50 99) "@range")
 | 
			
		||||
                        "(data->>'aField')::numeric BETWEEN @rangemin AND @rangemax"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when a between operator is passed with non-numeric values" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.whereByField (Field.BT "field0" "a" "b") "@alpha")
 | 
			
		||||
                        "data->>'field0' BETWEEN @alphamin AND @alphamax"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            test "whereById succeeds" {
 | 
			
		||||
                Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"
 | 
			
		||||
            }
 | 
			
		||||
            testList "Definition" [
 | 
			
		||||
                test "ensureTable succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
@ -61,6 +108,12 @@ let unitTests =
 | 
			
		||||
                        "CREATE INDEX statement not constructed correctly"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            test "update succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.update PostgresDb.TableName)
 | 
			
		||||
                    $"UPDATE {PostgresDb.TableName} SET data = @data WHERE data->>'Id' = @id"
 | 
			
		||||
                    "UPDATE full statement not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "whereDataContains succeeds" {
 | 
			
		||||
                Expect.equal (Query.whereDataContains "@test") "data @> @test" "WHERE clause not correct"
 | 
			
		||||
            }
 | 
			
		||||
@ -68,6 +121,18 @@ let unitTests =
 | 
			
		||||
                Expect.equal (Query.whereJsonPathMatches "@path") "data @? @path::jsonpath" "WHERE clause not correct"
 | 
			
		||||
            }
 | 
			
		||||
            testList "Count" [
 | 
			
		||||
                test "all succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Count.all PostgresDb.TableName)
 | 
			
		||||
                        $"SELECT COUNT(*) AS it FROM {PostgresDb.TableName}"
 | 
			
		||||
                        "Count query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Count.byField PostgresDb.TableName (Field.EQ "thatField" 0))
 | 
			
		||||
                        $"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data->>'thatField' = @field"
 | 
			
		||||
                        "JSON field text comparison count query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Count.byContains PostgresDb.TableName)
 | 
			
		||||
@ -82,6 +147,18 @@ let unitTests =
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Exists" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Exists.byId PostgresDb.TableName)
 | 
			
		||||
                        $"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Id' = @id) AS it"
 | 
			
		||||
                        "ID existence query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Exists.byField PostgresDb.TableName (Field.LT "Test" 0))
 | 
			
		||||
                        $"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data->>'Test' < @field) AS it"
 | 
			
		||||
                        "JSON field text comparison exists query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Exists.byContains PostgresDb.TableName)
 | 
			
		||||
@ -96,6 +173,18 @@ let unitTests =
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Find" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Find.byId PostgresDb.TableName)
 | 
			
		||||
                        $"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Id' = @id"
 | 
			
		||||
                        "SELECT by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Find.byField PostgresDb.TableName (Field.GE "Golf" 0))
 | 
			
		||||
                        $"SELECT data FROM {PostgresDb.TableName} WHERE data->>'Golf' >= @field"
 | 
			
		||||
                        "SELECT by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Find.byContains PostgresDb.TableName)
 | 
			
		||||
@ -113,13 +202,13 @@ let unitTests =
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Patch.byId PostgresDb.TableName)
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data->>'Id' = @id"
 | 
			
		||||
                        "UPDATE partial by ID statement not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Patch.byField PostgresDb.TableName (Field.LT "Snail" 0))
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Snail' < @field"
 | 
			
		||||
                        $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data->>'Snail' < @field"
 | 
			
		||||
                        "UPDATE partial by ID statement not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
@ -139,13 +228,13 @@ let unitTests =
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byId "tbl")
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        "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"
 | 
			
		||||
                        "UPDATE tbl SET data = data - @name WHERE data->>'Fly' < @field"
 | 
			
		||||
                        "Remove field by field query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
@ -162,6 +251,18 @@ let unitTests =
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Delete" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Delete.byId PostgresDb.TableName)
 | 
			
		||||
                        $"DELETE FROM {PostgresDb.TableName} WHERE data->>'Id' = @id"
 | 
			
		||||
                        "DELETE by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Delete.byField PostgresDb.TableName (Field.NEX "gone"))
 | 
			
		||||
                        $"DELETE FROM {PostgresDb.TableName} WHERE data->>'gone' IS NULL"
 | 
			
		||||
                        "DELETE by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byContains succeeds" {
 | 
			
		||||
                    Expect.equal (Query.Delete.byContains PostgresDb.TableName)
 | 
			
		||||
                        $"DELETE FROM {PostgresDb.TableName} WHERE data @> @criteria"
 | 
			
		||||
@ -391,8 +492,8 @@ let integrationTests =
 | 
			
		||||
                use db = PostgresDb.BuildDb()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
                
 | 
			
		||||
                let! theCount = Count.byField PostgresDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                Expect.equal theCount 2 "There should have been 2 matching documents"
 | 
			
		||||
                let! theCount = Count.byField PostgresDb.TableName (Field.BT "NumValue" 10 20)
 | 
			
		||||
                Expect.equal theCount 3 "There should have been 3 matching documents"
 | 
			
		||||
            }
 | 
			
		||||
            testTask "byContains succeeds" {
 | 
			
		||||
                use db = PostgresDb.BuildDb()
 | 
			
		||||
 | 
			
		||||
@ -12,23 +12,91 @@ open Types
 | 
			
		||||
let unitTests =
 | 
			
		||||
    testList "Unit" [
 | 
			
		||||
        testList "Query" [
 | 
			
		||||
            testList "whereByField" [
 | 
			
		||||
                test "succeeds when a logical operator is passed" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (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 (Field.NEX "thatField") "")
 | 
			
		||||
                        "data->>'thatField' IS NULL"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when the between operator is passed" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.whereByField (Field.BT "aField" 50 99) "@range")
 | 
			
		||||
                        "data->>'aField' BETWEEN @rangemin AND @rangemax"
 | 
			
		||||
                        "WHERE clause not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            test "whereById succeeds" {
 | 
			
		||||
                Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "Definition.ensureTable succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.Definition.ensureTable "tbl")
 | 
			
		||||
                    "CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)"
 | 
			
		||||
                    "CREATE TABLE statement not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "update succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.update "tbl")
 | 
			
		||||
                    "UPDATE tbl SET data = @data WHERE data->>'Id' = @id"
 | 
			
		||||
                    "UPDATE full statement not correct"
 | 
			
		||||
            }
 | 
			
		||||
            testList "Count" [
 | 
			
		||||
                test "all succeeds" {
 | 
			
		||||
                    Expect.equal (Query.Count.all "tbl") $"SELECT COUNT(*) AS it FROM tbl" "Count query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    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"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Exists" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Exists.byId "tbl")
 | 
			
		||||
                        "SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Id' = @id) AS it"
 | 
			
		||||
                        "ID existence query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    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"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Find" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Find.byId "tbl")
 | 
			
		||||
                        "SELECT data FROM tbl WHERE data->>'Id' = @id"
 | 
			
		||||
                        "SELECT by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    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"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Patch" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Patch.byId "tbl")
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Id' = @id"
 | 
			
		||||
                        "UPDATE partial by ID statement not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Patch.byField "tbl" (Field.NE "Part" 0))
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Part' <> @field"
 | 
			
		||||
                        "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field"
 | 
			
		||||
                        "UPDATE partial by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
@ -36,7 +104,7 @@ let unitTests =
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.RemoveFields.byId "tbl" [ SqliteParameter("@name", "one") ])
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name) WHERE data ->> 'Id' = @id"
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name) WHERE data->>'Id' = @id"
 | 
			
		||||
                        "Remove field by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
@ -45,10 +113,24 @@ let unitTests =
 | 
			
		||||
                             "tbl"
 | 
			
		||||
                             (Field.GT "Fly" 0)
 | 
			
		||||
                             [ SqliteParameter("@name0", "one"); SqliteParameter("@name1", "two") ])
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data ->> 'Fly' > @field"
 | 
			
		||||
                        "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' > @field"
 | 
			
		||||
                        "Remove field by field query not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "Delete" [
 | 
			
		||||
                test "byId succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Delete.byId "tbl")
 | 
			
		||||
                        "DELETE FROM tbl WHERE data->>'Id' = @id"
 | 
			
		||||
                        "DELETE by ID query not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "byField succeeds" {
 | 
			
		||||
                    Expect.equal
 | 
			
		||||
                        (Query.Delete.byField "tbl" (Field.NEX "gone"))
 | 
			
		||||
                        "DELETE FROM tbl WHERE data->>'gone' IS NULL"
 | 
			
		||||
                        "DELETE by JSON comparison query not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        testList "Parameters" [
 | 
			
		||||
            test "idParam succeeds" {
 | 
			
		||||
@ -303,8 +385,8 @@ let integrationTests =
 | 
			
		||||
                use! db = SqliteDb.BuildDb()
 | 
			
		||||
                do! loadDocs ()
 | 
			
		||||
        
 | 
			
		||||
                let! theCount = Count.byField SqliteDb.TableName (Field.EQ "Value" "purple")
 | 
			
		||||
                Expect.equal theCount 2L "There should have been 2 matching documents"
 | 
			
		||||
                let! theCount = Count.byField SqliteDb.TableName (Field.BT "NumValue" 10 20)
 | 
			
		||||
                Expect.equal theCount 3L "There should have been 3 matching documents"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "Exists" [
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user