diff --git a/Packages.props b/Packages.props
index 46ef28be..fa30c81f 100644
--- a/Packages.props
+++ b/Packages.props
@@ -20,6 +20,7 @@
+
diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj b/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj
index 09357bf2..347ffa76 100644
--- a/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj
+++ b/src/FSharp.Data.GraphQL.Server.Middleware/FSharp.Data.GraphQL.Server.Middleware.fsproj
@@ -16,6 +16,7 @@
+
diff --git a/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs b/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs
index da0489d8..2fd5257e 100644
--- a/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs
+++ b/src/FSharp.Data.GraphQL.Server.Middleware/ObjectListFilter.fs
@@ -60,12 +60,15 @@ open System.Reflection
///
[]
type ObjectListFilterLinqOptions<'T, 'D>
- ([] compareDiscriminator : Expression> | null, [] getDiscriminatorValue : (Type -> 'D) | null) =
+ ([] compareDiscriminator : Expression> | null, [] getDiscriminatorValue : (Type -> 'D) | null, [] optimize: bool) =
member _.CompareDiscriminator = compareDiscriminator |> ValueOption.ofObj
member _.GetDiscriminatorValue = getDiscriminatorValue |> ValueOption.ofObj
+ /// Whether perform optimization of a LINQ expression
+ member _.Optimize = optimize
- static member None = ObjectListFilterLinqOptions<'T, 'D> (null, null)
+ /// Empty options with optimization enabled
+ static member None = ObjectListFilterLinqOptions<'T, 'D> (null, null, true)
static member GetCompareDiscriminator (getDiscriminatorValue : Expression>) =
let tParam = Expression.Parameter (typeof<'T>, "x")
@@ -73,13 +76,13 @@ type ObjectListFilterLinqOptions<'T, 'D>
let body = Expression.Equal (Expression.Invoke (getDiscriminatorValue, tParam), dParam)
Expression.Lambda> (body, tParam, dParam)
- new (getDiscriminator : Expression>) =
- ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, null)
- new (compareDiscriminator : Expression>) = ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator, null)
- new (getDiscriminatorValue : Type -> 'D) =
- ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator = null, getDiscriminatorValue = getDiscriminatorValue)
- new (getDiscriminator : Expression>, getDiscriminatorValue : Type -> 'D) =
- ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, getDiscriminatorValue)
+ new (getDiscriminator : Expression>, [] optimize: bool) =
+ ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, null, optimize)
+ new (compareDiscriminator : Expression>, [] optimize: bool) = ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator, null, optimize)
+ new (getDiscriminatorValue : Type -> 'D, [] optimize: bool) =
+ ObjectListFilterLinqOptions<'T, 'D> (compareDiscriminator = null, getDiscriminatorValue = getDiscriminatorValue, optimize = optimize)
+ new (getDiscriminator : Expression>, getDiscriminatorValue : Type -> 'D, [] optimize: bool) =
+ ObjectListFilterLinqOptions<'T, 'D> (ObjectListFilterLinqOptions.GetCompareDiscriminator getDiscriminator, getDiscriminatorValue, optimize)
/// Contains tooling for working with ObjectListFilter.
module ObjectListFilter =
@@ -270,8 +273,12 @@ module ObjectListFilter =
)
let queryExpr =
let param = Expression.Parameter (typeof<'T>, "x")
- let body = buildFilterExpr (SourceExpression param) buildTypeDiscriminatorCheck filter
- whereExpr<'T> query param body
+ if options.Optimize then
+ let body = ExpressionOptimizer.visit(buildFilterExpr (SourceExpression param) buildTypeDiscriminatorCheck filter)
+ whereExpr<'T> query param body
+ else
+ let body = buildFilterExpr (SourceExpression param) buildTypeDiscriminatorCheck filter
+ whereExpr<'T> query param body
// Create and execute the final expression
query.Provider.CreateQuery<'T> (queryExpr)
@@ -282,9 +289,14 @@ module ObjectListFilterExtensions =
type ObjectListFilter with
- member inline filter.ApplyTo<'T, 'D> (query : IQueryable<'T>, [] options : ObjectListFilterLinqOptions<'T, 'D>) =
+ member inline filter.Apply<'T, 'D> (query : IQueryable<'T>) =
+ apply ObjectListFilterLinqOptions<'T, 'D>.None filter query
+
+ member inline filter.Apply<'T, 'D> (query : IQueryable<'T>, [] options : ObjectListFilterLinqOptions<'T, 'D>) =
apply options filter query
type IQueryable<'T> with
- member inline query.Apply (filter : ObjectListFilter, [] options : ObjectListFilterLinqOptions<'T, 'D>) = apply options filter query
+ member inline query.Apply (filter : ObjectListFilter) = apply ObjectListFilterLinqOptions.None filter query
+
+ member inline query.Apply (filter : ObjectListFilter, options : ObjectListFilterLinqOptions<'T, 'D>) = apply options filter query
diff --git a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs
index 998aaf93..7a7970e7 100644
--- a/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs
+++ b/tests/FSharp.Data.GraphQL.Tests/ObjectListFilterLinqTests.fs
@@ -5,11 +5,12 @@ open System
open System.Linq
open FSharp.Data.GraphQL.Types
open FSharp.Data.GraphQL.Server.Middleware
+open FSharp.Data.GraphQL.Server.Middleware.ObjectListFilter.Operators
open FSharp.Data.GraphQL.Tests.LinqTests
[]
let ``ObjectListFilter works with Equals operator`` () =
- let filter = Equals { FieldName = "firstName"; Value = "Jonathan" } // :> IComparable
+ let filter = Equals { FieldName = "firstName"; Value = "Jonathan" }
let queryable = data.AsQueryable ()
let filteredData = queryable.Apply (filter) |> Seq.toList
List.length filteredData |> equals 1
@@ -22,7 +23,7 @@ let ``ObjectListFilter works with Equals operator`` () =
[]
let ``ObjectListFilter works with GreaterThan operator`` () =
- let filter = GreaterThan { FieldName = "id"; Value = 4 } // :> IComparable
+ let filter = GreaterThan { FieldName = "id"; Value = 4 }
let queryable = data.AsQueryable ()
let filteredData = queryable.Apply (filter) |> Seq.toList
List.length filteredData |> equals 1
@@ -35,7 +36,7 @@ let ``ObjectListFilter works with GreaterThan operator`` () =
[]
let ``ObjectListFilter works with GreaterThanOrEqual operator`` () =
- let filter = GreaterThanOrEqual { FieldName = "id"; Value = 4 } // :> IComparable
+ let filter = GreaterThanOrEqual { FieldName = "id"; Value = 4 }
let queryable = data.AsQueryable ()
let filteredData = queryable.Apply (filter) |> Seq.toList
List.length filteredData |> equals 2
@@ -56,7 +57,7 @@ let ``ObjectListFilter works with GreaterThanOrEqual operator`` () =
[]
let ``ObjectListFilter works with LessThan operator`` () =
- let filter = LessThan { FieldName = "id"; Value = 4 } // :> IComparable
+ let filter = LessThan { FieldName = "id"; Value = 4 }
let queryable = data.AsQueryable ()
let filteredData = queryable.Apply (filter) |> Seq.toList
List.length filteredData |> equals 1
@@ -69,7 +70,7 @@ let ``ObjectListFilter works with LessThan operator`` () =
[]
let ``ObjectListFilter works with LessThanOrEqual operator`` () =
- let filter = LessThanOrEqual { FieldName = "id"; Value = 4 } // :> IComparable
+ let filter = LessThanOrEqual { FieldName = "id"; Value = 4 }
let queryable = data.AsQueryable ()
let filteredData = queryable.Apply (filter) |> Seq.toList
List.length filteredData |> equals 2
@@ -155,6 +156,34 @@ let ``ObjectListFilter works with OR operator`` () =
result.Contact |> equals { Email = "b.adams@gmail.com" }
result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ]
+[]
+let ``LINQ tree is balanced after multiple usings of OR operator`` () =
+ let filter =
+ ("firstName" =@@ "J")
+ ||| (("id" ==> 2)
+ ||| (("id" >>> 4)
+ ||| (("lastName" === "Adams")
+ ||| (("lastName" @=@ "e")
+ ||| (("firstName" @=@ "a")
+ ||| ("lastName" @=@ "a"))))))
+ let queryable = data.AsQueryable ()
+ let filteredData = queryable.Apply (filter, ObjectListFilterLinqOptions(optimize = true)) |> Seq.toList
+ List.length filteredData |> equals 3
+ do
+ let result = List.head filteredData
+ result.ID |> equals 4
+ result.FirstName |> equals "Ben"
+ result.LastName |> equals "Adams"
+ result.Contact |> equals { Email = "b.adams@gmail.com" }
+ result.Friends |> equals [ { Email = "j.abrams@gmail.com" }; { Email = "l.trif@gmail.com" } ]
+ do
+ let result = List.last filteredData
+ result.ID |> equals 7
+ result.FirstName |> equals "Jeneffer"
+ result.LastName |> equals "Trif"
+ result.Contact |> equals { Email = "j.trif@gmail.com" }
+ result.Friends |> equals [ { Email = "j.abrams@gmail.com" } ]
+
[]
let ``ObjectListFilter works with IN operator for string type field`` () =
let filter = In { FieldName = "firstName"; Value = [ "Jeneffer"; "Ben" ] }
@@ -378,7 +407,8 @@ let ``ObjectListFilter works with getDiscriminatorValue for Horse`` () =
(function
| t when t = typeof -> t.Name
| t when t = typeof -> t.Name
- | _ -> raise (NotSupportedException "Type not supported"))
+ | _ -> raise (NotSupportedException "Type not supported")),
+ optimize = true
)
let filteredData = queryable.Apply (filter, options) |> Seq.toList
List.length filteredData |> equals 2