Skip to content

Commit

Permalink
Adds AlwaysRun feature to filters (#385)
Browse files Browse the repository at this point in the history
  • Loading branch information
deanmarcussen authored May 13, 2021
1 parent a47ceb4 commit 6681e3d
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 38 deletions.
22 changes: 15 additions & 7 deletions samples/YesSql.Samples.Web/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,17 +44,25 @@ public async Task<IActionResult> Index([ModelBinder(BinderType = typeof(QueryFil
var search = new Filter
{
SearchText = currentSearchText,
OriginalSearchText = currentSearchText,
Filters = new List<SelectListItem>()
{
new SelectListItem("Select...", ""),
new SelectListItem("Published", ContentsStatus.Published.ToString()),
new SelectListItem("Draft", ContentsStatus.Draft.ToString())
}
OriginalSearchText = currentSearchText
};

filterResult.MapTo(search);


search.Statuses = new List<SelectListItem>()
{
new SelectListItem("Select...", "", search.SelectedStatus == BlogPostStatus.Default),
new SelectListItem("Published", BlogPostStatus.Published.ToString(), search.SelectedStatus == BlogPostStatus.Published),
new SelectListItem("Draft", BlogPostStatus.Draft.ToString(), search.SelectedStatus == BlogPostStatus.Draft)
};

search.Sorts = new List<SelectListItem>()
{
new SelectListItem("Newest", BlogPostSort.Newest.ToString(), search.SelectedSort == BlogPostSort.Newest),
new SelectListItem("Oldest", BlogPostSort.Oldest.ToString(), search.SelectedSort == BlogPostSort.Oldest)
};

var vm = new BlogPostViewModel
{
BlogPosts = posts,
Expand Down
58 changes: 51 additions & 7 deletions samples/YesSql.Samples.Web/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ public void ConfigureServices(IServiceCollection services)
.WithNamedTerm("status", builder => builder
.OneCondition((val, query) =>
{
if (Enum.TryParse<ContentsStatus>(val, true, out var e))
if (Enum.TryParse<BlogPostStatus>(val, true, out var e))
{
switch (e)
{
case ContentsStatus.Published:
case BlogPostStatus.Published:
query.With<BlogPostIndex>(x => x.Published);
break;
case ContentsStatus.Draft:
case BlogPostStatus.Draft:
query.With<BlogPostIndex>(x => !x.Published);
break;
default:
Expand All @@ -53,22 +53,66 @@ public void ConfigureServices(IServiceCollection services)
})
.MapTo<Filter>((val, model) =>
{
if (Enum.TryParse<ContentsStatus>(val, true, out var e))
if (Enum.TryParse<BlogPostStatus>(val, true, out var e))
{
model.SelectedFilter = e;
model.SelectedStatus = e;
}
})
.MapFrom<Filter>((model) =>
{
if (model.SelectedFilter != ContentsStatus.Default)
if (model.SelectedStatus != BlogPostStatus.Default)
{
return (true, model.SelectedFilter.ToString());
return (true, model.SelectedStatus.ToString());
}
return (false, String.Empty);
})
)
.WithNamedTerm("sort", b => b
.OneCondition((val, query) =>
{
if (Enum.TryParse<BlogPostSort>(val, true, out var e))
{
switch (e)
{
case BlogPostSort.Newest:
query.With<BlogPostIndex>().OrderByDescending(x => x.PublishedUtc);
break;
case BlogPostSort.Oldest:
query.With<BlogPostIndex>().OrderBy(x => x.PublishedUtc);
break;
default:
query.With<BlogPostIndex>().OrderByDescending(x => x.PublishedUtc);
break;
}
}
else
{
query.With<BlogPostIndex>().OrderByDescending(x => x.PublishedUtc);
}
return query;
})
.MapTo<Filter>((val, model) =>
{
if (Enum.TryParse<BlogPostSort>(val, true, out var e))
{
model.SelectedSort = e;
}
})
.MapFrom<Filter>((model) =>
{
if (model.SelectedSort != BlogPostSort.Newest)
{
return (true, model.SelectedSort.ToString());
}
return (false, String.Empty);
})
.AlwaysRun()
)
.WithDefaultTerm("title", b => b
.ManyCondition(
((val, query) => query.With<BlogPostIndex>(x => x.Title.Contains(val))),
Expand Down
18 changes: 14 additions & 4 deletions samples/YesSql.Samples.Web/ViewModels/BlogPostViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,29 @@ public class Filter
public string Author { get; set; }
public string SearchText { get; set; }
public string OriginalSearchText { get; set; }
public ContentsStatus SelectedFilter { get; set; }
public BlogPostStatus SelectedStatus { get; set; }
public BlogPostSort SelectedSort { get; set; }

[ModelBinder(BinderType = typeof(QueryFilterEngineModelBinder<BlogPost>), Name = "SearchText")]
[ModelBinder(BinderType = typeof(QueryFilterEngineModelBinder<BlogPost>), Name = nameof(SearchText))]
public QueryFilterResult<BlogPost> FilterResult { get; set; }

[BindNever]
public List<SelectListItem> Filters { get; set; } = new();
public List<SelectListItem> Statuses { get; set; } = new();

[BindNever]
public List<SelectListItem> Sorts { get; set; } = new();
}

public enum ContentsStatus
public enum BlogPostStatus
{
Default,
Draft,
Published
}

public enum BlogPostSort
{
Newest,
Oldest,
}
}
43 changes: 30 additions & 13 deletions samples/YesSql.Samples.Web/Views/Home/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,36 @@
</head>

<body class="m-5">

<form asp-action="IndexPost" method="post">
<input type="search" asp-for="Search.OriginalSearchText" type="hidden"></input>


<div class="form-group row mb-3">
<label asp-for="Search.SearchText" class="col-2 align-self-center">Search</label>
<div class="col-10">
<input class="form-control" type="search" asp-for="Search.SearchText"></input>
</div>
</div>

<div class="form-group row mb-3">
<label asp-for="Search.SelectedStatus" class="col-2 align-self-center">Status</label>
<div class="col-10">
<select class="form-control" asp-for="Search.SelectedStatus" asp-items="Model.Search.Statuses"></select>
</div>
</div>

<div class="form-group row mb-3">
<label asp-for="Search.SelectedSort" class="col-2 align-self-center">Sort</label>
<div class="col-10">
<select class="form-control" asp-for="Search.SelectedSort" asp-items="Model.Search.Sorts"></select>
</div>

</div>

<button class="btn btn-primary d-flex ms-auto mb-3" type="submit">Search</button>
</form>

@foreach (var blogPost in Model.BlogPosts)
{
<div class="card">
Expand All @@ -19,18 +49,5 @@
</div>
}

<form asp-action="IndexPost" method="post">
<input type="search" asp-for="Search.OriginalSearchText" type="hidden"></input>

<div class="w-100 form-group my-3">
<input class="form-control" type="search" asp-for="Search.SearchText"></input>
</div>


<div class="w-50 form-group my-3">
<select class="form-control" asp-for="Search.SelectedFilter" asp-items="Model.Search.Filters"></select>
</div>

<button class="btn btn-primary d-block my-3" type="submit">Search</button>
</form>
</body>
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ public UnaryEngineBuilder<T, TTermOption> AllowMultiple()

return this;
}

public UnaryEngineBuilder<T, TTermOption> AlwaysRun()
{
_termOption.AlwaysRun = true;

return this;
}

public override (Parser<OperatorNode> Parser, TTermOption TermOption) Build()
=> (_parser, _termOption);
Expand Down
2 changes: 1 addition & 1 deletion src/YesSql.Filters.Abstractions/Services/FilterResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace YesSql.Filters.Abstractions.Services
public abstract class FilterResult<T, TTermOption> : IEnumerable<TermNode> where TTermOption : TermOption
{

protected Dictionary<string, TermNode> _terms = new Dictionary<string, TermNode>();
protected Dictionary<string, TermNode> _terms = new Dictionary<string, TermNode>(StringComparer.OrdinalIgnoreCase);

public FilterResult(IReadOnlyDictionary<string, TTermOption> termOptions)
{
Expand Down
5 changes: 5 additions & 0 deletions src/YesSql.Filters.Abstractions/Services/TermOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public TermOption(string name)
/// </summary>
public bool Single { get; set; } = true;

/// <summary>
/// Whether this term filter should always run, even when not specified.
/// </summary>
public bool AlwaysRun { get; set; }

public Delegate MapTo { get; set; }
public Delegate MapFrom { get; set; }
public Func<string, string, TermNode> MapFromFactory { get; set; }
Expand Down
3 changes: 2 additions & 1 deletion src/YesSql.Filters.Query/QueryEngineBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using YesSql.Filters.Abstractions.Builders;
Expand All @@ -24,7 +25,7 @@ public IQueryParser<T> Build()
var builders = _termBuilders.Values.Select(x => x.Build());

var parsers = builders.Select(x => x.Parser).ToArray();
var termOptions = builders.Select(x => x.TermOption).ToDictionary(k => k.Name, v => v);
var termOptions = builders.Select(x => x.TermOption).ToDictionary(k => k.Name, v => v, StringComparer.OrdinalIgnoreCase);

return new QueryParser<T>(parsers, termOptions);
}
Expand Down
30 changes: 25 additions & 5 deletions src/YesSql.Filters.Query/QueryFilterResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,34 @@ public async ValueTask<IQuery<T>> ExecuteAsync(QueryExecutionContext<T> context)
foreach (var term in _terms.Values)
{
// TODO optimize value task.
context.CurrentTermOption = TermOptions[term.TermName];

var termQuery = visitor.Visit(term, context);
context.Item = await termQuery.Invoke(context.Item);
context.CurrentTermOption = null;
await VisitTerm(TermOptions, context, visitor, term);
}

// Execute always run terms. These are not added to the terms list.
foreach (var termOption in TermOptions)
{
if (!termOption.Value.AlwaysRun)
{
continue;
}

if (!_terms.ContainsKey(termOption.Key))
{
var alwaysRunNode = new NamedTermNode(termOption.Key, new UnaryNode(String.Empty));
await VisitTerm(TermOptions, context, visitor, alwaysRunNode);
}
}

return context.Item;
}

private async static Task VisitTerm(IReadOnlyDictionary<string, QueryTermOption<T>> termOptions, QueryExecutionContext<T> context, QueryFilterVisitor<T> visitor, TermNode term)
{
context.CurrentTermOption = termOptions[term.TermName];

var termQuery = visitor.Visit(term, context);
context.Item = await termQuery.Invoke(context.Item);
context.CurrentTermOption = null;
}
}
}

0 comments on commit 6681e3d

Please sign in to comment.