diff --git a/src/Common/Library.fs b/src/Common/Library.fs index 7dd2993..a279b55 100644 --- a/src/Common/Library.fs +++ b/src/Common/Library.fs @@ -98,6 +98,15 @@ type Field = { { this with Qualifier = Some alias } +/// How fields should be matched +[] +type FieldMatch = + /// Any field matches (OR) + | Any + /// All fields match (AND) + | All + + /// The required document serialization implementation type IDocumentSerializer = diff --git a/src/Postgres/Library.fs b/src/Postgres/Library.fs index cd551d4..c13641c 100644 --- a/src/Postgres/Library.fs +++ b/src/Postgres/Library.fs @@ -109,24 +109,46 @@ module Parameters = [] module Query = + /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document + [] + let whereByFields fields howMatched = + let mutable idx = 0 + let nameField () = + let name = $"field{idx}" + idx <- idx + 1 + name + fields + |> List.map (fun it -> + let fieldName = it.Qualifier |> Option.map (fun q -> $"{q}.data") |> Option.defaultValue "data" + let jsonPath = + if it.Name.Contains '.' then "#>>'{" + String.concat "," (it.Name.Split '.') + "}'" + else $"->>'{it.Name}'" + let column = fieldName + jsonPath + match it.Op with + | EX | NEX -> $"{column} {it.Op}" + | BT -> + let p = defaultArg it.ParameterName (nameField ()) + let names = $"{p}min AND {p}max" + let values = it.Value :?> obj list + match values[0] with + | :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64 + | :? decimal | :? single | :? double -> $"({column})::numeric {it.Op} {names}" + | _ -> $"{column} {it.Op} {names}" + | _ -> + let p = defaultArg it.ParameterName (nameField ()) + $"{column} {it.Op} {p}") + |> String.concat (match howMatched with Any -> " OR " | All -> " AND ") + /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document [] + //[] 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}" + whereByFields [ { field with ParameterName = Some paramName } ] Any /// Create a WHERE clause fragment to implement an ID-based query [] let whereById paramName = - whereByField (Field.EQ (Configuration.idField ()) 0) paramName + whereByFields [ { Field.EQ (Configuration.idField ()) 0 with ParameterName = Some paramName } ] Any /// Table and index definition queries module Definition = diff --git a/src/Tests/CommonTests.fs b/src/Tests/CommonTests.fs index fcc35a2..fd8c5e2 100644 --- a/src/Tests/CommonTests.fs +++ b/src/Tests/CommonTests.fs @@ -115,9 +115,9 @@ let all = Expect.equal "@name" field.ParameterName.Value "The parameter name is incorrect" } test "WithQualifier succeeds" { - let field = (Field.EQ "Bill" "Matt").WithParameterName "@joe" + let field = (Field.EQ "Bill" "Matt").WithQualifier "joe" Expect.isSome field.Qualifier "The table qualifier should have been filled" - Expect.equal "@joe" field.Qualifier.Value "The table qualifier is incorrect" + Expect.equal "joe" field.Qualifier.Value "The table qualifier is incorrect" } ] testList "Query" [