Facet.Search
0.1.1
See the version list below for details.
dotnet add package Facet.Search --version 0.1.1
NuGet\Install-Package Facet.Search -Version 0.1.1
<PackageReference Include="Facet.Search" Version="0.1.1" />
<PackageVersion Include="Facet.Search" Version="0.1.1" />
<PackageReference Include="Facet.Search" />
paket add Facet.Search --version 0.1.1
#r "nuget: Facet.Search, 0.1.1"
#:package Facet.Search@0.1.1
#addin nuget:?package=Facet.Search&version=0.1.1
#tool nuget:?package=Facet.Search&version=0.1.1
Facet.Search
Compile-time faceted search generation for .NET — Zero boilerplate, type-safe, and performant.
Facet.Search uses source generators to automatically create search filter classes, LINQ extension methods, facet aggregations, and metadata from your domain models — all at compile time with no runtime overhead.
Features
- Zero Boilerplate - Just add attributes to your models
- Type-Safe - All filters are compile-time checked
- Performant - Generated code is as efficient as hand-written
- SQL Translated - All filters execute on the database, not in memory
- EF Core Integration - Async extensions for Entity Framework Core
- Full-Text Search - Built-in text search with multiple strategies
- Facet Aggregations - Automatic counting and range detection
- Frontend Metadata - Generate facet metadata for UI consumption
Installation
dotnet add package Facet.Search
For Entity Framework Core integration:
dotnet add package Facet.Search.EFCore
Quick Start
1. Define Your Model
using Facet.Search;
[FacetedSearch]
public class Product
{
public int Id { get; set; }
[FullTextSearch]
public string Name { get; set; } = null!;
[FullTextSearch(Weight = 0.5f)]
public string? Description { get; set; }
[SearchFacet(Type = FacetType.Categorical, DisplayName = "Brand")]
public string Brand { get; set; } = null!;
[SearchFacet(Type = FacetType.Range, DisplayName = "Price")]
public decimal Price { get; set; }
[SearchFacet(Type = FacetType.Boolean, DisplayName = "In Stock")]
public bool InStock { get; set; }
[SearchFacet(Type = FacetType.DateRange, DisplayName = "Created Date")]
public DateTime CreatedAt { get; set; }
}
2. Use Generated Code
The source generator automatically creates:
ProductSearchFilter— Filter class with all facet propertiesProductSearchExtensions— LINQ extension methodsProductFacetAggregations— Aggregation resultsProductSearchMetadata— Facet metadata for frontends
using YourNamespace.Search;
// Create a filter
var filter = new ProductSearchFilter
{
Brand = ["Apple", "Samsung"],
MinPrice = 100m,
MaxPrice = 1000m,
InStock = true,
SearchText = "laptop"
};
// Apply to any IQueryable<Product>
var results = products.AsQueryable()
.ApplyFacetedSearch(filter)
.ToList();
// Get facet aggregations
var aggregations = products.AsQueryable().GetFacetAggregations();
// aggregations.Brand = { "Apple": 5, "Samsung": 3, ... }
// aggregations.PriceMin = 99.99m
// aggregations.PriceMax = 2499.99m
// Access metadata for UI
foreach (var facet in ProductSearchMetadata.Facets)
{
Console.WriteLine($"{facet.DisplayName} ({facet.Type})");
}
How It Works with EF Core
All generated filters are translated to SQL — no client-side evaluation for facet filters.
| Filter Type | Generated Code | SQL Translation |
|---|---|---|
| Categorical | .Where(x => filter.Brand.Contains(x.Brand)) |
WHERE Brand IN ('Apple', 'Samsung') |
| Range | .Where(x => x.Price >= min && x.Price <= max) |
WHERE Price >= @min AND Price <= @max |
| Boolean | .Where(x => x.InStock == true) |
WHERE InStock = 1 |
| DateRange | .Where(x => x.CreatedAt >= from) |
WHERE CreatedAt >= @from |
| Full-Text | .Where(x => x.Name.Contains(term)) |
WHERE Name LIKE '%term%' |
Full-Text Search Strategies
By default, full-text search uses LIKE '%term%' which works with all databases but doesn't use full-text indexes. You can configure different strategies:
[FacetedSearch(FullTextStrategy = FullTextSearchStrategy.LinqContains)] // Default: LIKE '%term%'
[FacetedSearch(FullTextStrategy = FullTextSearchStrategy.SqlServerFreeText)] // SQL Server FREETEXT
[FacetedSearch(FullTextStrategy = FullTextSearchStrategy.PostgreSqlFullText)] // PostgreSQL tsvector
[FacetedSearch(FullTextStrategy = FullTextSearchStrategy.ClientSide)] // In-memory (use with caution)
public class Product { }
| Strategy | Database | Requires Index | Performance |
|---|---|---|---|
LinqContains |
All | No | Slow on large data |
EfLike |
All | No | Same as LinqContains |
SqlServerFreeText |
SQL Server | FULLTEXT index | Fast |
SqlServerContains |
SQL Server | FULLTEXT index | Fast |
PostgreSqlFullText |
PostgreSQL | GIN index | Fast |
ClientSide |
N/A | No | Loads to memory |
EF Core Integration
Use the Facet.Search.EFCore package for async operations:
using Facet.Search.EFCore;
// Async search execution
var results = await dbContext.Products
.ApplyFacetedSearch(filter)
.ExecuteSearchAsync();
// Paginated results
var pagedResult = await dbContext.Products
.ApplyFacetedSearch(filter)
.ToPagedResultAsync(page: 1, pageSize: 20);
// pagedResult.Items, pagedResult.TotalCount, pagedResult.TotalPages
// Async facet aggregation
var brandCounts = await dbContext.Products
.AggregateFacetAsync(p => p.Brand, limit: 10);
// Get min/max range
var (minPrice, maxPrice) = await dbContext.Products
.GetRangeAsync(p => p.Price);
Facet Types
| Type | Description | Generated Filter Properties |
|---|---|---|
Categorical |
Discrete values (Brand, Category) | string[]? PropertyName |
Range |
Numeric ranges (Price, Rating) | decimal? MinPropertyName, decimal? MaxPropertyName |
Boolean |
True/false filters (InStock) | bool? PropertyName |
DateRange |
Date/time ranges | DateTime? PropertyNameFrom, DateTime? PropertyNameTo |
Hierarchical |
Nested categories | string[]? PropertyName |
Attributes Reference
[FacetedSearch]
Marks a class for search generation.
[FacetedSearch(
FilterClassName = "CustomFilter", // Custom filter class name
GenerateAggregations = true, // Generate aggregation methods
GenerateMetadata = true, // Generate metadata class
Namespace = "Custom.Namespace", // Custom namespace for generated code
FullTextStrategy = FullTextSearchStrategy.LinqContains // Full-text search strategy
)]
public class Product { }
[SearchFacet]
Marks a property as a filterable facet.
[SearchFacet(
Type = FacetType.Categorical, // Facet type
DisplayName = "Product Brand", // UI display name
OrderBy = FacetOrder.Count, // Aggregation ordering (Count, Alphabetical)
Limit = 10, // Max aggregation values
DependsOn = "Category", // Dependent facet
IsHierarchical = false, // Hierarchical category
NavigationPath = "Category.Name", // For navigation properties
AutoInclude = true // Auto-include in EF Core queries
)]
public string Brand { get; set; }
[FullTextSearch]
Marks a property for full-text search.
[FullTextSearch(
Weight = 1.0f, // Search relevance weight (higher = more important)
CaseSensitive = false, // Case sensitivity
Behavior = TextSearchBehavior.Contains // Match behavior: Contains, StartsWith, EndsWith, Exact
)]
public string Name { get; set; }
[Searchable]
Marks a property as searchable but not a facet (useful for sorting).
[Searchable(Sortable = true)]
public int Rating { get; set; }
Navigation Properties
Filter on related entities by specifying the navigation path:
public class Product
{
[SearchFacet(
Type = FacetType.Categorical,
DisplayName = "Category",
NavigationPath = "Category.Name" // Filter on Category.Name
)]
public Category Category { get; set; }
}
Generated Code Location
Generated files appear in your project's obj folder:
obj/Debug/net8.0/generated/Facet.Search.Generators/
> ProductSearchFilter.g.cs
> ProductSearchExtensions.g.cs
> ProductFacetAggregations.g.cs
> ProductSearchMetadata.g.cs
Performance Tips
- Use database indexes on facet columns for fast filtering
- Use full-text indexes for text search on large datasets
- Apply filters before pagination to reduce data transfer
- Cache aggregations if they don't change frequently
- Use
Limiton categorical facets to avoid loading all distinct values
Requirements
- .NET Standard 2.0+ (for Facet.Search)
- .NET 10+ (for Facet.Search.EFCore)
- C# 9.0+
License
MIT License — see LICENSE.txt for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Related Projects
- Facet — The Facet ecosystem
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Facet.Search:
| Package | Downloads |
|---|---|
|
Facet.Search.EFCore
Entity Framework Core integration for Facet.Search |
GitHub repositories
This package is not used by any popular GitHub repositories.