Skip to content

Commit

Permalink
feat: extension for Gridify to be able to use it with Elasticsearch (#…
Browse files Browse the repository at this point in the history
…126)

* Extend Gridify to work with Elasticsearch. Add filtering and sorting functionality. Add tests for different types of data and operators with different combinations of complexity.

* Update README.md with info about Elasticsearch compatibility

* Add ability to apply CustomElasticsearchNamingAction. Add more tests.

* Refactor filtering and ordering functionality. Create base builders and use them for LINQ and Elasticsearch query builders.

* Add more Gridify extension for Gridify.Elasticsearch in order to have the same API as original Gridify

* feat: check for unsupported features

* docs: extend the documentation with information about Gridify.Elasticsearch

* fix: remove extra spaces from expected results in tests

* docs: move the documentation about Gridify.Elasticsearch to a separate thread

* fix: avoid using string extensions for filtering purposes, use interfaces instead

* style: remove husky exclude filter for the tests

* style: reformat elasticsearch tests

* refactor: improve custom operator error message

---------

Co-authored-by: AliReza <[email protected]>
  • Loading branch information
ne4ta and alirezanet authored Oct 27, 2023
1 parent fddd6a6 commit 9cdbf55
Show file tree
Hide file tree
Showing 51 changed files with 3,301 additions and 715 deletions.
4 changes: 2 additions & 2 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
]
},
"dotnet-format": {
"version": "5.1.250801",
"version": "8.0.452407",
"commands": [
"dotnet-format"
]
}
}
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ node_modules
.temp
.cache
docs/.vuepress/dist
/*.user
4 changes: 2 additions & 2 deletions .husky/task-runner.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"command": "dotnet",
"group": "pre-commit",
"args": [
"dotnet-format",
"--include",
"format",
"--include=",
"${staged}"
],
"include": [
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Gridify (A Modern Dynamic LINQ library)

![GitHub](https://img.shields.io/github/license/alirezanet/gridify)
![Nuget](https://img.shields.io/nuget/dt/gridify?color=%239100ff)
![GitHub](https://img.shields.io/github/license/alirezanet/gridify)
![Nuget](https://img.shields.io/nuget/dt/gridify?color=%239100ff)
![Nuget](https://img.shields.io/nuget/v/gridify?label=stable)
![Nuget (with prereleases)](https://img.shields.io/nuget/vpre/gridify?label=latest)
[![NuGet version (Gridify)](https://img.shields.io/nuget/v/Gridify.svg?style=flat-square)](https://www.nuget.org/packages/Gridify/)
Expand Down Expand Up @@ -29,6 +29,7 @@ Gridify is a dynamic LINQ library that simplifies the process of converting stri
- Compatible with ORMs, especially Entity Framework
- Can be used on every collection that LINQ supports
- Compatible with object-mappers like AutoMapper
- Compatible with Elasticsearch

## Documentation

Expand Down
4 changes: 4 additions & 0 deletions docs/.vuepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { defineUserConfig } from 'vuepress-vite'
import type { DefaultThemeOptions } from 'vuepress-vite'
import type { ViteBundlerOptions } from '@vuepress/bundler-vite'
import { plugin, themeConfig, head } from './configs'
import markdownItInclude from 'markdown-it-include'

export default defineUserConfig<DefaultThemeOptions, ViteBundlerOptions>({
lang: 'en-US',
Expand All @@ -13,4 +14,7 @@ export default defineUserConfig<DefaultThemeOptions, ViteBundlerOptions>({
port: 3000,
base: '/Gridify/',
head: head,
extendsMarkdown: (md) => {
md.use(markdownItInclude);
}
})
13 changes: 12 additions & 1 deletion docs/.vuepress/configs/navbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@ export const navbar: NavbarConfig = [
},
{
text: 'Guide',
link: '/guide/',
children: [
{
text: 'LINQ / Entity Framework',
link: '/guide/',
activeMatch: '^((?!\/guide\/elasticsearch).)*$',
},
{
text: 'Elasticsearch',
link: '/guide/elasticsearch/',
activeMatch: '^\/guide\/elasticsearch\/*.*$',
}
],
},
{
text: 'Examples',
Expand Down
32 changes: 32 additions & 0 deletions docs/.vuepress/configs/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,38 @@ export const sidebar: SidebarConfig = {
]
}
],
'/guide/elasticsearch/': [
{
text: 'Introduction',
children: [
'/guide/elasticsearch/README.md',
'/guide/elasticsearch/getting-started.md',
'/guide/elasticsearch/extensions.md',
],
},
{
text: 'Configuration',
children: [
'/guide/elasticsearch/gridifyQuery.md',
'/guide/elasticsearch/gridifyMapper.md',
'/guide/elasticsearch/gridifyGlobalConfiguration.md',
]
},
{
text: 'Syntax',
children: [
'/guide/elasticsearch/filtering.md',
'/guide/elasticsearch/ordering.md',
]
},
{
text: 'Advanced',
children: [
'/guide/elasticsearch/dependency-injection.md',
'/guide/elasticsearch/elasticsearch.md',
]
}
],
'/contribution/': [
{
text: 'Contribution',
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ features:
- title: Exceptional Performance
details: Gridify has been designed with performance in mind and can outperform other dynamic LINQ libraries.
- title: Compatibility
details: Gridify can be used in any .NET Core or .NET Framework project, making it highly compatible. It can also be used alongside Entity Framework and other ORMs.
details: Gridify can be used in any .NET Core or .NET Framework project, making it highly compatible. It can also be used alongside Entity Framework and other ORMs. Gridify.Elasticsearch extension provides and ability to build Elasticsearch DSL queries from Gridify filters.
footer: MIT Licensed | © 2021-present AliReZa Sabouri
---
1 change: 1 addition & 0 deletions docs/contribution/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,5 @@ check out the [github contributing guide](https://git-scm.com/book/en/v2/GitHub-
- [Alireza Arabshahi](https://github.com/AlirezaArabshahi)
- [sunyuliang](https://github.com/sunyuliang)
- [TSrgy](https://github.com/TSrgy)
- [Dzmitry Koush](https://github.com/ne4ta)
- Add your name
23 changes: 13 additions & 10 deletions docs/guide/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# 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.
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.

Gridify.Elasticsearch is an extension of Gridify, that provides an ability to generate Elasticsearch DSL queries. Read more about Gridify.Elasticsearch in [a separate thread of the documentation](./elasticsearch/).

## Features

Expand All @@ -16,21 +20,20 @@ Gridify is a dynamic LINQ library that simplifies the process of converting stri
- Can be used on every collection that LINQ supports
- Compatible with object-mappers like AutoMapper


## Examples

To better illustrate how Gridify works, we've prepared a few examples:

- [Using Gridify in API Controllers](../example/api-controller.md)
- Coming soon ...


## Performance

Filtering can be the most expensive feature in Gridify. The following benchmark compares filtering in the most well-known dynamic LINQ libraries. As you can see, Gridify has the closest result to native LINQ:
Filtering can be the most expensive feature in Gridify. The following benchmark compares filtering in the most
well-known dynamic LINQ libraries. As you can see, Gridify has the closest result to native LINQ:

| Method | Mean | Error | StdDev | RatioSD | Allocated | Alloc Ratio |
|----------------- |-------------:|------------:|------------:|--------:|------------:|------------:|
| Method | Mean | Error | StdDev | RatioSD | Allocated | Alloc Ratio |
|------------------|-------------:|------------:|------------:|--------:|------------:|------------:|
| Native LINQ | 651.2 us | 6.89 us | 6.45 us | 0.00 | 32.74 KB | 1.00 |
| Gridify | 689.1 us | 10.70 us | 11.45 us | 0.02 | 36.85 KB | 1.13 |
| DynamicLinq | 829.3 us | 10.98 us | 9.17 us | 0.01 | 119.29 KB | 3.64 |
Expand All @@ -42,13 +45,13 @@ Filtering can be the most expensive feature in Gridify. The following benchmark
BenchmarkDotNet v0.13.8, Windows 11 (10.0.22621.2283/22H2/2022Update/SunValley2)
12th Gen Intel Core i7-12800H, 1 CPU, 20 logical and 14 physical cores
.NET SDK 8.0.100-preview.1.23115.2
[Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2
[Host] : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2
DefaultJob : .NET 7.0.11 (7.0.1123.42427), X64 RyuJIT AVX2

This Benchmark is available [Here](https://github.com/alirezanet/Gridify/blob/master/benchmark/LibraryComparisionFilteringBenchmark.cs)
This Benchmark is
available [Here](https://github.com/alirezanet/Gridify/blob/master/benchmark/LibraryComparisionFilteringBenchmark.cs)
:::


<style scoped>
tr:nth-child(2) {
color: #42b983;
Expand Down
21 changes: 11 additions & 10 deletions docs/guide/autoMapper.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# AutoMapper

Gridify is complitely compatible with AutoMapper. also, these two packages can help each other nicely. we can use Gridify for filtering, sorting, and paging and AutoMapper for the projection.
Gridify is completely compatible with AutoMapper. Also, these two packages can help each other nicely. We can use Gridify for filtering, sorting, and paging and AutoMapper for the projection.

``` csharp
// AutoMapper ProjectTo + Filtering Only, example
Expand All @@ -11,29 +11,30 @@ var result = query.ProjectTo<PersonDTO>().ToList();
``` csharp
// AutoMapper ProjectTo + Filtering + Ordering + Paging, example
QueryablePaging<Person> qp = personRepo.GridifyQueryable(gridifyQuery);
var result = new Paging<Person> (qp.Count, qp.Query.ProjectTo<PersonDTO>().ToList ());
var result = new Paging<Person>(qp.Count, qp.Query.ProjectTo<PersonDTO>().ToList());
```

## GridifyTo!
Filtering, Ordering, Paging, and Projection are all done with GridifyTo.

Gridify library does not have a built-in GridifyTo extension method because we don't want to have AutoMapper dependency. but if you are using AutoMapper in your project, I recommend you to add the bellow extension method to your project.
Filtering, Ordering, Paging, and Projection are all done with `GridifyTo`.

Gridify library does not have a built-in `GridifyTo` extension method because we don't want to have AutoMapper dependency. but if you are using AutoMapper in your project, I recommend you to add the bellow extension method to your project.

``` csharp
public static Paging<TDestination> GridifyTo<TSource, TDestination>(this IQueryable<TSource> query,
IMapper autoMapper, IGridifyQuery gridifyQuery, IGridifyMapper<TSource> mapper = null)
public static Paging<TDestination> GridifyTo<TSource, TDestination>(
this IQueryable<TSource> query, IMapper autoMapper, IGridifyQuery gridifyQuery, IGridifyMapper<TSource> mapper = null)
{
var res = query.GridifyQueryable(gridifyQuery, mapper);
return new Paging<TDestination> (res.Count , res.Query.ProjectTo<TDestination>(autoMapper.ConfigurationProvider).ToList());
return new Paging<TDestination>(res.Count, res.Query.ProjectTo<TDestination>(autoMapper.ConfigurationProvider).ToList());
}
```

``` csharp
// only if you have Gridify.EntityFramework package installed.
public static async Task<Paging<TDestination>> GridifyToAsync<TSource, TDestination>(this IQueryable<TSource> query,
IMapper autoMapper, IGridifyQuery gridifyQuery, IGridifyMapper<TSource> mapper = null)
public static async Task<Paging<TDestination>> GridifyToAsync<TSource, TDestination>(
this IQueryable<TSource> query, IMapper autoMapper, IGridifyQuery gridifyQuery, IGridifyMapper<TSource> mapper = null)
{
var res = await query.GridifyQueryableAsync(gridifyQuery, mapper);
return new Paging<TDestination> (res.Count , await res.Query.ProjectTo<TDestination>(autoMapper.ConfigurationProvider).ToListAsync());
return new Paging<TDestination>(res.Count, await res.Query.ProjectTo<TDestination>(autoMapper.ConfigurationProvider).ToListAsync());
}
```
23 changes: 13 additions & 10 deletions docs/guide/compile.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
# Compile and Reuse

You can access Gridify generated expressions using the `GetFilteringExpression` of [GridifyQuery](./gridifyQuery.md) or `BuildCompiled` methods of [QueryBuilder](./queryBuilder.md) class,
You can access Gridify generated expressions using the `GetFilteringExpression` of [GridifyQuery](./gridifyQuery.md)
or `BuildCompiled` methods of [QueryBuilder](./queryBuilder.md) class,
by storing an expression you can use it multiple times without having any overheads,
also if you store a compiled expression you get a massive performance boost.

::: warning
you should only use a **compiled** expression (delegate) if you are **not** using Gridify alongside an ORM like Entity-Framework.
You should only use a **compiled** expression (delegate) if you are **not** using Gridify alongside an ORM like
Entity-Framework.
:::

``` csharp
// eg.1 - using GridifyQuery - Compield - where only
// eg.1 - using GridifyQuery - Compiled - where only
var gq = new GridifyQuery() { Filter = "name=John" };
var expression = gq.GetFilteringExpression<Person>();
var compiledExpression = expression.Compile();
var result = persons.Where(compiledExpression);
```

``` csharp
// eg.2 - using QueryBuilder - Compield - where only
// eg.2 - using QueryBuilder - Compiled - where only
var compiledExpression = new QueryBuilder<Person>()
.AddCondition("name=John")
.BuildFilteringExpression()
Expand All @@ -35,10 +37,11 @@ var result = func(persons);
```

## Performance
This is the performance improvement example when you use a compiled expression

| Method | Mean | Ratio | RatioSD | Gen 0 | Gen 1 | Allocated |
|---------------- |-------------:|------:|--------:|---------:|--------:|----------:|
| GridifyCompiled | 1.008 us | 0.001 | 0.00 | 0.1564 | - | 984 B |
| NativeLINQ | 724.329 us | 1.000 | 0.00 | 5.8594 | 2.9297 | 37,392 B |
| Gridify | 736.854 us | 1.018 | 0.01 | 5.8594 | 2.9297 | 39,924 B |
This is the performance improvement example when you use a compiled expression.

| Method | Mean | Ratio | RatioSD | Gen 0 | Gen 1 | Allocated |
|-----------------|-----------:|------:|--------:|-------:|-------:|----------:|
| GridifyCompiled | 1.008 us | 0.001 | 0.00 | 0.1564 | - | 984 B |
| NativeLINQ | 724.329 us | 1.000 | 0.00 | 5.8594 | 2.9297 | 37,392 B |
| Gridify | 736.854 us | 1.018 | 0.01 | 5.8594 | 2.9297 | 39,924 B |
17 changes: 17 additions & 0 deletions docs/guide/elasticsearch/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Introduction

Gridify.Elasticsearch is an extension of Gridify, that provides an ability to generate Elasticsearch DSL queries.

## Features

- Fast and easy to use
- Supports filtering, sorting, and pagination
- Supports nested queries
- Supports `string` to `object` mapping
- Compatible with
Elasticsearch ([Elastic.Clients.Elasticsearch 8.*](https://www.nuget.org/packages/Elastic.Clients.Elasticsearch) is a
dependency)

## Examples

[Here](./elasticsearch.md#examples-of-usage) you can find some examples how Gridify.Elasticsearch can be used.
1 change: 1 addition & 0 deletions docs/guide/elasticsearch/dependency-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!!!include(./docs/guide/dependency-injection.md)!!!
Loading

0 comments on commit 9cdbf55

Please sign in to comment.