From f23f7b90e969dc98969e96858e3cf0dc14630f06 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Thu, 21 Apr 2022 20:36:30 -0400 Subject: [PATCH] WIP toward completeness - Add opt args for the run command - Support Task/Async/sync for all run/write/result cmds - Define builder using funcs from Functions module - Functions module no longer auto-opened - Update README with some of the above --- README.md | 12 +- src/RethinkDb.Driver.FSharp/Builder.fs | 824 ++++++++++++++---- src/RethinkDb.Driver.FSharp/Functions.fs | 269 +++++- src/RethinkDb.Driver.FSharp/OptArgs.fs | 84 ++ .../RethinkDb.Driver.FSharp.fsproj | 6 +- src/RethinkDb.Driver.FSharp/Retry.fs | 36 +- 6 files changed, 1004 insertions(+), 227 deletions(-) diff --git a/README.md b/README.md index 8a481f7..9a6db7d 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ The goal is to provide: - A composable pipeline for creating ReQL statements: ```fsharp +open RethinkDb.Driver.FSharp.Functions + /// string -> (IConnection -> Task) let fetchPost (postId : string) = fromDb "Blog" @@ -25,6 +27,8 @@ let fetchPost (postId : string) = - An F# domain-specific language (DSL) using a `rethink` computation expression (CE): ```fsharp +open RethinkDb.Driver.FSharp + /// string -> (IConnection -> Task) let fetchPost (postId : string) = rethink { @@ -59,19 +63,21 @@ The examples above both use the default retry logic. - Only rename functions/methods where required -Within the CE, there are a few differing names, mostly notably at the start (selecting databases and tables); this is to allow for a more natural language flow. Its names may change in the 0.8.x series; it is the most alpha part of the project at this point. +Within the CE, there are a few differing names, mostly notably at the start (selecting databases and tables); this is to allow for a more natural language flow. Its names may change in the 0.8.x series; it is the most alpha part of the project at this point. Also, while CEs now support overloading (thank you F# 6 developers!), they do not detect if the first value in the tupled arguments is different. This is most noticeable once `result*` or `write*` commands have been issued; these support `Task<'T>`, `Async<'T>`, and synchronous `'T` operations, but the follow-on commands will be different (e.x. `withRetryDefault` (tasks) vs. `withAsyncRetryDefault` vs. `withSyncRetryDefault`). There are also versions of these that support optional arguments (for all) and cancellation tokens (for task/async). -The functions do have to change a bit, since they do not support overloading; an example for `filter` is below. +The functions show this pattern throughout, as functions in a module do not support overloading; an example for `filter` is below. ```fsharp // Function names cannot be polymorphic the way object-oriented methods can, so filter's three overloads become filter (r.HashMap ("age", 30)) // and -filterFunc (fun row -> row.["age"].Eq(30)) +filterFunc (fun row -> row.G("age").Eq 30) // and filterJS "function (row) { return 30 == row['age'] }" ``` +Functions that support optional arguments end with `WithOptArgs`; those that support cancellation tokens end with `WithCancel`; and, those that support both end with `WithOptArgsAndCancel`. + ## Licensing While no specific additional license restrictions exist for this project, there are modifications to the Apache v2 diff --git a/src/RethinkDb.Driver.FSharp/Builder.fs b/src/RethinkDb.Driver.FSharp/Builder.fs index 4f5592a..17ed694 100644 --- a/src/RethinkDb.Driver.FSharp/Builder.fs +++ b/src/RethinkDb.Driver.FSharp/Builder.fs @@ -1,18 +1,17 @@ [] module RethinkDb.Driver.FSharp.RethinkBuilder +open System.Threading +open System.Threading.Tasks open RethinkDb.Driver open RethinkDb.Driver.Ast open RethinkDb.Driver.FSharp +open RethinkDb.Driver.FSharp.Functions open RethinkDb.Driver.Net -open System.Threading.Tasks /// Computation Expression builder for RethinkDB queries type RethinkBuilder<'T> () = - /// Await a Task (avoids using the task CE when we may raise exceptions) - let await = Async.AwaitTask >> Async.RunSynchronously - /// Create a RethinkDB hash map of the given field/value pairs let fieldsToMap (fields : (string * obj) list) = fields @@ -26,6 +25,9 @@ type RethinkBuilder<'T> () = Some parts[0], parts[1] | false -> None, table + /// Return None if the item is null, Some if it is not + let noneIfNull (it : 'T) = match (box >> isNull) it with true -> None | false -> Some it + member _.Bind (expr : ReqlExpr, f : ReqlExpr -> ReqlExpr) = f expr member this.For (expr, f) = this.Bind (expr, f) @@ -36,15 +38,15 @@ type RethinkBuilder<'T> () = /// Specify a database for further commands [] - member _.Db (r : RethinkDB, db : string) = - match db with "" -> invalidArg db "db name cannot be blank" | _ -> r.Db db + member _.Db (_ : RethinkDB, dbName : string) = + match dbName with "" -> invalidArg dbName "db name cannot be blank" | _ -> db dbName /// Identify a table (of form "dbName.tableName"; if no db name, uses default database) [] - member this.Table (r : RethinkDB, table : string) = - match dbAndTable table with - | Some db, tbl -> this.Db(r, db).Table tbl - | None, _ -> r.Table table + member this.Table (r, tableName) = + match dbAndTable tableName with + | Some dbName, tblName -> this.Db (r, dbName) |> table tblName + | None, _ -> fromTable tableName /// Create an equality join with another table [] @@ -55,338 +57,717 @@ type RethinkBuilder<'T> () = /// List all databases [] - member _.DbList (r : RethinkDB) = r.DbList () + member _.DbList (_ : RethinkDB) = dbList () /// Create a database [] - member _.DbCreate (r : RethinkDB, db : string) = r.DbCreate db + member _.DbCreate (_ : RethinkDB, dbName) = dbCreate dbName /// Drop a database [] - member _.DbDrop (r : RethinkDB, db : string) = r.DbDrop db + member _.DbDrop (_ : RethinkDB, dbName) = dbDrop dbName /// List all tables for the default database [] - member _.TableList (r : RethinkDB) = r.TableList () + member _.TableList (_ : RethinkDB) = tableListFromDefault () /// List all tables for the specified database [] - member this.TableList (r : RethinkDB, db : string) = - match db with "" -> this.TableList r | _ -> this.Db(r, db).TableList () + member this.TableList (r, dbName) = + match dbName with "" -> tableListFromDefault () | _ -> this.Db (r, dbName) |> tableList /// Create a table (of form "dbName.tableName"; if no db name, uses default database) [] - member this.TableCreate (r : RethinkDB, table : string) = - match dbAndTable table with - | Some db, tbl -> this.Db(r, db).TableCreate tbl - | None, _ -> r.TableCreate table + member this.TableCreate (r, tableName) = + match dbAndTable tableName with + | Some dbName, tblName -> this.Db (r, dbName) |> tableCreate tblName + | None, _ -> tableCreateInDefault tableName /// Drop a table (of form "dbName.tableName"; if no db name, uses default database) [] - member this.TableDrop (r : RethinkDB, table : string) = - match dbAndTable table with - | Some db, tbl -> this.Db(r, db).TableDrop tbl - | None, _ -> r.TableDrop table + member this.TableDrop (r, tableName) = + match dbAndTable tableName with + | Some dbName, tblName -> this.Db (r, dbName) |> tableDrop tblName + | None, _ -> tableDropFromDefault tableName /// List all indexes for a table [] - member _.IndexList (tbl : Table) = tbl.IndexList () + member _.IndexList tbl = indexList tbl /// Create an index for a table [] - member _.IndexCreate (tbl : Table, index : string) = tbl.IndexCreate index + member _.IndexCreate (tbl, index) = indexCreate index tbl /// Create an index for a table, specifying an optional argument [] - member this.IndexCreate (tbl : Table, index : string, opts : IndexCreateOptArg list) = - this.IndexCreate (tbl, index) |> IndexCreateOptArg.apply opts + member _.IndexCreate (tbl, index, opts) = indexCreateWithOptArgs index opts tbl /// Create an index for a table, using a function to calculate the index [] - member _.IndexCreate (tbl : Table, index : string, f : ReqlExpr -> obj) = tbl.IndexCreate (index, ReqlFunction1 f) + member _.IndexCreate (tbl, index, f) = indexCreateFunc<'T> index f tbl /// Create an index for a table, using a function to calculate the index [] - member this.IndexCreate (tbl : Table, index : string, f : ReqlExpr -> obj, opts : IndexCreateOptArg list) = - this.IndexCreate (tbl, index, f) |> IndexCreateOptArg.apply opts + member _.IndexCreate (tbl, index, f, opts) = indexCreateFuncWithOptArgs<'T> index f opts tbl + + /// Create an index for a table, using a JavaScript function to calculate the index + [] + member _.IndexCreate (tbl, index, js) = indexCreateJS index js tbl + + /// Create an index for a table, using a JavaScript function to calculate the index + [] + member _.IndexCreate (tbl, index, js, opts) = indexCreateJSWithOptArgs index js opts tbl /// Drop an index for a table [] - member _.IndexDrop (tbl : Table, index : string) = tbl.IndexDrop index + member _.IndexDrop (tbl, index) = indexDrop index tbl /// Rename an index on a table [] - member _.IndexRename (tbl : Table, oldName : string, newName : string) = tbl.IndexRename (oldName, newName) + member _.IndexRename (tbl, oldName, newName) = indexRename oldName newName tbl /// Rename an index on a table, specifying an overwrite option [] - member this.IndexRename (tbl : Table, oldName : string, newName : string, arg : IndexRenameOptArg) = - this.IndexRename(tbl, oldName, newName) |> IndexRenameOptArg.apply arg + member _.IndexRename (tbl, oldName, newName, opt) = indexRenameWithOptArg oldName newName opt tbl /// Get the status of all indexes on a table [] - member _.IndexStatus (tbl : Table) = tbl.IndexStatus () + member _.IndexStatus tbl = indexStatusAll tbl /// Get the status of specific indexes on a table [] - member _.IndexStatus (tbl : Table, indexes : string list) = tbl.IndexStatus (Array.ofList indexes) + member _.IndexStatus (tbl, indexes) = indexStatus indexes tbl /// Wait for all indexes on a table to become ready [] - member _.IndexWait (tbl : Table) = tbl.IndexWait () + member _.IndexWait tbl = indexWaitAll tbl /// Wait for specific indexes on a table to become ready [] - member _.IndexWait (tbl : Table, indexes : string list) = tbl.IndexWait (Array.ofList indexes) + member _.IndexWait (tbl, indexes) = indexWait indexes tbl // data retrieval / manipulation /// Get a document from a table by its ID [] - member _.Get (tbl : Table, key : obj) = tbl.Get key + member _.Get (tbl, key : obj) = get key tbl /// Get all documents matching the given primary key value [] - member _.GetAll (tbl : Table, keys : obj list) = - tbl.GetAll (Array.ofList keys) + member _.GetAll (tbl, keys) = getAll (Seq.ofList keys) tbl /// Get all documents matching the given index value [] - member this.GetAll (tbl : Table, keys : obj list, index : string) = - this.GetAll(tbl, keys).OptArg ("index", index) + member _.GetAll (tbl, keys, index) = getAllWithIndex (Seq.ofList keys) index tbl /// Skip a certain number of results [] - member _.Skip (expr : ReqlExpr, toSkip : int) = expr.Skip toSkip + member _.Skip (expr, toSkip) = skip toSkip expr /// Limit the results of this query [] - member _.Limit (expr : ReqlExpr, limit : int) = expr.Limit limit + member _.Limit (expr, toLimit) = limit toLimit expr /// Count documents for the current query [] - member _.Count (expr : ReqlExpr) = expr.Count () + member _.Count expr = count expr + + /// Count documents for the current query + [] + member _.Count (expr, f) = countFunc f expr + + /// Count documents for the current query + [] + member _.Count (expr, js) = countJS js expr /// Filter a query by a single field value [] - member _.Filter (expr : ReqlExpr, field : string, value : obj) = expr.Filter (fieldsToMap [ field, value ]) + member _.Filter (expr, field, value) = filter (fieldsToMap [ field, value ]) expr /// Filter a query by a single field value, including an optional argument [] - member this.Filter (expr : ReqlExpr, field : string, value : obj, opt : FilterOptArg) = - this.Filter (expr, field, value) |> FilterOptArg.apply opt + member _.Filter (expr, field, value, opt) = filterWithOptArgs (fieldsToMap [ field, value ]) opt expr /// Filter a query by multiple field values [] - member _.Filter (expr : ReqlExpr, filter : (string * obj) list) = expr.Filter (fieldsToMap filter) + member _.Filter (expr, filters) = filter (fieldsToMap filters) expr /// Filter a query by multiple field values, including an optional argument [] - member this.Filter (expr : ReqlExpr, filter : (string * obj) list, opt : FilterOptArg) = - this.Filter (expr, filter) |> FilterOptArg.apply opt + member _.Filter (expr, filters, opt) = filterWithOptArgs (fieldsToMap filters) opt expr /// Filter a query by a function [] - member _.Filter (expr : ReqlExpr, f : ReqlExpr -> obj) = expr.Filter (ReqlFunction1 f) + member _.Filter (expr, f) = filterFunc f expr /// Filter a query by a function, including an optional argument [] - member this.Filter (expr : ReqlExpr, f : ReqlExpr -> obj, opt : FilterOptArg) = - this.Filter (expr, f) |> FilterOptArg.apply opt + member _.Filter (expr, f, opt) = filterFuncWithOptArgs f opt expr /// Filter a query by multiple functions (has the effect of ANDing them) [] - member _.Filter (expr : ReqlExpr, fs : (ReqlExpr -> obj) list) : Filter = - (fs |> List.fold (fun (e : ReqlExpr) f -> e.Filter (ReqlFunction1 f)) expr) :?> Filter + member _.Filter (expr, fs) = filterFuncAll fs expr /// Filter a query by multiple functions (has the effect of ANDing them), including an optional argument [] - member this.Filter (expr : ReqlExpr, fs : (ReqlExpr -> obj) list, opt : FilterOptArg) = - this.Filter (expr, fs) |> FilterOptArg.apply opt + member _.Filter (expr, fs, opt) = filterFuncAllWithOptArgs fs opt expr /// Filter a query using a JavaScript expression [] - member _.Filter (expr : ReqlExpr, js : string) = expr.Filter (Javascript js) + member _.Filter (expr, js) = filterJS js expr /// Filter a query using a JavaScript expression, including an optional argument [] - member this.Filter (expr : ReqlExpr, js : string, opt : FilterOptArg) = - this.Filter (expr, js) |> FilterOptArg.apply opt + member _.Filter (expr, js, opt) = filterJSWithOptArgs js opt expr /// Filter a query by a range of values [] - member _.Between (expr : ReqlExpr, lower : obj, upper : obj) = - expr.Between (lower, upper) + member _.Between (expr, lower : obj, upper : obj) = between lower upper expr /// Filter a query by a range of values, using optional arguments [] - member this.Between (expr : ReqlExpr, lower : obj, upper : obj, opts : BetweenOptArg list) = - this.Between (expr, lower, upper) |> BetweenOptArg.apply opts + member _.Between (expr, lower : obj, upper : obj, opts) = betweenWithOptArgs lower upper opts expr - /// Map fields for the current query + /// Map fields for the current query using a function [] - member _.Map (expr : ReqlExpr, f : ReqlExpr -> obj) = expr.Map (ReqlFunction1 f) + member _.Map (expr, f) = mapFunc f expr + + /// Map fields for the current query using a JavaScript function + [] + member _.Map (expr, js) = mapJS js expr /// Exclude the given fields from the output [] - member _.Without (expr : ReqlExpr, fields : obj list) = expr.Without (Array.ofList fields) + member _.Without (expr, fields) = without (Seq.ofList fields) expr /// Combine a left and right selection into a single record [] - member _.Zip (expr : ReqlExpr) = expr.Zip () + member _.Zip expr = zip expr /// Merge a document into the current query [] - member _.Merge (expr : ReqlExpr, f : ReqlExpr -> obj) = expr.Merge (ReqlFunction1 f) + member _.Merge (expr, doc : obj) = merge doc expr + + /// Merge a document into the current query, constructed by a function + [] + member _.Merge (expr, f) = mergeFunc f expr + + /// Merge a document into the current query, constructed by a JavaScript function + [] + member _.Merge (expr, js) = mergeJS js expr /// Pluck (select only) specific fields from the query [] - member _.Pluck (expr : ReqlExpr, fields : string list) = expr.Pluck (Array.ofList fields) + member _.Pluck (expr, fields) = pluck (Seq.ofList fields) expr - /// Order the results by the given field value (ascending) + /// Order the results by the given field name (ascending) [] - member _.OrderBy (expr : ReqlExpr, field : string) = expr.OrderBy field + member _.OrderBy (expr, field) = orderBy field expr - /// Order the results by the given function value - [] - member _.OrderBy (expr : ReqlExpr, f : ReqlExpr -> obj) = expr.OrderBy (ReqlFunction1 f) - - /// Order the results by the given field value (descending) + /// Order the results by the given field name (descending) [] - member _.OrderByDescending (expr : ReqlExpr, field : string) = expr.OrderBy (RethinkDB.R.Desc field) + member _.OrderByDescending (expr, field) = orderByDescending field expr + + /// Order the results by the given index name (ascending) + [] + member _.OrderByIndex (expr, index) = orderByIndex index expr + + /// Order the results by the given index name (descending) + [] + member _.OrderByIndexDescending (expr, index) = orderByIndexDescending index expr + + /// Order the results by the given function value (ascending) + [] + member _.OrderByFunc (expr, f) = orderByFunc f expr + + /// Order the results by the given function value (descending) + [] + member _.OrderByFuncDescending (expr, f) = orderByFuncDescending f expr + + /// Order the results by the given JavaScript function value (ascending) + [] + member _.OrderByJS (expr, js) = orderByJS js expr + + /// Order the results by the given JavaScript function value (descending) + [] + member _.OrderByJSDescending (expr, js) = orderByJSDescending js expr /// Insert a document into the given table [] - member _.Insert (tbl : Table, doc : obj) = tbl.Insert doc + member _.Insert (tbl, doc) = insert<'T> doc tbl + + /// Insert multiple documents into the given table + [] + member _.Insert (tbl, doc) = insertMany<'T> (Seq.ofList doc) tbl /// Insert a document into the given table, using optional arguments [] - member this.Insert (tbl : Table, doc : obj, opts : InsertOptArg list) = - this.Insert (tbl, doc) |> InsertOptArg.apply opts + member _.Insert (tbl, docs, opts) = insertWithOptArgs<'T> docs opts tbl + + /// Insert multiple documents into the given table, using optional arguments + [] + member _.Insert (tbl, docs, opts) = insertManyWithOptArgs<'T> (Seq.ofList docs) opts tbl /// Update specific fields in a document [] - member _.Update (expr : ReqlExpr, fields : (string * obj) list) = expr.Update (fieldsToMap fields) + member _.Update (expr, fields) = update (fieldsToMap fields) expr /// Update specific fields in a document, using optional arguments [] - member this.Update (expr : ReqlExpr, fields : (string * obj) list, args : UpdateOptArg list) = - this.Update (expr, fields) |> UpdateOptArg.apply args + member _.Update (expr, fields, args) = updateWithOptArgs (fieldsToMap fields) args expr /// Update specific fields in a document using a function [] - member _.Update (expr : ReqlExpr, f : ReqlExpr -> obj) = expr.Update (ReqlFunction1 f) + member _.Update (expr, f) = updateFunc<'T> f expr /// Update specific fields in a document using a function, with optional arguments [] - member this.Update (expr : ReqlExpr, f : ReqlExpr -> obj, args : UpdateOptArg list) = - this.Update (expr, f) |> UpdateOptArg.apply args + member _.Update (expr, f, args) = updateFuncWithOptArgs<'T> f args expr /// Update specific fields in a document using a JavaScript function [] - member _.Update (expr : ReqlExpr, js : string) = expr.Update (Javascript js) + member _.Update (expr, js) = updateJS js expr /// Update specific fields in a document using a JavaScript function, with optional arguments [] - member this.Update (expr : ReqlExpr, js : string, args : UpdateOptArg list) = - this.Update (expr, js) |> UpdateOptArg.apply args + member _.Update (expr, js, args) = updateJSWithOptArgs js args expr /// Replace the current query with the specified document [] - member _.Replace (expr : ReqlExpr, doc : obj) = expr.Replace doc + member _.Replace (expr, doc) = replace<'T> doc expr /// Replace the current query with the specified document, using optional arguments [] - member this.Replace (expr : ReqlExpr, doc : obj, args : ReplaceOptArg list) = - this.Replace (expr, doc) |> ReplaceOptArg.apply args + member _.Replace (expr, doc, args) = replaceWithOptArgs<'T> doc args expr /// Replace the current query with document(s) created by a function [] - member _.Replace (expr : ReqlExpr, f : ReqlExpr -> obj) = expr.Replace (ReqlFunction1 f) + member _.Replace (expr, f) = replaceFunc<'T> f expr /// Replace the current query with document(s) created by a function, using optional arguments [] - member this.Replace (expr : ReqlExpr, f : ReqlExpr -> obj, args : ReplaceOptArg list) = - this.Replace (expr, f) |> ReplaceOptArg.apply args + member _.Replace (expr, f, args) = replaceFuncWithOptArgs<'T> f args expr /// Replace the current query with document(s) created by a JavaScript function [] - member _.Replace (expr : ReqlExpr, js : string) = expr.Replace (Javascript js) + member _.Replace (expr, js) = replaceJS js expr /// Replace the current query with document(s) created by a JavaScript function, using optional arguments [] - member this.Replace (expr : ReqlExpr, js : string, args : ReplaceOptArg list) = - this.Replace (expr, js) |> ReplaceOptArg.apply args + member _.Replace (expr, js, args) = replaceJSWithOptArgs js args expr /// Delete the document(s) identified by the current query [] - member _.Delete (expr : ReqlExpr) = expr.Delete () + member _.Delete expr = delete expr /// Delete the document(s) identified by the current query [] - member this.Delete (expr : ReqlExpr, opts : DeleteOptArg list) = - this.Delete expr |> DeleteOptArg.apply opts + member _.Delete (expr, opts) = deleteWithOptArgs opts expr /// Wait for updates to a table to be synchronized to disk [] - member _.Sync (tbl : Table) = tbl.Sync () + member _.Sync tbl = sync tbl // executing queries - /// Execute the query, returning the result of the type specified + /// Execute the query, returning the result of the type specified using the given cancellation token [] - member _.Result (expr : ReqlExpr) : IConnection -> Task<'T> = - fun conn -> backgroundTask { - return! expr.RunResultAsync<'T> conn - } + member _.Result (expr, cancelToken) = fun conn -> backgroundTask { + return! runResultWithCancel<'T> cancelToken conn expr + } + + /// Execute the query, returning the result of the type specified using the given cancellation token + [] + member _.Result (expr, cancelToken, conn) = runResultWithCancel<'T> cancelToken conn expr /// Execute the query, returning the result of the type specified [] - member this.Result (expr, conn) = - this.Result expr conn + member _.Result expr = fun conn -> backgroundTask { + return! runResult<'T> conn expr + } + + /// Execute the query, returning the result of the type specified + [] + member _.Result (expr, conn) = runResult<'T> conn expr + + /// Execute the query, returning the result of the type specified, using optional arguments + [] + member _.Result (expr, args) = fun conn -> backgroundTask { + return! runResultWithOptArgs<'T> args conn expr + } + + /// Execute the query, returning the result of the type specified + [] + member _.Result (expr, args, conn) = runResultWithOptArgs<'T> args conn expr + + /// Execute the query, returning the result of the type specified, using optional arguments and a cancellation token + [] + member _.Result (expr, args, cancelToken) = fun conn -> backgroundTask { + return! runResultWithOptArgsAndCancel<'T> args cancelToken conn expr + } + + /// Execute the query, returning the result of the type specified, using optional arguments and a cancellation token + [] + member _.Result (expr, args, cancelToken, conn) = + runResultWithOptArgsAndCancel<'T> args cancelToken conn expr /// Execute the query, returning the result of the type specified, or None if no result is found [] - member _.ResultOption (expr : ReqlExpr) : IConnection -> Task<'T option> = - fun conn -> backgroundTask { - let! result = expr.RunResultAsync<'T> conn - return match (box >> isNull) result with true -> None | false -> Some result - } + member _.ResultOption expr = fun conn -> backgroundTask { + let! result = runResult<'T> conn expr + return noneIfNull result + } /// Execute the query, returning the result of the type specified, or None if no result is found [] member this.ResultOption (expr, conn) = this.ResultOption expr conn + /// Execute the query with a cancellation token, returning the result of the type specified, or None if no result is + /// found + [] + member _.ResultOption (expr, cancelToken) = fun conn -> backgroundTask { + let! result = runResultWithCancel<'T> cancelToken conn expr + return noneIfNull result + } + + /// Execute the query with a cancellation token, returning the result of the type specified, or None if no result is + /// found + [] + member this.ResultOption (expr : ReqlExpr, cancelToken : CancellationToken, conn) = + this.ResultOption (expr, cancelToken) conn + + /// Execute the query with optional arguments, returning the result of the type specified, or None if no result is + /// found + [] + member _.ResultOption (expr, opts) = fun conn -> backgroundTask { + let! result = runResultWithOptArgs<'T> opts conn expr + return noneIfNull result + } + + /// Execute the query with optional arguments, returning the result of the type specified, or None if no result is + /// found + [] + member this.ResultOption (expr, opts : RunOptArg list, conn) = + this.ResultOption (expr, opts) conn + + /// Execute the query with optional arguments and a cancellation token, returning the result of the type specified, + /// or None if no result is found + [] + member _.ResultOption (expr, opts, cancelToken) = fun conn -> backgroundTask { + let! result = runResultWithOptArgsAndCancel<'T> opts cancelToken conn expr + return noneIfNull result + } + + /// Execute the query with optional arguments and a cancellation token, returning the result of the type specified, + /// or None if no result is found + [] + member this.ResultOption (expr, opts : RunOptArg list, cancelToken : CancellationToken, conn) = + this.ResultOption (expr, opts, cancelToken) conn + + /// Execute the query, returning the result of the type specified using the given cancellation token + [] + member _.AsyncResult (expr, cancelToken) = fun conn -> async { + return! asyncResultWithCancel<'T> cancelToken conn expr + } + + /// Execute the query, returning the result of the type specified using the given cancellation token + [] + member _.AsyncResult (expr, cancelToken, conn) = asyncResultWithCancel<'T> cancelToken conn expr + + /// Execute the query, returning the result of the type specified + [] + member _.AsyncResult expr = fun conn -> async { + return! asyncResult<'T> conn expr + } + + /// Execute the query, returning the result of the type specified + [] + member _.AsyncResult (expr, conn) = asyncResult<'T> conn expr + + /// Execute the query, returning the result of the type specified, using optional arguments + [] + member _.AsyncResult (expr, args) = fun conn -> async { + return! asyncResultWithOptArgs<'T> args conn expr + } + + /// Execute the query, returning the result of the type specified + [] + member _.AsyncResult (expr, args, conn) = asyncResultWithOptArgs<'T> args conn expr + + /// Execute the query, returning the result of the type specified, using optional arguments and a cancellation token + [] + member _.AsyncResult (expr, args, cancelToken) = fun conn -> async { + return! asyncResultWithOptArgsAndCancel<'T> args cancelToken conn expr + } + + /// Execute the query, returning the result of the type specified, using optional arguments and a cancellation token + [] + member _.AsyncResult (expr, args, cancelToken, conn) = + asyncResultWithOptArgsAndCancel<'T> args cancelToken conn expr + + /// Execute the query, returning the result of the type specified, or None if no result is found + [] + member _.AsyncOption expr = fun conn -> async { + let! result = asyncResult<'T> conn expr + return noneIfNull result + } + + /// Execute the query, returning the result of the type specified, or None if no result is found + [] + member this.AsyncOption (expr, conn) = + this.AsyncOption expr conn + + /// Execute the query with a cancellation token, returning the result of the type specified, or None if no result is + /// found + [] + member _.AsyncOption (expr, cancelToken) = fun conn -> async { + let! result = asyncResultWithCancel<'T> cancelToken conn expr + return noneIfNull result + } + + /// Execute the query with a cancellation token, returning the result of the type specified, or None if no result is + /// found + [] + member this.AsyncOption (expr : ReqlExpr, cancelToken : CancellationToken, conn) = + this.AsyncOption (expr, cancelToken) conn + + /// Execute the query with optional arguments, returning the result of the type specified, or None if no result is + /// found + [] + member _.AsyncOption (expr, opts) = fun conn -> async { + let! result = asyncResultWithOptArgs<'T> opts conn expr + return noneIfNull result + } + + /// Execute the query with optional arguments, returning the result of the type specified, or None if no result is + /// found + [] + member this.AsyncOption (expr, opts : RunOptArg list, conn) = + this.AsyncOption (expr, opts) conn + + /// Execute the query with optional arguments and a cancellation token, returning the result of the type specified, + /// or None if no result is found + [] + member _.AsyncOption (expr, opts, cancelToken) = fun conn -> async { + let! result = asyncResultWithOptArgsAndCancel<'T> opts cancelToken conn expr + return noneIfNull result + } + + /// Execute the query with optional arguments and a cancellation token, returning the result of the type specified, + /// or None if no result is found + [] + member this.AsyncOption (expr, opts : RunOptArg list, cancelToken : CancellationToken, conn) = + this.AsyncOption (expr, opts, cancelToken) conn + + /// Execute the query synchronously, returning the result of the type specified + [] + member _.SyncResult expr = fun conn -> syncResult<'T> conn expr + + /// Execute the query synchronously, returning the result of the type specified + [] + member _.SyncResult (expr, conn) = syncResult<'T> conn expr + + /// Execute the query synchronously, returning the result of the type specified, using optional arguments + [] + member _.SyncResult (expr, args) = fun conn -> syncResultWithOptArgs<'T> args conn expr + + /// Execute the query synchronously, returning the result of the type specified + [] + member _.SyncResult (expr, args, conn) = syncResultWithOptArgs<'T> args conn expr + + /// Execute the query synchronously, returning the result of the type specified, or None if no result is found + [] + member _.SyncOption expr = fun conn -> noneIfNull (syncResult<'T> conn expr) + + /// Execute the query synchronously, returning the result of the type specified, or None if no result is found + [] + member _.SyncOption (expr, conn) = noneIfNull (syncResult<'T> conn expr) + + /// Execute the query synchronously with optional arguments, returning the result of the type specified, or None if + /// no result is found + [] + member _.SyncOption (expr, opts) = fun conn -> noneIfNull (syncResultWithOptArgs<'T> opts conn expr) + + /// Execute the query synchronously with optional arguments, returning the result of the type specified, or None if + /// no result is found + [] + member _.SyncOption (expr, opts, conn) = noneIfNull (syncResultWithOptArgs<'T> opts conn expr) + /// Perform a write operation [] - member _.Write (expr : ReqlExpr) : IConnection -> Task = - fun conn -> - let result = expr.RunWriteAsync conn |> await - match result.Errors with - | 0UL -> Task.FromResult result - | _ -> raise <| ReqlRuntimeError result.FirstError + member _.Write expr = fun conn -> runWrite conn expr /// Perform a write operation [] - member this.Write (expr, conn) = - this.Write expr conn + member _.Write (expr, conn) = runWrite conn expr + + /// Perform a write operation using optional arguments + [] + member _.Write (expr, args) = fun conn -> runWriteWithOptArgs args conn expr + /// Perform a write operation using optional arguments + [] + member _.Write (expr, args, conn) = runWriteWithOptArgs args conn expr + + /// Perform a write operation using a cancellation token + [] + member _.Write (expr, cancelToken) = fun conn -> runWriteWithCancel cancelToken conn expr + + /// Perform a write operation using a cancellation token + [] + member _.Write (expr, cancelToken, conn) = runWriteWithCancel cancelToken conn expr + + /// Perform a write operation using optional arguments and a cancellation token + [] + member _.Write (expr, args, cancelToken) = fun conn -> runWriteWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a write operation using optional arguments and a cancellation token + [] + member _.Write (expr, args, cancelToken, conn) = runWriteWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a write operation + [] + member _.AsyncWrite expr = fun conn -> asyncWrite conn expr + + /// Perform a write operation + [] + member _.AsyncWrite (expr, conn) = asyncWrite conn expr + + /// Perform a write operation using optional arguments + [] + member _.AsyncWrite (expr, args) = fun conn -> asyncWriteWithOptArgs args conn expr + + /// Perform a write operation using optional arguments + [] + member _.AsyncWrite (expr, args, conn) = asyncWriteWithOptArgs args conn expr + + /// Perform a write operation using a cancellation token + [] + member _.AsyncWrite (expr, cancelToken) = fun conn -> asyncWriteWithCancel cancelToken conn expr + + /// Perform a write operation using a cancellation token + [] + member _.AsyncWrite (expr, cancelToken, conn) = asyncWriteWithCancel cancelToken conn expr + + /// Perform a write operation using optional arguments and a cancellation token + [] + member _.AsyncWrite (expr, args, cancelToken) = fun conn -> + asyncWriteWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a write operation using optional arguments and a cancellation token + [] + member _.AsyncWrite (expr, args, cancelToken, conn) = asyncWriteWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a synchronous write operation + [] + member _.SyncWrite expr = fun conn -> syncWrite conn expr + + /// Perform a synchronous write operation + [] + member _.SyncWrite (expr, conn) = syncWrite conn expr + + /// Perform a write operation using optional arguments + [] + member _.SyncWrite (expr, args) = fun conn -> syncWriteWithOptArgs args conn expr + + /// Perform a write operation using optional arguments + [] + member _.SyncWrite (expr, args, conn) = syncWriteWithOptArgs args conn expr + + /// Perform a write operation, returning a result even if there are errors + [] + member _.WriteResult expr = fun conn -> runWriteResult conn expr + + /// Perform a write operation, returning a result even if there are errors + [] + member _.WriteResult (expr, conn) = runWriteResult conn expr + + /// Perform a write operation with optional arguments, returning a result even if there are errors + [] + member _.WriteResult (expr, args) = fun conn -> runWriteResultWithOptArgs args conn expr + + /// Perform a write operation with optional arguments, returning a result even if there are errors + [] + member _.WriteResult (expr, args, conn) = runWriteResultWithOptArgs args conn expr + + /// Perform a write operation with a cancellation token, returning a result even if there are errors + [] + member _.WriteResult (expr, cancelToken) = fun conn -> runWriteResultWithCancel cancelToken conn expr + + /// Perform a write operation with a cancellation token, returning a result even if there are errors + [] + member _.WriteResult (expr, cancelToken, conn) = runWriteResultWithCancel cancelToken conn expr + + /// Perform a write operation with optional arguments and a cancellation token, returning a result even if there are + /// errors + [] + member _.WriteResult (expr, args, cancelToken) = fun conn -> + runWriteResultWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a write operation with optional arguments and a cancellation token, returning a result even if there are + /// errors + [] + member _.WriteResult (expr, args, cancelToken, conn) = runWriteResultWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a write operation, returning a result even if there are errors + [] + member _.AsyncWriteResult expr = fun conn -> asyncWriteResult conn expr + + /// Perform a write operation, returning a result even if there are errors + [] + member _.AsyncWriteResult (expr, conn) = asyncWriteResult conn expr + + /// Perform a write operation with optional arguments, returning a result even if there are errors + [] + member _.AsyncWriteResult (expr, args) = fun conn -> asyncWriteResultWithOptArgs args conn expr + + /// Perform a write operation with optional arguments, returning a result even if there are errors + [] + member _.AsyncWriteResult (expr, args, conn) = asyncWriteResultWithOptArgs args conn expr + + /// Perform a write operation with a cancellation token, returning a result even if there are errors + [] + member _.AsyncWriteResult (expr, cancelToken) = fun conn -> asyncWriteResultWithCancel cancelToken conn expr + + /// Perform a write operation with a cancellation token, returning a result even if there are errors + [] + member _.AsyncWriteResult (expr, cancelToken, conn) = asyncWriteResultWithCancel cancelToken conn expr + + /// Perform a write operation with optional arguments and a cancellation token, returning a result even if there are + /// errors + [] + member _.AsyncWriteResult (expr, args, cancelToken) = fun conn -> + asyncWriteResultWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a write operation with optional arguments and a cancellation token, returning a result even if there are + /// errors + [] + member _.AsyncWriteResult (expr, args, cancelToken, conn) = + asyncWriteResultWithOptArgsAndCancel args cancelToken conn expr + + /// Perform a synchronous write operation, returning a result even if there are errors + [] + member _.SyncWriteResult expr = fun conn -> syncWriteResult conn expr + + /// Perform a synchronous write operation, returning a result even if there are errors + [] + member _.SyncWriteResult (expr, conn) = syncWriteResult conn expr + + /// Perform a synchronous write operation with optional arguments, returning a result even if there are errors + [] + member _.SyncWriteResult (expr, args) = fun conn -> syncWriteResultWithOptArgs args conn expr + + /// Perform a synchronous write operation with optional arguments, returning a result even if there are errors + [] + member _.SyncWriteResult (expr, args, conn) = syncWriteResultWithOptArgs args conn expr + /// Ignore the result of an operation [] - member _.IgnoreResult<'T> (f : IConnection -> Task<'T>) = - fun conn -> task { - let! _ = (f conn).ConfigureAwait false - () - } - - /// Ignore the result of an operation - [] - member _.IgnoreResult (f : IConnection -> Task<'T option>) = - fun conn -> task { - let! _ = (f conn).ConfigureAwait false - () - } + member _.IgnoreResult (f : IConnection -> Task<'T>) = fun conn -> task { + let! _ = (f conn).ConfigureAwait false + () + } /// Ignore the result of an operation [] @@ -394,71 +775,166 @@ type RethinkBuilder<'T> () = this.IgnoreResult f conn /// Ignore the result of an operation - [] - member this.IgnoreResult (f : IConnection -> Task<'T option>, conn) = - this.IgnoreResult f conn + [] + member _.IgnoreAsync (f : IConnection -> Async<'T>) = fun conn -> f conn |> Async.Ignore + + /// Ignore the result of an operation + [] + member this.IgnoreAsync (f : IConnection -> Async<'T>, conn) = this.IgnoreAsync f conn + + /// Ignore the result of a synchronous operation + [] + member _.IgnoreSync (f : IConnection -> 'T) = fun conn -> f conn |> ignore + + /// Ignore the result of a synchronous operation + [] + member this.IgnoreSync (f : IConnection -> 'T, conn) = this.IgnoreSync f conn // Reconnection /// Retries a variable number of times, waiting each time for the seconds specified [] - member _.WithRetry (f : IConnection -> Task<'T>, retries) = - Retry.withRetry f retries + member _.WithRetry (f, retries) = withRetry<'T> retries f /// Retries a variable number of times, waiting each time for the seconds specified [] - member _.WithRetry (f : IConnection -> Task<'T option>, retries) = - Retry.withRetry f retries - - /// Retries a variable number of times, waiting each time for the seconds specified - [] - member this.WithRetry (f : IConnection -> Task<'T>, retries, conn) = - this.WithRetry (f, retries) conn + member _.WithRetry (f, retries, conn) = withRetry<'T> retries f conn /// Retries a variable number of times, waiting each time for the seconds specified - [] - member this.WithRetry (f : IConnection -> Task<'T option>, retries, conn) = - this.WithRetry (f, retries) conn + [] + member _.WithRetryOption (f, retries) = withRetry<'T option> retries f + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithRetryOption (f, retries, conn) = withRetry<'T option> retries f conn + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithAsyncRetry (f, retries) = withAsyncRetry<'T> retries f + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithAsyncRetry (f, retries, conn) = withAsyncRetry<'T> retries f conn + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithAsyncRetryOption (f, retries) = withAsyncRetry<'T option> retries f + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithAsyncRetryOption (f, retries, conn) = withAsyncRetry<'T option> retries f conn + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithSyncRetry (f, retries) = withSyncRetry<'T> retries f + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithSyncRetry (f, retries, conn) = withSyncRetry<'T> retries f conn + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithSyncRetryOption (f, retries) = withSyncRetry<'T option> retries f + + /// Retries a variable number of times, waiting each time for the seconds specified + [] + member _.WithSyncRetryOption (f, retries, conn) = withSyncRetry<'T option> retries f conn /// Retries at 200ms, 500ms, and 1s [] - member _.WithRetryDefault (f : IConnection -> Task<'T>) = - Retry.withRetryDefault f + member _.WithRetryDefault f = withRetryDefault<'T> f /// Retries at 200ms, 500ms, and 1s [] - member _.WithRetryDefault (f : IConnection -> Task<'T option>) = - Retry.withRetryDefault f + member _.WithRetryDefault (f, conn) = withRetryDefault<'T> f conn /// Retries at 200ms, 500ms, and 1s - [] - member this.WithRetryDefault (f : IConnection -> Task<'T>, conn) = - this.WithRetryDefault f conn + [] + member _.WithRetryOptionDefault f = withRetryDefault<'T option> f /// Retries at 200ms, 500ms, and 1s - [] - member this.WithRetryDefault (f : IConnection -> Task<'T option>, conn) = - this.WithRetryDefault f conn + [] + member _.WithRetryOptionDefault (f, conn) = withRetryDefault<'T option> f conn + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithAsyncRetryDefault f = withAsyncRetryDefault<'T> f + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithAsyncRetryDefault (f, conn) = withAsyncRetryDefault<'T> f conn + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithAsyncRetryOptionDefault f = withAsyncRetryDefault<'T option> f + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithAsyncRetryOptionDefault (f, conn) = withAsyncRetryDefault<'T option> f conn + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithSyncRetryDefault f = withSyncRetryDefault<'T> f + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithSyncRetryDefault (f, conn) = withSyncRetryDefault<'T> f conn + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithSyncRetryOptionDefault f = withSyncRetryDefault<'T option> f + + /// Retries at 200ms, 500ms, and 1s + [] + member _.WithSyncRetryOptionDefault (f, conn) = withSyncRetryDefault<'T option> f conn /// Retries once immediately [] - member _.WithRetryOnce (f : IConnection -> Task<'T>) = - Retry.withRetryOnce f + member _.WithRetryOnce f = withRetryOnce<'T> f /// Retries once immediately [] - member _.WithRetryOnce (f : IConnection -> Task<'T option>) = - Retry.withRetryOnce f + member _.WithRetryOnce (f, conn) = withRetryOnce<'T> f conn /// Retries once immediately - [] - member this.WithRetryOnce (f : IConnection -> Task<'T>, conn) = - this.WithRetryOnce f conn + [] + member _.WithRetryOptionOnce f = withRetryOnce<'T option> f /// Retries once immediately - [] - member this.WithRetryOnce (f : IConnection -> Task<'T option>, conn) = - this.WithRetryOnce f conn + [] + member _.WithRetryOptionOnce (f, conn) = withRetryOnce<'T option> f conn + + /// Retries once immediately + [] + member _.WithAsyncRetryOnce f = withAsyncRetryOnce<'T> f + + /// Retries once immediately + [] + member _.WithAsyncRetryOnce (f, conn) = withAsyncRetryOnce<'T> f conn + + /// Retries once immediately + [] + member _.WithAsyncRetryOptionOnce f = withAsyncRetryOnce<'T option> f + + /// Retries once immediately + [] + member _.WithAsyncRetryOptionOnce (f, conn) = withAsyncRetryOnce<'T option> f conn + + /// Retries once immediately + [] + member _.WithSyncRetryOnce f = withSyncRetryOnce<'T> f + + /// Retries once immediately + [] + member _.WithSyncRetryOnce (f, conn) = withSyncRetryOnce<'T> f conn + + /// Retries once immediately + [] + member _.WithSyncRetryOptionOnce f = withSyncRetryOnce<'T option> f + + /// Retries once immediately + [] + member _.WithSyncRetryOptionOnce (f, conn) = withSyncRetryOnce<'T option> f conn /// RethinkDB computation expression diff --git a/src/RethinkDb.Driver.FSharp/Functions.fs b/src/RethinkDb.Driver.FSharp/Functions.fs index 7437897..6449462 100644 --- a/src/RethinkDb.Driver.FSharp/Functions.fs +++ b/src/RethinkDb.Driver.FSharp/Functions.fs @@ -1,6 +1,7 @@ -[] +/// The function-based Domain-Specific Language (DSL) for RethinkDB module RethinkDb.Driver.FSharp.Functions +open System.Threading open RethinkDb.Driver open RethinkDb.Driver.Ast @@ -18,29 +19,118 @@ let asyncCursor<'T> conn (expr : ReqlExpr) = expr.RunCursorAsync<'T> conn |> Async.AwaitTask -/// Get the result of a non-select ReQL expression -let asyncReqlResult conn (expr : ReqlExpr) = - expr.RunWriteAsync conn - |> Async.AwaitTask - -/// Write a ReQL command, always returning a result -let runWriteResult (expr : ReqlExpr) = - expr.RunWriteAsync +/// Raise an exception if a write command encountered an error +let private raiseIfWriteError (result : Model.Result) = + match result.Errors with + | 0UL -> result + | _ -> raise <| ReqlRuntimeError result.FirstError + +/// Write a ReQL command, raising an exception if an error occurs +let runWriteWithCancel cancelToken conn (expr : ReqlExpr) = backgroundTask { + let! result = expr.RunWriteAsync (conn, cancelToken) + return raiseIfWriteError result +} /// Write a ReQL command, raising an exception if an error occurs -let runWrite (expr : ReqlExpr) = fun conn -> backgroundTask { - let! result = expr.RunWriteAsync conn - if result.Errors > 0UL then raise <| ReqlRuntimeError result.FirstError - return result +let runWrite conn expr = runWriteWithCancel CancellationToken.None conn expr + +/// Write a ReQL command with optional arguments, raising an exception if an error occurs +let runWriteWithOptArgsAndCancel args cancelToken conn (expr : ReqlExpr) = backgroundTask { + let! result = expr.RunWriteAsync (conn, RunOptArg.create args, cancelToken) + return raiseIfWriteError result } - -/// Get the results of an expression -let asyncResult<'T> conn (expr : ReqlExpr) = - expr.RunResultAsync<'T> conn - |> Async.AwaitTask + +/// Write a ReQL command with optional arguments, raising an exception if an error occurs +let runWriteWithOptArgs args conn expr = runWriteWithOptArgsAndCancel args CancellationToken.None conn expr + +/// Write a ReQL command, raising an exception if an error occurs +let asyncWrite conn expr = runWrite conn expr |> Async.AwaitTask + +/// Write a ReQL command with optional arguments, raising an exception if an error occurs +let asyncWriteWithOptArgs args conn expr = runWriteWithOptArgs args conn expr |> Async.AwaitTask + +/// Write a ReQL command, raising an exception if an error occurs +let asyncWriteWithCancel cancelToken conn expr = runWriteWithCancel cancelToken conn expr |> Async.AwaitTask + +/// Write a ReQL command with optional arguments, raising an exception if an error occurs +let asyncWriteWithOptArgsAndCancel args cancelToken conn expr = + runWriteWithOptArgsAndCancel args cancelToken conn expr |> Async.AwaitTask + +/// Write a ReQL command synchronously, raising an exception if an error occurs +let syncWrite conn expr = asyncWrite conn expr |> Async.RunSynchronously + +/// Write a ReQL command synchronously with optional arguments, raising an exception if an error occurs +let syncWriteWithOptArgs args conn expr = asyncWriteWithOptArgs args conn expr |> Async.RunSynchronously + +/// Write a ReQL command with a cancellation token, always returning a result +let runWriteResultWithCancel cancelToken conn (expr : ReqlExpr) = + expr.RunWriteAsync (conn, cancelToken) + +/// Write a ReQL command, always returning a result +let runWriteResult conn expr = runWriteResultWithCancel CancellationToken.None conn expr + +/// Write a ReQL command with optional arguments and a cancellation token, always returning a result +let runWriteResultWithOptArgsAndCancel args cancelToken conn (expr : ReqlExpr) = + expr.RunWriteAsync (conn, RunOptArg.create args, cancelToken) + +/// Write a ReQL command with optional arguments, always returning a result +let runWriteResultWithOptArgs args conn expr = runWriteResultWithOptArgsAndCancel args CancellationToken.None conn expr + +/// Write a ReQL command, always returning a result +let asyncWriteResult conn expr = runWriteResult conn expr |> Async.AwaitTask + +/// Write a ReQL command with optional arguments, always returning a result +let asyncWriteResultWithOptArgs args conn expr = runWriteResultWithOptArgs args conn expr |> Async.AwaitTask + +/// Write a ReQL command with a cancellation token, always returning a result +let asyncWriteResultWithCancel cancelToken conn expr = runWriteResultWithCancel cancelToken conn expr |> Async.AwaitTask + +/// Write a ReQL command with optional arguments and a cancellation token, always returning a result +let asyncWriteResultWithOptArgsAndCancel args cancelToken conn expr = + runWriteResultWithOptArgsAndCancel args cancelToken conn expr |> Async.AwaitTask + +/// Write a ReQL command synchronously, always returning a result +let syncWriteResult conn expr = asyncWriteResult conn expr |> Async.RunSynchronously + +/// Write a ReQL command synchronously with optional arguments, always returning a result +let syncWriteResultWithOptArgs args conn expr = asyncWriteResultWithOptArgs args conn expr |> Async.RunSynchronously + +/// Run the ReQL command using a cancellation token, returning the result as the type specified +let runResultWithCancel<'T> cancelToken conn (expr : ReqlExpr) = expr.RunResultAsync<'T> (conn, cancelToken) + +/// Run the ReQL command using optional arguments and a cancellation token, returning the result as the type specified +let runResultWithOptArgsAndCancel<'T> args cancelToken conn (expr : ReqlExpr) = + expr.RunResultAsync<'T> (conn, RunOptArg.create args, cancelToken) /// Run the ReQL command, returning the result as the type specified -let runResult<'T> (expr : ReqlExpr) = expr.RunResultAsync<'T> +let runResult<'T> = runResultWithCancel<'T> CancellationToken.None + +/// Run the ReQL command using optional arguments, returning the result as the type specified +let runResultWithOptArgs<'T> args = runResultWithOptArgsAndCancel<'T> args CancellationToken.None + +/// Run the ReQL command, returning the result as the type specified +let asyncResult<'T> conn expr = + runResult<'T> expr conn |> Async.AwaitTask + +/// Run the ReQL command using optional arguments, returning the result as the type specified +let asyncResultWithOptArgs<'T> args conn expr = + runResultWithOptArgs<'T> args conn expr |> Async.AwaitTask + +/// Run the ReQL command using a cancellation token, returning the result as the type specified +let asyncResultWithCancel<'T> cancelToken conn (expr : ReqlExpr) = + runResultWithCancel<'T> cancelToken conn expr |> Async.AwaitTask + +/// Run the ReQL command using optional arguments and a cancellation token, returning the result as the type specified +let asyncResultWithOptArgsAndCancel<'T> args cancelToken conn expr = + runResultWithOptArgsAndCancel<'T> args cancelToken conn expr |> Async.AwaitTask + +/// Run the ReQL command, returning the result as the type specified +let syncResult<'T> conn expr = + asyncResult<'T> expr conn |> Async.RunSynchronously + +/// Run the ReQL command using optional arguments, returning the result as the type specified +let syncResultWithOptArgs<'T> args conn expr = + asyncResultWithOptArgs<'T> args conn expr |> Async.RunSynchronously /// Get documents between a lower bound and an upper bound based on a primary key let between (lowerKey : obj) (upperKey : obj) (expr : ReqlExpr) = @@ -58,6 +148,18 @@ let betweenIndex (lowerKey : obj) (upperKey : obj) index expr = let connection () = r.Connection () +/// Count the documents in this query +let count (expr : ReqlExpr) = + expr.Count () + +/// Count the documents in this query where the function returns true +let countFunc (f : ReqlExpr -> bool) (expr : ReqlExpr) = + expr.Count (ReqlFunction1 (fun row -> f row :> obj)) + +/// Count the documents in this query where the function returns true +let countJS js (expr : ReqlExpr) = + expr.Count (toJS js) + /// Reference a database let db dbName = match dbName with "" -> r.Db () | _ -> r.Db dbName @@ -110,21 +212,25 @@ let eqJoinJSIndex js table (indexName : string) expr = let filter (filterSpec : obj) (expr : ReqlExpr) = expr.Filter filterSpec -/// Apply optional argument to a filter -let private optArgFilter arg (filter : Filter) = - filter.OptArg (match arg with Default d -> d.reql) - /// Filter documents, providing optional arguments let filterWithOptArgs (filterSpec : obj) arg expr = - filter filterSpec expr |> optArgFilter arg + filter filterSpec expr |> FilterOptArg.apply arg /// Filter documents using a function -let filterFunc<'T> (f : ReqlExpr -> 'T) (expr : ReqlExpr) = +let filterFunc (f : ReqlExpr -> bool) (expr : ReqlExpr) = expr.Filter (ReqlFunction1 (fun row -> f row :> obj)) /// Filter documents using a function, providing optional arguments -let filterFuncWithOptArgs<'T> (f : ReqlExpr -> 'T) arg expr = - filterFunc f expr |> optArgFilter arg +let filterFuncWithOptArgs f arg expr = + filterFunc f expr |> FilterOptArg.apply arg + +/// Filter documents using multiple functions (has the effect of ANDing them) +let filterFuncAll (fs : (ReqlExpr -> bool) list) (expr : ReqlExpr) = + (fs |> List.fold (fun (e : ReqlExpr) f -> filterFunc f e) expr) :?> Filter + +/// Filter documents using multiple functions (has the effect of ANDing them), providing optional arguments +let filterFuncAllWithOptArgs fs arg expr = + filterFuncAll fs expr |> FilterOptArg.apply arg /// Filter documents using JavaScript let filterJS js (expr : ReqlExpr) = @@ -132,7 +238,7 @@ let filterJS js (expr : ReqlExpr) = /// Filter documents using JavaScript, providing optional arguments let filterJSWithOptArgs js arg expr = - filterJS js expr |> optArgFilter arg + filterJS js expr |> FilterOptArg.apply arg /// Get a document by its primary key let get (documentId : obj) (table : Table) = @@ -182,12 +288,12 @@ let indexList (table : Table) = let indexRename (oldName : string) (newName : string) (table : Table) = table.IndexRename (oldName, newName) -/// Rename an index (overwrite will succeed) -let indexRenameWithOverwrite (oldName : string) (newName : string) (table : Table) = - indexRename oldName newName table |> IndexRenameOptArg.apply Overwrite +/// Rename an index (specifying overwrite action) +let indexRenameWithOptArg oldName newName arg table = + indexRename oldName newName table |> IndexRenameOptArg.apply arg /// Get the status of specific indexes for the given table -let indexStatus (table : Table) (indexes : string list) = +let indexStatus (indexes : string list) (table : Table) = table.IndexStatus (Array.ofList indexes) /// Get the status of all indexes for the given table @@ -195,7 +301,7 @@ let indexStatusAll (table : Table) = table.IndexStatus () /// Wait for specific indexes on the given table to become ready -let indexWait (table : Table) (indexes : string list) = +let indexWait (indexes : string list) (table : Table) = table.IndexWait (Array.ofList indexes) /// Wait for all indexes on the given table to become ready @@ -231,13 +337,65 @@ let isEmpty (expr : ReqlExpr) = expr.IsEmpty () /// End a sequence after a given number of elements -let limit n (expr : ReqlExpr) = +let limit (n : int) (expr : ReqlExpr) = expr.Limit n +/// Map the results using a function +let mapFunc f (expr : ReqlExpr) = + expr.Map (ReqlFunction1 f) + +/// Map the results using a JavaScript function +let mapJS js (expr : ReqlExpr) = + expr.Map (toJS js) + +/// Merge the current query with given document +let merge (doc : obj) (expr : ReqlExpr) = + expr.Merge doc + +/// Merge the current query with the results of a function +let mergeFunc f (expr : ReqlExpr) = + expr.Merge (ReqlFunction1 f) + +/// Merge the current query with the results of a JavaScript function +let mergeJS js (expr : ReqlExpr) = + expr.Merge (toJS js) + /// Retrieve the nth element in a sequence let nth n (expr : ReqlExpr) = expr.Nth n +/// Order a sequence by a given field +let orderBy (field : string) (expr : ReqlExpr) = + expr.OrderBy field + +/// Order a sequence in descending order by a given field +let orderByDescending (field : string) (expr : ReqlExpr) = + expr.OrderBy (r.Desc field) + +/// Order a sequence by a given function +let orderByFunc f (expr : ReqlExpr) = + expr.OrderBy (ReqlFunction1 f) + +/// Order a sequence in descending order by a given function +let orderByFuncDescending f (expr : ReqlExpr) = + expr.OrderBy (r.Desc (ReqlFunction1 f)) + +/// Order a sequence by a given index +let orderByIndex (index : string) (expr : ReqlExpr) = + expr.OrderBy().OptArg("index", index) + +/// Order a sequence in descending order by a given index +let orderByIndexDescending (index : string) (expr : ReqlExpr) = + expr.OrderBy().OptArg("index", r.Desc index) + +/// Order a sequence by a given JavaScript function +let orderByJS js (expr : ReqlExpr) = + expr.OrderBy (toJS js) + +/// Order a sequence in descending order by a given JavaScript function +let orderByJSDescending js (expr : ReqlExpr) = + expr.OrderBy (r.Desc (toJS js)) + /// Create an outer join between two sequences, specifying the join condition with a function let outerJoinFunc<'T> (otherSeq : obj) (f : ReqlExpr -> ReqlExpr -> 'T) (expr : ReqlExpr) = expr.OuterJoin (otherSeq, ReqlFunction2 (fun f1 f2 -> f f1 f2 :> obj)) @@ -275,7 +433,7 @@ let replaceJSWithOptArgs js args expr = replaceJS js expr |> ReplaceOptArg.apply args /// Skip a number of elements from the head of a sequence -let skip n (expr : ReqlExpr) = +let skip (n : int) (expr : ReqlExpr) = expr.Skip n /// Ensure changes to a table are written to permanent storage @@ -350,16 +508,43 @@ let zip (expr : ReqlExpr) = // ~~ RETRY ~~ open RethinkDb.Driver.Net -open System.Threading.Tasks /// Retry, delaying for each the seconds provided (if required) -let withRetry intervals (f : IConnection -> Task<'T>) = - Retry.withRetry f intervals +let withRetry<'T> intervals f = + Retry.withRetry<'T> f intervals + +/// Convert an async function to a task function (Polly does not understand F# Async) +let private asyncFuncToTask<'T> (f : IConnection -> Async<'T>) = + fun conn -> f conn |> Async.StartAsTask + +/// Retry, delaying for each the seconds provided (if required) +let withAsyncRetry<'T> intervals f = fun conn -> + withRetry<'T> intervals (asyncFuncToTask f) conn |> Async.AwaitTask + +/// Retry, delaying for each the seconds provided (if required) +let withSyncRetry<'T> intervals f = + Retry.withRetrySync<'T> f intervals /// Retry failed commands with 200ms, 500ms, and 1 second delays -let withRetryDefault (f : IConnection -> Task<'T>) = - Retry.withRetryDefault f +let withRetryDefault<'T> f = + Retry.withRetryDefault<'T> f + +/// Retry, delaying for each the seconds provided (if required) +let withAsyncRetryDefault<'T> f = fun conn -> + withRetryDefault<'T> (asyncFuncToTask f) conn |> Async.AwaitTask + +/// Retry, delaying for each the seconds provided (if required) +let withSyncRetryDefault<'T> f = + Retry.withRetrySyncDefault<'T> f /// Retry failed commands one time with no delay -let withRetryOnce (f : IConnection -> Task<'T>) = - Retry.withRetryOnce f +let withRetryOnce<'T> f = + Retry.withRetryOnce<'T> f + +/// Retry, delaying for each the seconds provided (if required) +let withAsyncRetryOnce<'T> f = fun conn -> + withRetryOnce<'T> (asyncFuncToTask f) conn |> Async.AwaitTask + +/// Retry, delaying for each the seconds provided (if required) +let withSyncRetryOnce<'T> f = + Retry.withRetrySyncOnce<'T> f diff --git a/src/RethinkDb.Driver.FSharp/OptArgs.fs b/src/RethinkDb.Driver.FSharp/OptArgs.fs index c997950..7e12af6 100644 --- a/src/RethinkDb.Driver.FSharp/OptArgs.fs +++ b/src/RethinkDb.Driver.FSharp/OptArgs.fs @@ -215,6 +215,90 @@ module ReplaceOptArg = r +/// How RethinkDB should read the data +type ReadMode = + /// Return values in memory from the primary replica (default) + | Single + /// Return values committed to a majority of replicas + | Majority + /// Return values in memory from an arbitrary replica + | Outdated + + /// The ReQL representation of the read mode + member this.reql = match this with Single -> "single" | Majority -> "majority" | Outdated -> "outdated" + + +/// How the returned data should be formatted +type ReturnFormat = + /// The native representation for the calling environment + | Native + /// The raw representation + | Raw + + /// The ReQL representation of the format + member this.reql = match this with Native -> "native" | Raw -> "raw" + + +/// Optional arguments for the `run` command +type RunOptArg = + /// How data should be read + | ReadMode of ReadMode + /// The time format (raw is JSON) + | TimeFormat of ReturnFormat + /// Whether to profile the query + | Profile of bool + /// The durability of the command + | Durability of Durability + /// The format of grouped data and streams + | GroupFormat of ReturnFormat + /// The database against which this run should be executed + | Db of string + /// The maximum size of a returned array (RethinkDB default is 100,000) + | ArrayLimit of int + /// The format of binary data + | BinaryFormat of ReturnFormat + /// Minimum number of rows to wait before batching results (RethinkDB default is 8) + | MinBatchRows of int + /// Maximum number of rows to wait before batching results (RethinkDB default is no upper bound) + | MaxBatchRows of int + /// Maximum number of bytes to wait before batching results (RethinkDB default is 1MB) + | MaxBatchBytes of int + /// Maximum number of seconds to wait before batching results (RethinkDB default is 0.5) + | MaxBatchSeconds of float + /// Factor to reduce other values for the first batch + | FirstBatchScaleDownFactor of int + + /// The ReQL representation of the optional argument + member this.reql = + let pair = + match this with + | ReadMode mde -> "read_mode", mde.reql :> obj + | TimeFormat fmt -> "time_format", fmt.reql + | Profile prf -> "profile", prf + | Durability dur -> match dur.reql with k, v -> k, v + | GroupFormat fmt -> "group_format", fmt.reql + | Db db -> "db", db + | ArrayLimit lim -> "array_limit", lim + | BinaryFormat fmt -> "binary_format", fmt.reql + | MinBatchRows min -> "min_batch_rows", min + | MaxBatchRows max -> "max_batch_rows", max + | MaxBatchBytes max -> "max_batch_bytes", max + | MaxBatchSeconds max -> "max_batch_seconds", max + | FirstBatchScaleDownFactor fac -> "first_batch_scaledown_factor", fac + fst pair, RethinkDB.R.Expr (snd pair) + +/// Function to support `run` optional arguments +module RunOptArg = + + open RethinkDb.Driver.Model + + /// Create optional argument for a run command + let create (opts : RunOptArg list) = + let args = OptArgs () + opts |> List.iter (fun arg -> args.Add arg.reql) + args + + /// Optional arguments for the `update` statement type UpdateOptArg = /// The durability of the command diff --git a/src/RethinkDb.Driver.FSharp/RethinkDb.Driver.FSharp.fsproj b/src/RethinkDb.Driver.FSharp/RethinkDb.Driver.FSharp.fsproj index 511239a..7e10dc3 100644 --- a/src/RethinkDb.Driver.FSharp/RethinkDb.Driver.FSharp.fsproj +++ b/src/RethinkDb.Driver.FSharp/RethinkDb.Driver.FSharp.fsproj @@ -8,18 +8,18 @@ https://github.com/danieljsummers/RethinkDb.Driver.FSharp false - Alpha; use at your own risk See LICENSE RethinkDB document F# 0.8.0 - alpha-0005 + alpha-0006 + Alpha; use at your own risk - + diff --git a/src/RethinkDb.Driver.FSharp/Retry.fs b/src/RethinkDb.Driver.FSharp/Retry.fs index 3495d3f..c19eb7c 100644 --- a/src/RethinkDb.Driver.FSharp/Retry.fs +++ b/src/RethinkDb.Driver.FSharp/Retry.fs @@ -20,16 +20,42 @@ let retryPolicy (intervals : float seq) (conn : IConnection) = (conn :?> Connection).Reconnect false | false -> ())) +/// Create a retry policy that attempts to reconnect to RethinkDB when a synchronous operation encounters an error +let retryPolicySync (intervals : float seq) (conn : IConnection) = + Policy + .Handle() + .WaitAndRetry( + intervals |> Seq.map TimeSpan.FromSeconds, + System.Action (fun ex _ _ _ -> + printf $"Encountered RethinkDB exception: {ex.Message}" + match ex.Message.Contains "socket" with + | true -> + printf "Reconnecting to RethinkDB" + (conn :?> Connection).Reconnect false + | false -> ())) + /// Perform a query, retrying after each delay specified -let withRetry (f : IConnection -> Task<'T>) retries = +let withRetry<'T> (f : IConnection -> Task<'T>) retries = fun conn -> backgroundTask { return! (retryPolicy retries conn).ExecuteAsync(fun () -> f conn) } +/// Perform a synchronous query, retrying after each delay specified +let withRetrySync<'T> (f : IConnection -> 'T) retries = fun conn -> + (retryPolicySync retries conn).Execute(fun () -> f conn) + /// Retry three times, after 200ms, 500ms, and 1 second -let withRetryDefault f = - withRetry f [ 0.2; 0.5; 1.0 ] +let withRetryDefault<'T> f = + withRetry<'T> f [ 0.2; 0.5; 1.0 ] + +/// Retry three times, after 200ms, 500ms, and 1 second +let withRetrySyncDefault<'T> f = + withRetrySync<'T> f [ 0.2; 0.5; 1.0 ] /// Retry one time immediately -let withRetryOnce f = - withRetry f [ 0.0 ] +let withRetryOnce<'T> f = + withRetry<'T> f [ 0.0 ] + +/// Retry one time immediately +let withRetrySyncOnce<'T> f = + withRetrySync<'T> f [ 0.0 ]