RSCG–Facet Search

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


Posted

in

, ,

by

Tags: