Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

QueryBuilder #14

Open
agriffard opened this issue Jan 26, 2024 · 6 comments
Open

QueryBuilder #14

agriffard opened this issue Jan 26, 2024 · 6 comments

Comments

@agriffard
Copy link

What is exactly your purpose with the MQueryBuilder?

The sample does not go beyond declaring the fields you want to be able to filter on.

How do you declare an instance of the field in the page and then, how are you able in the code to retrieve the actual selected filters with their field, operator and value?

Thank you for your answer.

@manureini
Copy link
Owner

manureini commented Jan 30, 2024

Hello ;)

Thanks for you question! I'm really happy when someone is interested in this library and may using it.

I'm using the Query Builder in a wasm application, but it will also work on server side.
In my case, there a people which can be filtered by a custom query.
I've defined 3 different values, which can be combined with operators. I'm calling them Rule, but it's (in a simple use case) just the part of a rule without an operator.

RuleName="FirstName" defines the FirstName Rule with PropertyType="typeof(string)".

MQueryBuilderComplexField is an example for a dropdown of some values (in this case objects of class Event).

<MQueryBuilder T="object" @bind-RuleGroup="mModel.RuleGroup" @bind-RuleGroup:after="OnRulesChanged">

      <MQueryBuilderField RuleName="FirstName" Title="@L[nameof(Loc.FirstName)]" PropertyType="typeof(string)" />
      <MQueryBuilderField RuleName="LastName" Title="@L[nameof(Loc.LastName)]" PropertyType="typeof(string)" />

      <MQueryBuilderComplexField T="Guid" RuleName="EventEmployed" Title="@L[Loc.RuleEventEmployed]" AllowedOperators="new MQueryBuilderConditionOperator[0]">
          <FormTemplate>
              <MSelect T="Event" Property="@nameof(Event.Name)" Options="mEvents" Value="mEvents.FirstOrDefault(e => e.Id == context.Value)" ValueChanged="async v => { await context.ValueChanged.InvokeAsync(v.Id); }" ValueExpression="@(RenderHelper.GetFakePropertyInfoExpression<Event>(string.Empty, "Id"))" />
          </FormTemplate>
      </MQueryBuilderComplexField>

  </MQueryBuilder>

In OnRulesChanged I serialize the MQueryBuilderRuleGroup mModel.RuleGroup property and send it to a controller.
If you are on ServerSide you can skip this step.

In the controller I do something like this:

 var people = mEfContext.Set<Person>().Where(p => p.OwnerId != null);

  var query = MQueryBuilderHelper.ApplyRules(people, pRuleGroup, (c) =>
  {
      return c switch
      {
          "FirstName" => (u, v) => u.FirstName.ToLower(),
          "LastName" => (u, v) => u.LastName.ToLower(),
          "EventEmployed" => (p, v) => efContext.Set<Employment>().Any(a => a.PersonId == p.Id && (object)a.EventId == v)
      };
  });

var result = query.ToArray();

So we prepare a IQueryable (in this case my database table), the user configured MQueryBuilderRuleGroup and a callback, which will map RuleName to an part of a query.
With this information the MQueryBuilderHelper will add the corresponding filter to the queryable.

There is a small trick implemented:
"FirstName" => (u, v) => u.FirstName.ToLower(),
Here the parameter v is not used. So MQueryBuilderHelper assumes, that there is an operator missing.
This is ok, because the user can select it and it will be applied by the helper.

"EventEmployed" => (p, v) => efContext.Set<Employment>().Any(a => a.PersonId == p.Id && (object)a.EventId == v)
In this case the parameter v is used. So no operator can be used in this case.
Thats why they are disabled in the ui AllowedOperators="new MQueryBuilderConditionOperator[0]"
For example in this case people needs an employment of a selected event to be in the result.

Fell free to ask any questions. I'd be happy to help you ;)

@manureini
Copy link
Owner

image

also see:
e82547f

@agriffard
Copy link
Author

Thank you for the sample you added.

I recently used this Grid component https://demos.blazorbootstrap.com/grid#server-side-filtering-paging-sorting and I find interesting the way the filters are exposed and how you can use them to build your query.

https://github.com/vikramlearning/blazorbootstrap/blob/ba7a76afce7e190f8b6c85c53e1e97fe6f42e782/BlazorBootstrap.Demo.RCL/Services/CustomerService.cs#L23

https://github.com/vikramlearning/blazorbootstrap/blob/ba7a76afce7e190f8b6c85c53e1e97fe6f42e782/blazorbootstrap/Models/GridDataProviderRequest.cs#L87

In this case, the filters are under each table column header but I would love to be able to build my filters outside the grid with a component like the one you are providing.

I use EF Core and https://github.com/alirezanet/Gridify to dynamicly query SQL.
Thanks to it, I can either use LINQ expressions or an interpreted string to filter an IQueryable.

@manureini
Copy link
Owner

Oh yes I also had to solve this issue. I want to use server side filtering for my projects, too.

I've implemented an Odata adapter and my Simple.Odata.Client fork
https://github.com/manureini/MComponents.Simple.Odata.Client/blob/master/MComponents.Simple.Odata.Client/ListViews/ListView.cs
(inherit from this class and use the DataAdapter for MGrids)
on the client side and I'm using AspNetCoreOData.
But yes it's somehow working, and right now I'm ok with this solution, but honestly not 100% happy ;).
I want to have a better working local data cache, but my version is not very advanced.

Oh nice I did now know that something like Gridify exists. I'll keep an eye on it ;)

I'd be happy if you can give me some feedback ;)

@agriffard
Copy link
Author

Do you plan to make a new release of the library with this component included?

@manureini
Copy link
Owner

Do you plan to make a new release of the library with this component included?

I've released a new version right now ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants