Giter Site home page Giter Site logo

alirezanet / gridify Goto Github PK

View Code? Open in Web Editor NEW
675.0 14.0 52.0 2.01 MB

Easy and optimized way to apply Filtering, Sorting, and Pagination using text-based data.

Home Page: https://alirezanet.github.io/Gridify/

License: MIT License

C# 96.73% Shell 0.06% JavaScript 0.37% TypeScript 2.84%
csharp filtering ordering sorting pagination dynamic-queries entity-framework-core gridify vbnet dotnet

gridify's Introduction

Gridify (A Modern Dynamic LINQ library)

GitHub Nuget Static Badge Static Badge NuGet version (Gridify) NPM Version

Gridify

Introduction

Gridify is a dynamic LINQ library that simplifies the process of converting strings to LINQ queries. With exceptional performance and ease-of-use, Gridify makes it effortless to apply filtering, sorting, and pagination using text-based data.

Consider Gridify 𝖺 𝗌𝗂𝗆𝗉𝗅𝖾𝗋 𝖺𝗅𝗍𝖾𝗋𝗇𝖺𝗍𝗂𝗏𝖾 𝗍𝗈 𝗚𝗿𝗮𝗽𝗵𝗤𝗟/𝗢𝗗𝗮𝘁𝗮. 𝖨𝗍 𝖺𝗅𝗅𝗈𝗐𝗌 𝗍𝗁𝖾 𝖿𝗋𝗈𝗇𝗍-𝖾𝗇𝖽 𝗍𝗈 𝗌𝖾𝗅𝖾𝖼𝗍𝗂𝗏𝖾𝗅𝗒 𝖿𝗂𝗅𝗍𝖾𝗋 𝗋𝖾𝗌𝗈𝗎𝗋𝖼𝖾𝗌 𝖺𝗇𝖽 𝗋𝖾𝗊𝗎𝖾𝗌𝗍 𝗈𝗇𝗅𝗒 𝗍𝗁𝖾 𝗇𝖾𝖼𝖾𝗌𝗌𝖺𝗋𝗒 𝗋𝖾𝖼𝗈𝗋𝖽𝗌.

Features

  • Fast and easy to use
  • Supports filtering, sorting, and pagination
  • Supports string to LINQ conversion
  • Supports nested queries and sub-collections
  • Supports string to object mapping
  • Supports query compilation
  • Supports collection indexes
  • Custom Operators
  • Compatible with ORMs, especially Entity Framework
  • Can be used on every collection that LINQ supports
  • Compatible with object-mappers like AutoMapper
  • Compatible with Elasticsearch
  • Javascript/Typescript client

Documentation

Check out our docs at https://alirezanet.github.io/Gridify/.

Articles / Examples

Want to support us?

  • Don't forget to give a ⭐ to this repo on GitHub!
  • Share your feedback and ideas to improve the library!
  • Share the library on your favorite social media and with your friends!
  • Help us to improve the documentation!

Contribution

We welcome contributions! Feel free to send us a pull request. Check out our Contribution Page for more information.

Contributors

Thank you to everyone who has contributed to the Gridify codebase. We appreciate you!

License

This project is licensed under the MIT License.

gridify's People

Contributors

alirezaarabshahi avatar alirezanet avatar cezarstefan avatar chr-ber avatar dependabot[bot] avatar mdonatas avatar ne4ta avatar tsrgy avatar yassinebennani avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gridify's Issues

apply multiple sort

hi MR Alireza
i want apply multiple sort on query

order by A asc , B desc

i test with below pseudo code but just apply last item
for( var o in orderlist)
{
query=quer.applyordering( new gridifyquery( ) { sortby = o.fieldname ,issortasc = o.isasc } )
}

Index parameter fails on nested collections

Describe the bug
Based on the recent added functionality GridifyMapper with additional index parameter, I am getting error System.NullReferenceException: 'Object reference not set to an instance of an object.' When attempting this with nested collections.

To Reproduce
This example raises the error on line var expression = gridifyQuery.GetFilteringExpression(gridifyMapper).Compile();

Code:

using Gridify;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            var test = new Test();
            test.Run();
        }
    }

    public class Test
    {
        public void Run()
        {
            List<Level1> level1List = new List<Level1>();
            level1List.Add(new Level1());

            var gridifyMapper = new GridifyMapper<Level1>().GenerateMappings()
                .AddMap("level4_property1", (q, index) => q.Level2List.Select(x => x.Level3.Level4List[index].Property1));

            var gridifyQuery = new GridifyQuery() { Filter = "level4_property1[0] > 5" };
            var expression = gridifyQuery.GetFilteringExpression(gridifyMapper).Compile();

            var query = level1List.Where(expression);
        }
    }

    public class Level1
    {
        public string Name { get; set; }
        public List<Level2> Level2List = new List<Level2>()
        {
            new Level2()
            {
                Name = "1",
                Level3 = new Level3()
                {
                    Name = "2",
                    Level4List = new List<Level4>()
                    {
                        new Level4() {Name = "3", Property1 = 3, Property2 = 4},
                        new Level4() {Name = "4", Property1 = 4, Property2 = 5},
                        new Level4() {Name = "5", Property1 = 5, Property2 = 6}
                    }
                }
            },

            new Level2()
            {
                Name = "6",
                Level3 = new Level3()
                {
                    Name = "7",
                    Level4List = new List<Level4>()
                    {
                        new Level4() {Name = "8", Property1 = 8, Property2 = 9},
                        new Level4() {Name = "9", Property1 = 9, Property2 = 10},
                        new Level4() {Name = "10", Property1 = 10, Property2 = 11}
                    }
                }
            },

        };
    }

    public class Level2
    {
        public string Name { get; set; }
        public Level3 Level3 = new Level3();
    }

    public class Level3
    {
        public string Name { get; set; }
        public List<Level4> Level4List = new List<Level4>();
    }

    public class Level4
    {
        public string Name { get; set; }
        public double Property1 { get; set; }
        public double Property2 { get; set; }
    }
}

Is it possible to do this with nested collections?

Limit page size

Details

Hello Alireza.

I was wondering is there any way to limit page size? for example page size can't be more than 2000 records. for performance and security reasons.

Chain filtering

Is there a way to introduce chained logic so that there can be sub filters? For example based on the sample code below the filter Filter = "Level2List_Id=101, Level2List_Level3List_Property1>=3.0" might not do what is expected.

It currently finds Level2 items with Id=101 and Level3 items with Property1>=3.0 - but they do not have to be the same item.

Sometimes we need to chain filters so they apply to a single item, eg filter where Level2 item is BOTH Id=101 and also has Level3 items with Property1>=3.0.

For example, something like this

AND:
(Level2List_Id=101, Level2List_Level3List_Property1>=3.0) , Id < 10

OR:
(Level2List_Id=101| Level2List_Level3List_Property1>=3.0) , Id < 10

The example below returns 1 item as both conditions are met but with chained logic should return zero items as there is no data with Level2.Id == 101 and also Level3 Property1 >= 3.0.

        static void Main(string[] args)
        {
            Level1 level1 = new Level1()
            {
                Id = 1,
                Name = "Level1Name",
                Level2List = new List<Level2>()
                {
                    new Level2() { Id = 101, Name = "Level2_1", Level3List = new List<Level3>() { new Level3() { Property1 = 2.0, Property2 = 100.0, Level = 0 } } },
                    new Level2() { Id = 102, Name = "Level2_2", Level3List = new List<Level3>() { new Level3() { Property1 = 3.0, Property2 = 200.0, Level = 1 } } },
                    new Level2() { Id = 103, Name = "Level2_3", Level3List = new List<Level3>() { new Level3() { Property1 = 4.0, Property2 = 300.0, Level = 2 } } }
                }
            };

            Level1[] array = new Level1[1];
            array[0] = level1;
            var ds = array.AsQueryable();

            // Multi Nested Property
            var gq2 = new GridifyQuery { Filter = "Level2List_Id=101, Level2List_Level3List_Property1>=3.0" };
            var gm2 = new GridifyMapper<Level1>()
            .GenerateMappings()
            .AddMap("Level2List_Level3List_Property1", l1 => l1.Level2List.SelectMany(l2 => l2.Level3List).Select(l3 => l3.Property1))
            .AddMap("Level2List_Id", l1 => l1.Level2List.Select(l3 => l3.Id));

            var actual2 = ds.AsQueryable()
               .ApplyFiltering(gq2, gm2)
               .ToList();
        }

It may be required to actually specify what property (object or collection) is passed in the chain for sub-filtering.

Mongo Driver Compatibility

Details

Version
2.7.4

Details
Hi, i to want use this package on MongoDB .NET Driver and all functionalities except Filtering work as it should.
i use ApplyFiltering on IQueryable
Screen Shot 2022-06-06 at 2 24 16 PM

and mongo driver throw below exception

System.ArgumentNullException: Value cannot be null. (Parameter 'itemName')
at MongoDB.Driver.Core.Misc.Ensure.IsNotNull[T](T value, String paramName)
at MongoDB.Driver.Linq.Linq2Implementation.Expressions.WhereExpression..ctor(Expression source, String itemName, Expression predicate)
at MongoDB.Driver.Linq.Linq2Implementation.Processors.BinderHelper.BindWhere(PipelineExpression pipeline, IBindingContext bindingContext, LambdaExpression lambda)
at MongoDB.Driver.Linq.Linq2Implementation.Processors.Pipeline.MethodCallBinders.WhereBinder.Bind(PipelineExpression pipeline, PipelineBindingContext bindingContext, MethodCallExpression node, IEnumerable1 arguments) at MongoDB.Driver.Linq.Linq2Implementation.Processors.MethodInfoMethodCallBinder1.Bind(PipelineExpression pipeline, TBindingContext bindingContext, MethodCallExpression node, IEnumerable1 arguments) at MongoDB.Driver.Linq.Linq2Implementation.Processors.PipelineBinderBase1.BindMethodCall(MethodCallExpression node)
at MongoDB.Driver.Linq.Linq2Implementation.Processors.PipelineBinderBase1.Bind(Expression node) at MongoDB.Driver.Linq.Linq2Implementation.Processors.Pipeline.PipelineBinder.Bind(Expression node, IBsonSerializerRegistry serializerRegistry) at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl1.Prepare(Expression expression)
at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl1.Translate(Expression expression) at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryProviderImpl1.Execute(Expression expression)
at MongoDB.Driver.Linq.Linq2Implementation.MongoQueryableImpl2.GetEnumerator() at System.Collections.Generic.List1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)
at .Infrastructure.RouteRepository.Find(GridifyQuery query) in /Users/almas/Desktop/.Infrastructure/RouteRepository.cs:line 25

this exception happened when lambda Parameters Name is null on where

https://github.com/mongodb/mongo-csharp-driver/blob/93614cccfa52d34a5807477df614aee6a2beb0a6/src/MongoDB.Driver/Linq/Linq2Implementation/Processors/BinderHelper.cs#L49

https://github.com/mongodb/mongo-csharp-driver/blob/93614cccfa52d34a5807477df614aee6a2beb0a6/src/MongoDB.Driver/Linq/Linq2Implementation/Expressions/WhereExpression.cs#L31

i look at CreateExpression method on GridifyMapper
i think if on

var parameter = Expression.Parameter(typeof(T));

fill Expression Parameter Name by nameof(T) it will do the work

  var parameter = Expression.Parameter(typeof(T), nameof(T));

Nested filtering with child element property and collection combination

Version

2.7.1

Details

Hi,

I'm having a problem with a nested collection filtering. Take for example this class structure:

public class Root
{
    public Child1 Child1 { get; set; }
    public IEnumerable<Child2> Child2List { get; set; }
}

public class Child1
{
    public IEnumerable<Child2> Child2List { get; set; }
}

public class Child2
{
    public string Name { get; set; }
}

Then create mapping like this:

var mapping = new GridifyMapper<Root>()
            .AddMap("Child2Name", x => x.Child2List.Select(c => c.Name)) // Works fine
            .AddMap("Child2NameNested", x => x.Child1.Child2List.Select(c => c.Name)); // Throws exception

Then generate filtering expression.

new GridifyQuery {Filter = "Child2NameNested=name"}.GetFilteringExpression(mapping);

When I try to get the filtering expression, the Gridify.GridifyFilteringException: Invalid expression is thrown.

Would it be possible to add support for this type of nested filtering?

Query mapping validator

Some other ideas...

  1. Ability to validate filter strings without raising errors. The following raises an expected error Gridify.GridifyMapperException HResult=0x80131500 Message=Mapping 'XYZ' not found if any part of the filter string doesn't map.
var gridifyFilterQuery = new GridifyQuery() { Filter = "XYZ" };
var gridifyFilterExpression = gridifyFilterQuery.GetFilteringExpression(gridifyDataMapper).Compile(); // Error Mapping 'XYZ' not found`

It would be nice if we could validate the filter, similar to this

var gridifyFilterQuery = new GridifyQuery() { Filter = "XYZ" };
bool validFilter =  gridifyFilterQuery.IsValid(gridifyDataMapper);
  1. Front end, something like this but C#/Gridify centric

Originally posted by @cirrusone in #40 (comment)

Support parsing 0 & 1 for Boolean values

Version

2.7

Details

If we apply filtering on boolean fields (columns) the where expression produces wrong statement.
for example if my model has a boolean field named IsImport and create filter "IsImport =1" and apply GridifyQueryable on it we get this result as where statement :
In DebugView.Expression :
...
.Where(namelessParameter{0} => False)
...

In DebugView.Query :
...
WHERE 0 = 1
...

Steps to reproduce

apply filter on boolean fields

Filtering with Null values

Hi again!

I have some troubles with filtering.

When I apply
"filter": "ResId==1|ResId==2" ...

I get

System.ArgumentException: An item with the same key has already been added. Key: Param_0
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareLambda(LambdaExpression a, LambdaExpression b)

I don't understand why, but this behavior exists only with 1|2 values. 1|3, 1|4.. works and even 2|1 filter works.

And how can I specify NULLable field?
"filter": "ResId==1|ResId==null" ...

I get

System.ArgumentNullException: Value cannot be null. (Parameter 'expression')
   at System.Dynamic.Utils.ContractUtils.RequiresNotNull(Object value, String paramName, Int32 index)

but ResId has int? type.

Can performance of BuildCompiledEvaluator be improved?

Is your feature request related to a problem? Please describe.
The new evaluate features are great but there is quite a large performance difference from the compiled FilteringExpression methods. Can the performance be improved?

In my own data I get the following perfomance for a single filter/evaluate on a large dataset
compiled FilteringExpression: circa 50µs
compiled Evaluator: circa 8000µs

I can't put the data here but below is some Random data showing the performance difference (this data has been randomly created so is not data issue)

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19043.1348 (21H1/May2021Update)
Intel Core i7-4790K CPU 4.00GHz (Haswell), 1 CPU, 8 logical and 4 physical cores
.NET SDK=6.0.100
  [Host]   : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT
  .NET 6.0 : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT

Job=.NET 6.0  Runtime=.NET 6.0  
Method N Mean Error StdDev Ratio RatioSD
Filter 1 34.51 ns 0.076 ns 0.064 ns 1.00 0.00
Evaluate 1 164,582.25 ns 792.866 ns 662.079 ns 4,769.55 23.33
Filter 10 371.26 ns 1.778 ns 1.577 ns 1.00 0.00
Evaluate 10 1,663,048.05 ns 7,002.305 ns 6,549.960 ns 4,481.12 23.22
Filter 100 3,664.04 ns 5.960 ns 5.575 ns 1.00 0.00
Evaluate 100 16,384,316.29 ns 122,861.043 ns 108,913.114 ns 4,471.11 33.26
Filter 1000 35,577.08 ns 44.707 ns 41.819 ns 1.00 0.00
Evaluate 1000 165,678,484.62 ns 763,739.288 ns 637,756.896 ns 4,657.37 16.95

Code:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using Gridify;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;

namespace BenchmarkBetfairAPI
{
    [SimpleJob(RuntimeMoniker.Net60)]
    public class Benchmark2
    {
        [Params(1, 10, 100, 1000)]
        public int N;

        private const string _jsonRandomData = @"[{""_id"":""61991418b1cb9fdbb7c0d90f"",""index"":0,""guid"":""c7a16806-6089-4e9d-bf41-c3196748fc8a"",""isActive"":true,""balance"":""$2,539.81"",""picture"":""http://placehold.it/32x32"",""age"":28,""eyeColor"":""blue"",""name"":""Rebekah Lancaster"",""gender"":""female"",""company"":""BUZZOPIA"",""email"":""[email protected]"",""phone"":""+1 (943) 410-2534"",""address"":""955 Winthrop Street, Motley, Alaska, 1367"",""about"":""Consectetur laboris deserunt ullamco laborum qui. Elit elit culpa ea reprehenderit eiusmod dolore sit nisi exercitation exercitation qui. Non id enim consectetur aliquip. Esse eu amet eu ipsum esse anim.\r\n"",""registered"":""2014-11-01T03:36:05 -00:00"",""latitude"":-7.594734,""longitude"":-86.450065,""tags"":[""ipsum"",""pariatur"",""ipsum"",""duis"",""velit"",""sunt"",""labore""],""friends"":[{""id"":0,""name"":""Briana Rose""},{""id"":1,""name"":""Nellie Gentry""},{""id"":2,""name"":""Lauren Christensen""}],""greeting"":""Hello, Rebekah Lancaster! You have 7 unread messages."",""favoriteFruit"":""strawberry""},{""_id"":""619914186f4d649ea57dae24"",""index"":1,""guid"":""c10017d0-7bee-4d35-827f-e1acae4cd34e"",""isActive"":true,""balance"":""$2,435.39"",""picture"":""http://placehold.it/32x32"",""age"":26,""eyeColor"":""brown"",""name"":""Marianne Dixon"",""gender"":""female"",""company"":""INCUBUS"",""email"":""[email protected]"",""phone"":""+1 (814) 541-2367"",""address"":""288 Bills Place, Chilton, Puerto Rico, 8522"",""about"":""Aute et tempor incididunt ex ea est nisi labore. Ea nulla irure exercitation eu excepteur velit. Nisi dolore amet dolor occaecat duis ullamco ea exercitation consectetur esse et dolor. Nulla non tempor proident deserunt cillum culpa nisi adipisicing. Enim labore ut deserunt commodo excepteur.\r\n"",""registered"":""2017-03-25T08:32:50 -00:00"",""latitude"":-74.531797,""longitude"":123.413538,""tags"":[""ea"",""laborum"",""esse"",""et"",""non"",""incididunt"",""nostrud""],""friends"":[{""id"":0,""name"":""Mayo Mays""},{""id"":1,""name"":""Alba Frazier""},{""id"":2,""name"":""Lilly Dodson""}],""greeting"":""Hello, Marianne Dixon! You have 2 unread messages."",""favoriteFruit"":""banana""},{""_id"":""619914185c7d311604f76e51"",""index"":2,""guid"":""382e4d71-1bd5-4c2b-84a9-be05ad64aef9"",""isActive"":false,""balance"":""$1,500.22"",""picture"":""http://placehold.it/32x32"",""age"":24,""eyeColor"":""green"",""name"":""Terry Pratt"",""gender"":""female"",""company"":""COMDOM"",""email"":""[email protected]"",""phone"":""+1 (841) 417-3492"",""address"":""330 Ashland Place, Matthews, New Jersey, 6345"",""about"":""Eiusmod pariatur deserunt anim velit nulla culpa est velit consectetur mollit tempor incididunt. Est pariatur adipisicing ea consequat duis esse sit ad reprehenderit labore deserunt aliqua. Et sit culpa anim nulla enim nostrud et anim ad mollit. Non sunt dolore ullamco amet duis cillum. Est irure nisi commodo eiusmod mollit. Mollit Lorem eiusmod ullamco ipsum in nostrud est aute.\r\n"",""registered"":""2014-02-15T07:09:38 -00:00"",""latitude"":39.257497,""longitude"":-107.209067,""tags"":[""ex"",""eu"",""eu"",""eiusmod"",""do"",""ullamco"",""proident""],""friends"":[{""id"":0,""name"":""Rose Singleton""},{""id"":1,""name"":""Hubbard Richmond""},{""id"":2,""name"":""Shaffer Fields""}],""greeting"":""Hello, Terry Pratt! You have 6 unread messages."",""favoriteFruit"":""apple""},{""_id"":""619914187d84fac75ac71aee"",""index"":3,""guid"":""f22d0da4-8127-441a-8835-5e521e6897bb"",""isActive"":true,""balance"":""$2,245.92"",""picture"":""http://placehold.it/32x32"",""age"":26,""eyeColor"":""blue"",""name"":""Angelique Ortega"",""gender"":""female"",""company"":""ZAGGLE"",""email"":""[email protected]"",""phone"":""+1 (915) 446-2976"",""address"":""755 Campus Road, Rosedale, District Of Columbia, 407"",""about"":""Qui dolore ad culpa irure aliqua excepteur minim nisi adipisicing. Exercitation irure non nulla nulla aute. Do dolore voluptate culpa labore dolore consectetur id sint non incididunt pariatur id sit.\r\n"",""registered"":""2021-10-19T09:39:14 -01:00"",""latitude"":-89.710554,""longitude"":-64.470535,""tags"":[""labore"",""excepteur"",""ex"",""cupidatat"",""et"",""ut"",""aliqua""],""friends"":[{""id"":0,""name"":""Mildred Case""},{""id"":1,""name"":""Pansy Morton""},{""id"":2,""name"":""Amber Brown""}],""greeting"":""Hello, Angelique Ortega! You have 9 unread messages."",""favoriteFruit"":""apple""},{""_id"":""619914183a9ba122628e8504"",""index"":4,""guid"":""52654a62-5ee9-409a-978c-b38b482bf69a"",""isActive"":false,""balance"":""$2,468.77"",""picture"":""http://placehold.it/32x32"",""age"":20,""eyeColor"":""blue"",""name"":""Jacqueline England"",""gender"":""female"",""company"":""INVENTURE"",""email"":""[email protected]"",""phone"":""+1 (801) 553-2309"",""address"":""545 Halleck Street, Saddlebrooke, Washington, 8073"",""about"":""Sint aute commodo est ad esse qui excepteur velit nostrud. Nostrud nostrud sunt sit excepteur id velit culpa. Officia ea velit proident eu. Minim sit proident laboris nostrud excepteur sint tempor mollit in quis.\r\n"",""registered"":""2021-10-16T12:46:13 -01:00"",""latitude"":-29.528122,""longitude"":105.622315,""tags"":[""duis"",""minim"",""laboris"",""nisi"",""proident"",""dolor"",""ea""],""friends"":[{""id"":0,""name"":""Obrien Cain""},{""id"":1,""name"":""Milagros Downs""},{""id"":2,""name"":""Torres Barlow""}],""greeting"":""Hello, Jacqueline England! You have 1 unread messages."",""favoriteFruit"":""banana""}]";
        private Root[] _deserializedClass;
        Func<Root, bool>? _compiledFilterExpression;
        Func<IEnumerable<Root>, bool> _compiledEvaluator;

        [GlobalSetup]
        public void Setup()
        {
            _deserializedClass = JsonConvert.DeserializeObject<Root[]>(_jsonRandomData);

            // Setup compiled filter
            var gridifyMapper = new GridifyMapper<Root>().GenerateMappings()
                .AddMap("Gender", q => q.Gender);

            var filterQuery = new GridifyQuery() { Filter = "Gender=female" };
            _compiledFilterExpression = filterQuery.GetFilteringExpression(gridifyMapper).Compile();

            // Setup compiled evaluate
            var builder = new QueryBuilder<Root>()
                .AddCondition("Gender=female");

            _compiledEvaluator = builder.BuildCompiledEvaluator();

#if DEBUG
            N = 1;
            Filter();
            Evaluate();
#endif
        }

        [Benchmark(Baseline = true)]
        public void Filter()
        {
            for (int n = 0; n < N; n++)
            {
                var filter1 = _deserializedClass.Where(_compiledFilterExpression);
                var match = filter1.Any();
            }
        }

        [Benchmark]
        public void Evaluate()
        {
            for (int n = 0; n < N; n++)
            {
                var evaluate = _compiledEvaluator(_deserializedClass);
            }
        }
    }

    public class Friend
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class Root
    {
        public string Id { get; set; }
        public int Index { get; set; }
        public string Guid { get; set; }
        public bool IsActive { get; set; }
        public string Balance { get; set; }
        public string Picture { get; set; }
        public int Age { get; set; }
        public string EyeColor { get; set; }
        public string Name { get; set; }
        public string Gender { get; set; }
        public string Company { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public string Address { get; set; }
        public string About { get; set; }
        public string Registered { get; set; }
        public double Latitude { get; set; }
        public double Longitude { get; set; }
        public List<string> Tags { get; set; }
        public List<Friend> Friends { get; set; }
        public string Greeting { get; set; }
        public string FavoriteFruit { get; set; }
    }
}

Error with GridifyQueryable

Hi. Thanks for the great library.
I get an error after applying GridifyQueryable to my query. This error raises only when I change sortBy to some columns. For example my string columns in sortBy works as expected but when I pass Id(int) or StartDate(DateTime) I get:

.. could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().

Example of filter model:

{
    Filter = null,
    IsSortAsc = true,
    Page = 1,
    PageSize = 25,
    SortBy = "startDate"
};

AutoMapper not working

Version

Gridify.EntityFramework 2.83

Details

We are unable to use ProjectTo
Please find the screen shot for your refeance.

image
screenshotreference

Steps to reproduce

Add package and
and try to use auto mapper with GridifyQueryable
you will see the attached error

Filter on nested collections of collections

Thanks for an excellent library.

I've read through the code and couldn't see any examples of filtering from the base collection where there are nested collections inside collections.

Eg is something like this possible without isolating each collection, Filter = "Level2List_Level3List_Property1 >= 3.0"?

I would still want to return items of type Level1 but filter on properties of Level3.


using Gridify;
using System.Collections.Generic;
using System.Linq;

namespace GridifyNestedProperties
{
    class Program
    {
        static void Main(string[] args)
        {
            Level1 level1 = new Level1()
            {
                Id = 1,
                Name = "Level1Name",
                Level2List = new List<Level2>()
                {
                    new Level2() { Id = 101, Name = "Level2_1", Level3List = new List<Level3>() { new Level3() { Property1 = 2.0, Property2 = 100.0, Level = 0 } } },
                    new Level2() { Id = 102, Name = "Level2_2", Level3List = new List<Level3>() { new Level3() { Property1 = 3.0, Property2 = 200.0, Level = 0 } } },
                    new Level2() { Id = 103, Name = "Level2_3", Level3List = new List<Level3>() { new Level3() { Property1 = 4.0, Property2 = 300.0, Level = 0 } } }
                }
            };

            Level1[] array = new Level1[1];
            array[0] = level1;
            var ds = array.AsQueryable();

            // Multi Nested Property
            var gq2 = new GridifyQuery { Filter = "Level2List_Level3List_Property1 >= 3.0" };
            var gm2 = new GridifyMapper<Level1>()
               .GenerateMappings()
               .AddMap("Level2List_Level3List_Property1", q => q.Level2List.SelectMany(x => x.Level3List.SelectMany(y => y.Property1)));

            var actual2 = ds.AsQueryable()
               .ApplyFiltering(gq2, gm2)
               .ToList();

        }
    }

    public class Level1
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Level2> Level2List { get; set; }
    }

    public class Level2
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public List<Level3> Level3List { get; set; }
    }

    public class Level3
    {
        public int Level { get; set; }
        public double Property1 { get; set; }
        public double Property2 { get; set; }
    }
}

Create parameterized queries

This library in-lines the field's values in EF-Core queries. For instance it creates queries like this:

   WHERE [m].[Title] = N'action'

For performance reasons (caching the query plan by SQL Server) it's better to create parameterized queries such as:

   WHERE [m].[Title] =  @__p_0

Better support for value types

Discussed in #72

Originally posted by hottabych07 March 21, 2022
Hi.
Could you help me with the answer to my question.
I have a Value Object called Number

public class Number  : ValueObject
{
   public string Prefix {get;set}
   public DateTime Date {get;set;}
   public int Sequence {get;set;}
}

From this object I form lines such as AFF-01.2022-00001 etc.

In the database I store this value as a string, by using conversion in the ef core.

And it does a great job of filtering that value for the Equal operator. Because your library expands the expression to look like this .Where(p => p.Number == new Number("AFF-01.2022-00001"))

But the problem arises when it comes to the Contains operator. I get an error that you can't call the Contains method on the Number class.

Can you advise me what to do in this situation?

More Details:

It looks like if you make the Number cast to string, everything works very well and you don't need to use EF.Functions .Where(w => ((string)w.Number).Contains("AFF-01.2022-00001"))

Based on this, I tried doing a mapping

var fm = new GridifyMapper() .AddMap("Number", w => (string) w.Number);

But unfortunately it doesn't work and gives error: TypeConverter cannot convert from System.String.

generate mapping for one to many entites

hi
I have query like below :
var mp=new GridifyMapper().GenerateMappings().AddMap("coursename",x=>x.course.where(y=>y.name==@name).count()>0)

i use LinqKit perdicate for this query but i must declare it for all operator (==.contains,not con.....)

how can run by Gridify?

Filtering on a nested collection's field, with surrounding parentheses and exactly 2 passed filter values for the field, generates an incorrect query

Version

2.8.1

Details

I'm filtering on a nested collection, mapped like so:

_gridifyMapper = new GridifyMapper<Model>(true);
_gridifyMapper.AddMap("AppId", a => a.Applications.Select(app => app.AppId));

If I pass it a filter such as appId=1,appId=2, I get something like this (which I think is correct):

WHERE (EXISTS (
        SELECT 1
        FROM [UserApplications] AS [d0]
        WHERE ([u].[UserId] = [d0].[UserId]) AND ([d0].[AppId] = @__Value_2)) AND EXISTS (
        SELECT 1
        FROM [UserApplications] AS [d1]
        WHERE ([u].[UserId] = [d1].[UserId]) AND ([d1].[AppId] = @__Value_3))

However, if I surround the filter string with parentheses, it instead becomes this (which seems incorrect):

WHERE (EXISTS (
        SELECT 1
        FROM [UserApplications] AS [d0]
        WHERE ([u].[UserId] = [d0].[UserId]) AND (([d0].[AppId] = @__Value_2) AND ([d0].[AppId] = @__Value_3))

If I then add a third appId in the filter string, still surrounded with parentheses, it generates the correct query, again.

Seems like a bug to me?

I'm using EF Core 6, if that makes any difference.

Steps to reproduce

  • Create a mapper and map a field to a nested collection.
  • Query with a filter string such as (field=value1,field=value2) (parentheses are important, and it has to be exactly 2 values passed (1 or (3 or more) will generate the correct query).

Index was outside the bounds of the array

Describe the bug
This bug seems to fixed for List but I get errors with arrays, System.IndexOutOfRangeException: 'Index was outside the bounds of the array.' on line var expression = gridifyQuery.GetFilteringExpression(gridifyMapper).Compile();

To Reproduce

using Gridify;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            var test = new Test();
            test.Run();
        }
    }

    public class Test
    {
        public void Run()
        {
            List<Level1> level1List = new List<Level1>();
            level1List.Add(new Level1());

            var gridifyMapper = new GridifyMapper<Level1>().GenerateMappings()
                .AddMap("level4_property1", (q, index) => q.Level2List.Select(x => x.Level3.Level4List[index].Property1));

            var gridifyQuery = new GridifyQuery() { Filter = "level4_property1[0] > 5" };
            var expression = gridifyQuery.GetFilteringExpression(gridifyMapper).Compile();

            var query = level1List.Where(expression);
        }
    }

    public class Level1
    {
        public string Name { get; set; }
        public Level2[] Level2List = new Level2[]
        {
            new Level2()
            {
                Name = "1",
                Level3 = new Level3()
                {
                    Name = "2",
                    Level4List = new List<Level4>()
                    {
                        new Level4() {Name = "3", Property1 = 3, Property2 = 4},
                        new Level4() {Name = "4", Property1 = 4, Property2 = 5},
                        new Level4() {Name = "5", Property1 = 5, Property2 = 6}
                    }
                }
            },

            new Level2()
            {
                Name = "6",
                Level3 = new Level3()
                {
                    Name = "7",
                    Level4List = new List<Level4>()
                    {
                        new Level4() {Name = "8", Property1 = 8, Property2 = 9},
                        new Level4() {Name = "9", Property1 = 9, Property2 = 10},
                        new Level4() {Name = "10", Property1 = 10, Property2 = 11}
                    }
                }
            },

        };
    }

    public class Level2
    {
        public string Name { get; set; }
        public Level3 Level3 = new Level3();
    }

    public class Level3
    {
        public string Name { get; set; }
        public List<Level4> Level4List = new List<Level4>();
    }

    public class Level4
    {
        public string Name { get; set; }
        public double Property1 { get; set; }
        public double Property2 { get; set; }
    }
}

Filter operator for case-insensitive for string

It's usual to filter string data in case-insensitive mode.

A way to do that is add # operator.

var gQuery = new GridifyQuery() { Filter = "FirstName=#John, Address=*#st", OrderBy = "PhoneNumber" };

Regards

Typo in name of nuget package

Version

2.7.1

Details

the name of package in nuget is:
Gridify.EnitityFramework
instead of:
Gridify.EntityFramework

Steps to reproduce

install nuget package

Null value in Custom operator when value is an anonymous object

Version

2.7.1

Details

Hi, I have a custom operator that runs EF.Functions.JsonContains(from PostgreSql)

public class JsonContainsOperator : IGridifyOperator
{
    public string GetOperator() => "#=";
    public Expression<OperatorParameter> OperatorHandler()
    {
        return (prop, value) => EF.Functions.JsonContains(prop, new[] { new { Id = value } });
    }
}

And when I apply the filter, I get a NULL value in the value.

Example

var test = _context.Products.ApplyFiltering("Users #= 1") .ToQueryString();
SELECT i."Id", i."Name", i.Users
FROM View_Products AS i
WHERE i."Users" @> '[{"Id":null}]'

Here is my model, I use SQL View, Users is jsonb

public class ProductsView : Entity
{
    public string Name{ get; set; }
    [Column(TypeName = "jsonb")]
    public IEnumerable<User> Users { get; set; }
}

public class UserView
{
    public int Id { get; set; }
    public string Name { get; set; }
}

What could be the reason? Thank

Steps to reproduce

To reproduce, you can repeat my code above

Processing `is null or empty`

It would be nice to have is null or empty processing by default. For instance if a user queries a Title without any value (Title=), it could be translated to:

Where (Title is null or Title = '')

Also for non-string fields, using their default value would be nice.

Custom search with GUID

Hi

Is there any limitation in using Gridify in Guid property in .Net Core? Because today when I make a query applying the feint informing a Guid correctly, the data is successfully returned to me, but if I inform a "broken" Guid it ignores it and returns all the records:

Example:

http://localhost:5001/api/v1/employee/custom-search?Filter=id==DD0CB126-829F-42FB-B033-F45B2443918E

It works correctly, but if I pass an ID with the broken guid, it is ignored and returns all records, ex:

http://localhost:5001/api/v1/employee/custom-search?Filter=id==DD0CB126-829F-42FB-

Allow for filter expressions to exclude unmapped fields instead of throwing an exception

When upgrading from version 1 to 2, I noticed that if the filter expression contains a column name that cannot be found in the database mapping, the method throws a GridifyFilteringException ("Invalid expression"). In version 1 this was not the case, and the incorrect filter value would simply be skipped when creating the query.

Would it be possible to skip adding the filtering to the column if it was not mapped?

I have fixed the underlying issue on my side by ensuring only mapped column names are included in the filter expression, however I think allowing for graceful fails by simply skipping the unmapped filter expression is safer than throwing an exception.

Support EF.Functions.FreeText

I'd like free text searches to (optionally) be able to use EF.Functions.FreeText rather than a LIKE with wildcards. This would greatly improve the performance of free text searches with wildcards on both sides of the search string.

Conditional operator and Select in GridifyMapper

Version

2.7.4

Details

Hi,
I'm having a problem with a GridifyMapper that has a conditional operator and Select method
Given this class structure:

public class Root
{
    public int Id { get; set; }

    public int? Blog1Id { get; set; }
    public Blog Blog1 { get; set; }

    public int? Blog2Id { get; set; }
    public Blog Blog2 { get; set; }
}

public class Blog
{
    public int Id { get; set; }
    public ICollection<Post> Posts { get; set; }
}

public class Post
{
    public int Id { get; set; }
    public string Text { get; set; }
}

and this mapper:

var mapper = new GridifyMapper<Root>()
    .AddMap("PostText", r => r.Blog1Id != null
        ? r.Blog1.Posts.Select(p => p.Text)
        : r.Blog2.Posts.Select(p => p.Text));

when applying filtering:

var result = _context.Root.ApplyFiltering("PostText = Hello", mapper).ToList();

this exception is thrown:

Gridify.GridifyFilteringException: The 'Select' method on 'PostText' not found
at Gridify.Syntax.ExpressionToQueryConvertor.GenerateNestedExpression[T](IGridifyMapper`1 mapper, IGMap`1 gMap, ValueExpressionSyntax value, SyntaxNode op)
at Gridify.Syntax.ExpressionToQueryConvertor.ConvertBinaryExpressionSyntaxToQuery[T](BinaryExpressionSyntax binarySyntax, IGridifyMapper`1 mapper)
at Gridify.Syntax.ExpressionToQueryConvertor.GenerateQuery[T](ExpressionSyntax expression, IGridifyMapper`1 mapper, Boolean isParenthesisOpen)
at Gridify.GridifyExtensions.ApplyFiltering[T](IQueryable`1 query, String filter, IGridifyMapper`1 mapper)

Steps to reproduce

have a conditional operator ?: and .Select() in a GridifyMapper

Extend Filter function with cross-record boolean logic

Is your feature request related to a problem? Please describe.
Say I have this filter logic,

Filter = "(Rank=1,Val1<5),(Rank=2,Val1>5)"

Assuming no records have the same Rank, this fails as there cannot be any records that are ranked both 1 and 2. However, sometimes we need cross-record logic such as return 1st ranked item but only if 2nd ranked item meets certain criteria.

In the following code, Example1 and Example2 filters both match a record, but Example3 fails as both cannot be satisfied at same time

using Gridify;

namespace ConsoleApp2
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            Level1[] array = new Level1[3];
            array[0] = new Level1() { Rank = 1, Val1 = 4.3 };
            array[1] = new Level1() { Rank = 2, Val1 = 5.2 };
            array[2] = new Level1() { Rank = 3, Val1 = 7.5 };

            var gridifyMapper = new GridifyMapper<Level1>().GenerateMappings()
                .AddMap("Rank", q => q.Rank)
                .AddMap("Val1", q => q.Val1);

            // Example1: Rank = 1 AND Val1 < 5  - Returns 1 item
            var query1 = new GridifyQuery() { Filter = "(Rank=1,Val1<5)" };
            var expression1 = query1.GetFilteringExpression(gridifyMapper).Compile();
            var filter1 = array.Where(expression1);
            int count1 = filter1.Count();

            // Example2: Rank = 2 AND Val1 > 5  - Returns 1 item
            var query2 = new GridifyQuery() { Filter = "(Rank=2,Val1>5)" };
            var expression2 = query2.GetFilteringExpression(gridifyMapper).Compile();
            var filter2 = array.Where(expression2);
            int count2 = filter2.Count();

            // Example3: (Rank = 1 AND Val1 < 5) AND (Rank = 2 AND Val1 > 5) - Returns 0 records even though both conditions are true
            var query3 = new GridifyQuery() { Filter = "(Rank=1,Val1<5),(Rank=2,Val1>5)" };
            var expression3 = query3.GetFilteringExpression(gridifyMapper).Compile();
            var filter3 = array.Where(expression3);
            int count3 = filter3.Count();
        }
    }

    public class Level1
    {
        public int Rank { get; set; }
        public double Val1 { get; set; }
    }
}

Describe the solution you'd like
Is it possible to extend functionality to include BooleanLogicExpression?

Eg something like this where the query returns if all of the filter expressions are true/false?

var query3 = new GridifyQuery() { Filter = "((Rank=1,Val1<5),(Rank=2,Val1>5))" };
var expression3 = query3.GetBooleanLogicExpression(gridifyMapper).Compile();
bool result = array.Where(expression3);

It similar to chaining as here but trying to apply additional logic outside of a record.

GridifyMapper with additional index parameter

Say I wanted to filter and check 3rd Item in DataList has Property2 > 10?

The following works okay

using Gridify;
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            new Example().Run();
        }
    }

    class Example
    {
        public void Run()
        {
            Obj1 obj1 = new Obj1();
            obj1.Id = 1;
            for(int i = 0; i < 5; i++)
            {
                obj1.DataList.Add(new Obj2() { Property1 = i + 1, Property2 = i + 10 });
            }

            Obj1[] array = new Obj1[1];
            array[0] = obj1;
            var ds = array.AsQueryable();

            // 3rd Item in list has property2 > 10?
            // 3rd Item = 2nd index DataList[2]
            var gq2 = new GridifyQuery { Filter = "DataList2_Property2>10.0" }; // Cannot escape "DataList[2]_Property2>10.0" ??
            var gm2 = new GridifyMapper<Obj1>()
            .GenerateMappings()
            .AddMap("DataList2_Property2", x => x.DataList[2].Property2);

            // Works okay!
            var actual2 = ds.AsQueryable()
               .ApplyFiltering(gq2, gm2)
               .ToList();

        }
    }

    class Obj1
    {
        public int Id { get; set; }
        public List<Obj2> DataList { get; set; } = new List<Obj2>();
    }
    class Obj2
    {
        public double Property1 { get; set; }
        public double Property2 { get; set; }
    }
}

However what if I wanted to filter and check nth Item in DataList has Property2 > 10? Is there a way we can pass an additional parameter in the mapping so that there is no need to create hundreds of mappings to cover each index?

Eg something like n=2

// nth Item in list has property2 > 10 for n=2?
var gq3 = new GridifyQuery { Filter = "DataList[n=2]_Property2>10.0"  };
var gm3 = new GridifyMapper<Obj1>()
.GenerateMappings()
.AddMap("DataList[n=2]_Property2", x => x.DataList[n].Property2);

Ordering with nullable types

Details

i can't find any feature to support order fields with send null values to end of list.
in ef for example we can simple write query.OrderByDescending(p => p.OrderDate.HasValue).ThenBy(p => p.OrderDate)
in this case OrderDate is nullable type so null values go to end of list

please add this feature to Gridify

Comparison operations and string types

Suppose our AddDate field is defined as an string and its column has values such as 1400/07/06, etc. If I apply the > opertaor to it, the result will be:

System.InvalidOperationException: 
The binary operator GreaterThan is not defined for the types 'System.String' and 'System.String'.

EF-Core and SQL are able to compare strings using the string.Compare.
I think this case should use the string.Compare method, instead of >, <, >= and <= for strings.

Adding a filter on a row which contains null data causes a null reference exception

Version

2.8.4

Details

When adding a filter on a string which contains null data, GridifyQueryable throws a null reference exception. Here is an example of the crash:

public PersonQueryResult<PersonUIModel> RunGetPerson(string Id, PagingRequest pagingRequest)
    {
        IEnumerable<PersonUIModel> persons= _context.Person
           .Where(ct => ct.Id== Id)
           .AsEnumerable()
           .Select(ct => new PersonUIModel
           {
               Id = ct.Id,
               FirstName = ct.FirstName,
               LastName = ct.LastName,
           });

        QueryablePaging<PersonUIModel> results = persons.AsQueryable().ApplyPageFiltering(pagingRequest, false);

        return new PersonQueryResult<PersonUIModel>()
        {
            TotalCount = results.Count,
            Transactions = results.Query.ToList()
        };
    }
    public static QueryablePaging<T> ApplyPageFiltering<T>(this IQueryable<T> query, PagingRequest pagingRequest, bool ignorePaging)
    {
        GridifyMapper<T> gridifyMapper = new GridifyMapper<T>(config => config.IgnoreNotMappedFields = true).GenerateMappings() as GridifyMapper<T>;

        return query.GridifyQueryable(PagingRequestConverter.ToQueryFilter(pagingRequest, ignorePaging), gridifyMapper);
    }

image

Workaround: Add null coalescing operator in query to default to string.Empty

FirstName = ct.FirstName ?? string.Empty,

Steps to reproduce

  • Add row in database which contains null string value
  • Add paging request filter to filter on said value (Ex: GridifyQuery.Filter = "FirstName=*ABC)
  • Result: Object reference not set to instance of object
  • Expected: Value not returned as per filter

Exception thrown when filtering collection that contains null values

Version

2.8.3

Details

Trying to filter on a collection of data where the property that I'm filtering on might contain null values.
A NullReferenceException is thrown if any of the values that I'm filtering on are null.

Steps to reproduce

Minimal reproducible example:

public class MyThing
{
	public string Name {get;set;}
	public string Mail {get;set;}
}

var myThings = new List<MyThing>()
{
        new MyThing() { Name = "Alpha", Mail = "[email protected]"},
        new MyThing() { Name = "Bravo", Mail = "[email protected]"},
        new MyThing() { Name = "Charlie", Mail = null},
}.AsQueryable();

var queryBuilder = new QueryBuilder<MyThing>();
queryBuilder.AddCondition("Mail$b.com/i");
var results = queryBuilder.Build(myThings).ToList(); // throws Null Reference Exception on materialization

Filters that throw an exception:

Filters that do not throw an exception:

multiple null or empty filter not working

Version

2.8.0

Details

I have a table for which i wanted to do filtering for multiple fields, filtering for null and string.empty values. I tried it but it failed with an error "Gridify.GridifyFilteringException: Unexpected token <ValueToken>, expected <End>".
Filtering for null and string.empty values works for a unique field that is in the end of the query.

Steps to reproduce

Filtering with :

((name=),(age=))

or with :

((name=),(age=10))

or with:

( (name=Ali), ( (age=), (id=2) ) )

does'nt work

Already escaped backslash is used to escape more characters

Version

2.8.3

Details

When a Gridify filter value ends with an escaped escape character (\\), the already escaped backslash is used to escape another character in certain circumstances.

An example is provided in this gist.

The exception thrown:

Unhandled exception. Gridify.GridifyFilteringException: Unexpected token <End>, expected <CloseParenthesis>
   at Gridify.GridifyExtensions.ApplyFiltering[T](IQueryable`1 query, String filter, IGridifyMapper`1 mapper)
   at Gridify.GridifyExtensions.ApplyFiltering[T](IQueryable`1 query, IGridifyFiltering gridifyFiltering, IGridifyMapper`1 mapper)
   at Program.<Main>$(String[] args) in C:\git\GridifyIssue\Program.cs:line 17

If I replace the filter value with (name=*\\ S), it works correctly, escaping the backslash and filtering the queryable. So I would expect (name=*\\) to work as well without considering the end bracket a value character.

Steps to reproduce

  • Create a GridifyQuery where Filter contains an escaped backslash \\ before a value exit character, e.g., );
  • Apply the filter to a set;
  • Observe the exception.

How to handle spaces in search string?

Hello

My search string is for example: Signup scheduled

This will get encoded in url as: prospect?filter=status=signup%20scheduled

This always returns no results.

If I was to use filter=status=*signup then this will return appropriate records.

How to filter when the search term has a space character within it?

Thanks

How to get paging meta data such as TotalPages, CurrentPage, TotalItems?

Hello.

It is not uncommon with paging implementations to have meta data such as TotalPages, CurrentPage, TotalItems available as part of the JSON response from an API.

Using .ApplyFilteringOrderingPaging I cannot see any of this meta data.

Is it possible to get this information with this package?

Thanks

Filtering and Ordering validation attribute

I needed to add documentations on Filter and OrderBy fields. I've created my own class Query that implements IGridifyQuery interface.

public class Query : IGridifyQuery {

    public Query() { }

    public Query(int page, int pageSize, string filter, string? orderBy = null) {
        Page = page;
        PageSize = pageSize;
        Filter = filter;
        OrderBy = orderBy;
    }

    public int Page { get; set; }

    public int PageSize { get; set; }

    /// <summary>
    /// Filtering syntax<br />
    /// <a href="https://alirezanet.github.io/Gridify/guide/filtering.html">https://alirezanet.github.io/Gridify/guide/filtering.html</a>
    /// </summary>
    public string? Filter { get; set; }

    /// <summary>
    /// Ordering syntax<br />
    /// <a href="https://alirezanet.github.io/Gridify/guide/ordering.html">https://alirezanet.github.io/Gridify/guide/ordering.html</a>
    /// </summary>
    public string? OrderBy { get; set; }

}

This woks great but my problem is that I need to catch GridifyMapperException in my controller enpoint if the Filter or OrderBy value cannot be mapped. I don't want to set IgnoreNotMappedFields to true because I want to return a BadRequest reponse. At the moment if I don't catch the GridifyMapperException my application will returns a 500 error. Maybe there's a way to create an ValidationAttribute for Filtering and Ordering that would validate the value on a type.

[GridifyFiltering(typeof(User))]
public string? Filter { get; set; }

error on Filtering on Nested Collections

hi tnx for add nested
i use :
.AddMap("shomare",x=>x.RequestManager.select(y=>y.shomare))

.net 4.6
i get error :
Cannot compare elements of type 'System.Collections.Generic.ICollection`1[[WebApp.Models.RequestManager, WebApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]'. Only primitive types, enumeration types and entity types are supported

Nested Collections filtering with NHibernate and .NET 5.0

Version

2.7.0

Details

Hi,

I'm having problems Nested Collections filtering

The Data Schema is as follows (User - UserProfiles - Profile). A user can contain a list of UserProfile (Associative class between users and profile).
Gridify Mapping
.AddMap("ProfileName", u => u.Profiles.Select(p => p.Profile.Name));

When I try to filter a user by some profile name, the following script is injected into the query using NHibernate and .NET 5.0
and ( . is not null)

Debugging gridify, I found the GetExpressionWithNullCheck method that adds this check to the expression.
Result of GetExpressionWithNullCheck
{u => ((u.UserProfiles != null) AndAlso u.UserProfiles.Any(p => (p.Profile.Name== value(GridifyDisplayClass).Value )))}

The (u.UserProfiles != null) condition, in NHibernate doesn't work very well. Breaking the query with and ( . is not null)

I found that a similar change has already been made in the .NET Framework (issue #58) and I would like to know if it is possible to adjust it in .NET5.0 using NHibernate.

If it returns Expression.Lambda(right, param); in the GetExpressionWithNullCheck method, Filtering works.

Steps to reproduce

Gridify Mapping
.AddMap("ProfileName", u => u.Profiles.Select(p => p.Profile.Name));

Compiled Expressions

I've been using your library against standard collections such as arrays, lists and dictionaries and not databases. It would be really nice to have the ability to compile the filter if possible as it can often improve performance. Compile Method

For example:

var query = collection.AsQueryable().ApplyFiltering(Filter: "name = John", Compiled: true);

and

var query = new GridifyQuery { Filter = "name = John", Compiled = true };

Edit: Thinking more about this, compiled filters are about both performance and re-use of the filter. It would probably work more like this

var compiledFilter = CreateFilter(Filter: "name = John", Compiled: true);

and then reuse it as
var query = collection.AsQueryable().ApplyFiltering(compiledFilter);
var query = new GridifyQuery { Filter = compiledFilter };

QueryBuilder ignores custom maps for ordering

Version

2.8.2

Details

QueryBuilder doesn't use its own mapper in internal ApplyOrdering calls

Steps to reproduce

  1. Create a new builder with custom mapper (empty e.g.)
  2. Use AddOrderBy with a property name from type of the data but which is not mapped in the custom mapper
  3. Call any build method
  • Expected: Mapper exception
  • Result: There is no exception and ordering is applied

linq to entites suport problem

hi alireza
tnx for library
when i use library like ur example i get this error : Unable to cast the type 'System.String' to type 'System.Object'. LINQ to Entities only supports casting Entity Data Model primitive types

my project on framework 4.6.1 and ef6 edxm file
tnx

Available fields list for filter

Details

Hi,

Can we add the ability to show available fields for filtering because the frontend developer will not know which fields are available for filtering when we intigrating .net API

Duplicate field queries do not parse correctly

Version

2.8.1

Details

I want to filter on a date field, like:

AppAllTasks.AsQueryable().ApplyFiltering($"CreatedAt >= 2022-6-30 , CreatedAt <= 2022-6-31");

The resulting query string is:

System.Collections.Generic.List`1[TaskDetailMain].Where(__TaskDetailMain => ((__TaskDetailMain.CreatedAt >= 2022/6/30 0:00:00) AndAlso False))

The second filter is identified as bool,Can't repeat a field for filtering?

The second condition is not a property field of the previous condition and there is no problem.

Steps to reproduce

AppAllTasks.AsQueryable().ApplyFiltering($"CreatedAt >= 2022-6-30 , CreatedAt <= 2022-6-31");

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.