First cut of BT operator (#3)

This commit is contained in:
2024-06-05 17:31:33 -04:00
parent 7d7214e9f2
commit 1707d3ce63
9 changed files with 658 additions and 290 deletions

View File

@@ -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 =