RSCG – Facet.Search
| name | Facet.Search |
| nuget | https://www.nuget.org/packages/Facet.Search/ |
| link | https://github.com/Tim-Maes/Facet.Search |
| author | Tim Maes |
Generating search from C# clasess and properties
Integrating search in .NET applications
This is how you can use Facet.Search .
The code that you start with is
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Facet.Search" Version="0.1.1" />
<PackageReference Include="Facet.Search.EFCore" Version="0.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.1" />
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
The code that you will use is
using Microsoft.EntityFrameworkCore;
using SearchDemo;
using SearchDemo.Search;
var filter = new PersonSearchFilter
{
DOBFrom = new DateTime(1970, 1, 1),
DOBTo = new DateTime(1980, 12, 31),
IsActive = true,
MinSalary=1,
MaxSalary= 10,
SearchText= "Andrei"
};
MyAppContext cnt = new ();
var p= cnt.Person.ApplyFacetedSearch(filter);
var sql = p.ToQueryString();
Console.WriteLine(sql);
using Facet.Search;
using Microsoft.EntityFrameworkCore;
namespace SearchDemo;
[FacetedSearch]
public class Person
{
public int Id { get; set; }
[FullTextSearch]
public string Name { get; set; }= string.Empty;
[SearchFacet(Type = FacetType.DateRange, DisplayName = "Date Of Birth")]
public DateTime DOB { get; set; }
[SearchFacet(Type = FacetType.Range, DisplayName = "SalaryRange")]
public int Salary { get; set; }
[SearchFacet(Type = FacetType.Boolean, DisplayName = "IsEmployee")]
public bool IsActive { get; set; }
}
public class MyAppContext : DbContext
{
public MyAppContext()
{
this.Person =this.Set<Person>();
//fake
Person.Add(new Person() {
DOB= new DateTime(1970,4,16),
Id=1,
IsActive=true,
Name="Andrei Ignat",
Salary= 3
});
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;");
base.OnConfiguring(optionsBuilder);
}
public DbSet<Person> Person { get; set; }
}
The code that is generated is
// <auto-generated />
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SearchDemo.Search;
/// <summary>
/// Aggregated facet results for Person.
/// </summary>
public class PersonFacetResults
{
public int? SalaryMin { get; set; }
public int? SalaryMax { get; set; }
public int IsActiveTrueCount { get; set; }
public int IsActiveFalseCount { get; set; }
}
public static class PersonFacetAggregationExtensions
{
/// <summary>
/// Gets facet aggregations for Person.
/// </summary>
public static PersonFacetResults GetFacetAggregations(
this System.Linq.IQueryable<SearchDemo.Person> query)
{
var results = new PersonFacetResults();
if (query.Any())
{
results.SalaryMin = query.Min(x => x.Salary);
results.SalaryMax = query.Max(x => x.Salary);
}
results.IsActiveTrueCount = query.Count(x => x.IsActive);
results.IsActiveFalseCount = query.Count(x => !x.IsActive);
return results;
}
}
// <auto-generated />
#nullable enable
using System;
using System.Linq;
namespace SearchDemo.Search;
/// <summary>
/// Extension methods for searching Person.
/// </summary>
public static class PersonSearchExtensions
{
/// <summary>
/// Applies faceted search filtering to a queryable of Person.
/// </summary>
/// <remarks>
/// Full-text search strategy: LinqContains
/// All filters are translated to SQL and executed on the database server.
/// </remarks>
public static System.Linq.IQueryable<SearchDemo.Person> ApplyFacetedSearch(
this System.Linq.IQueryable<SearchDemo.Person> query,
PersonSearchFilter filter)
{
if (filter == null)
return query;
if (filter.DOBFrom.HasValue)
query = query.Where(x => x.DOB >= filter.DOBFrom.Value);
if (filter.DOBTo.HasValue)
query = query.Where(x => x.DOB <= filter.DOBTo.Value);
if (filter.MinSalary.HasValue)
query = query.Where(x => x.Salary >= filter.MinSalary.Value);
if (filter.MaxSalary.HasValue)
query = query.Where(x => x.Salary <= filter.MaxSalary.Value);
if (filter.IsActive.HasValue)
query = query.Where(x => x.IsActive == filter.IsActive.Value);
// Full-text search
if (!string.IsNullOrWhiteSpace(filter.SearchText))
{
// Uses LINQ Contains() -> translates to SQL LIKE '%term%'
var searchTerm = filter.SearchText.ToLower();
query = query.Where(x => (x.Name != null && x.Name.ToLower().Contains(searchTerm)));
}
return query;
}
}
// <auto-generated />
#nullable enable
namespace SearchDemo.Search;
/// <summary>
/// Generated search filter for Person.
/// </summary>
public partial class PersonSearchFilter
{
/// <summary>
/// Filter by Date Of Birth.
/// </summary>
public System.DateTime? DOBFrom { get; set; }
/// <summary>
/// End date for DOB filter.
/// </summary>
public System.DateTime? DOBTo { get; set; }
/// <summary>
/// Filter by SalaryRange.
/// </summary>
public int? MinSalary { get; set; }
/// <summary>
/// Maximum Salary value.
/// </summary>
public int? MaxSalary { get; set; }
/// <summary>
/// Filter by IsEmployee.
/// </summary>
public bool? IsActive { get; set; }
/// <summary>
/// Full-text search query.
/// </summary>
public string? SearchText { get; set; }
}
// <auto-generated />
#nullable enable
using System.Collections.Generic;
namespace SearchDemo.Search;
/// <summary>
/// Facet metadata for Person.
/// </summary>
public class PersonFacetMetadata
{
public string Name { get; set; } = null!;
public string PropertyName { get; set; } = null!;
public string DisplayName { get; set; } = null!;
public string Type { get; set; } = null!;
public bool IsHierarchical { get; set; }
public string? DependsOn { get; set; }
public string OrderBy { get; set; } = null!;
public int Limit { get; set; }
public string? RangeIntervals { get; set; }
}
/// <summary>
/// Metadata about searchable facets for Person.
/// </summary>
public static class PersonSearchMetadata
{
public static IReadOnlyList<PersonFacetMetadata> Facets { get; } = new[]
{
new PersonFacetMetadata
{
Name = "DOB",
PropertyName = "DOB",
DisplayName = "Date Of Birth",
Type = "DateRange",
IsHierarchical = false,
OrderBy = "Count",
Limit = 0,
},
new PersonFacetMetadata
{
Name = "Salary",
PropertyName = "Salary",
DisplayName = "SalaryRange",
Type = "Range",
IsHierarchical = false,
OrderBy = "Count",
Limit = 0,
},
new PersonFacetMetadata
{
Name = "IsActive",
PropertyName = "IsActive",
DisplayName = "IsEmployee",
Type = "Boolean",
IsHierarchical = false,
OrderBy = "Count",
Limit = 0,
},
};
}
Code and pdf at
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Facet.Search