Astrolabe.SearchState 4.0.0

dotnet add package Astrolabe.SearchState --version 4.0.0
                    
NuGet\Install-Package Astrolabe.SearchState -Version 4.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Astrolabe.SearchState" Version="4.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Astrolabe.SearchState" Version="4.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Astrolabe.SearchState" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Astrolabe.SearchState --version 4.0.0
                    
#r "nuget: Astrolabe.SearchState, 4.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=Astrolabe.SearchState&version=4.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Astrolabe.SearchState&version=4.0.0
                    
Install as a Cake Tool

Astrolabe.SearchState

NuGet License: MIT

A C# library with generic searching data classes for implementing robust search functionality in .NET applications. Part of the Astrolabe Apps library stack.

Overview

Astrolabe.SearchState provides a set of generic classes and utilities to simplify the implementation of search, filtering, and sorting functionality in your applications. It allows you to build flexible search capabilities with minimal code by providing:

  • Generic search state management
  • Dynamic property-based filtering
  • Dynamic property-based sorting
  • Pagination support
  • Type-safe query building

Installation

dotnet add package Astrolabe.SearchState

Core Features

Search Options

The ISearchOptions interface and SearchOptions implementation provide a standard way to define search parameters:

  • Query: Optional text search query
  • Sort: Configurable sorting expressions
  • Filters: Dynamic filtering criteria
  • Pagination: Offset and length control

Search Results

The SearchResults<T> record encapsulates the results of a search operation:

  • Total: Optional total count of all matching items (for pagination)
  • Entries: The actual result items for the current page

Search Helpers

The SearchHelper class provides utility methods for building flexible search queries:

  • Dynamic Property Filtering: Filter query results based on property values
  • Dynamic Property Sorting: Sort query results by any property
  • Generic Search Builders: Create search functions with minimal boilerplate

Usage Examples

Basic Search Implementation

using Astrolabe.SearchState;

// Define your model
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
    public ProductCategory Category { get; set; }
}

// Define an enum if needed
public enum ProductCategory
{
    Electronics,
    Clothing,
    Food
}

// Create a search function
public class ProductService
{
    private readonly DbContext _dbContext;
    
    public ProductService(DbContext dbContext)
    {
        _dbContext = dbContext;
    }
    
    public Task<SearchResults<Product>> SearchProducts(SearchOptions options, bool includeTotal = true)
    {
        // Create the searcher with default sorters and filterers
        var searcher = SearchHelper.CreateSearcher<Product, Product>(
            query => query.ToListAsync(),
            query => query.CountAsync()
        );
        
        // Execute the search
        return searcher(_dbContext.Products, options, includeTotal);
    }
}

Customizing Sorting

// Create a custom sorter
var sorter = SearchHelper.MakeSorter<Product>(field =>
{
    // Handle special case sorting
    if (field == "relevance")
    {
        var param = Expression.Parameter(typeof(Product));
        return Expression.Lambda<Func<Product, object>>(
            Expression.Convert(
                Expression.MakeMemberAccess(
                    param,
                    typeof(Product).GetProperty(nameof(Product.Id))
                ),
                typeof(object)
            ),
            param
        );
    }
    
    // Fall back to default property-based sorting
    return null;
});

// Use the custom sorter
var searcher = SearchHelper.CreateSearcher<Product, ProductDto>(
    query => query.Select(p => new ProductDto 
    { 
        Id = p.Id, 
        Name = p.Name 
    }).ToListAsync(),
    query => query.CountAsync(),
    sorter
);

Customizing Filtering

// Create a custom filterer
var filterer = SearchHelper.MakeFilterer<Product>(field =>
{
    // Handle price range filtering
    if (field == "priceRange")
    {
        return (query, values) =>
        {
            if (values.Count != 2) return query;
            if (!decimal.TryParse(values[0], out var min)) return query;
            if (!decimal.TryParse(values[1], out var max)) return query;
            
            return query.Where(p => p.Price >= min && p.Price <= max);
        };
    }
    
    // Handle text search
    if (field == "search")
    {
        return (query, values) =>
        {
            var search = values.FirstOrDefault();
            if (string.IsNullOrEmpty(search)) return query;
            
            return query.Where(p => p.Name.Contains(search));
        };
    }
    
    // Fall back to default property-based filtering
    return null;
});

// Use the custom filterer
var searcher = SearchHelper.CreateSearcher<Product, Product>(
    query => query.ToListAsync(),
    query => query.CountAsync(),
    null, // Use default sorter
    filterer
);

Full Example with ASP.NET Core API

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly ProductService _productService;
    
    public ProductsController(ProductService productService)
    {
        _productService = productService;
    }
    
    [HttpGet]
    public async Task<ActionResult<SearchResults<ProductDto>>> Search([FromQuery] SearchOptions options)
    {
        var results = await _productService.SearchProducts(options);
        return Ok(results);
    }
}

Advanced Usage

Custom Member Lookups

The library provides functions to create custom property lookup mechanisms:

// Create a custom property member lookup
var lookup = SearchHelper.CreatePropertyMemberLookup<Product>();

// Use it to get information about a property
var memberInfo = lookup("name"); // Returns FilterableMember for the Name property

Custom Property Getters

You can create expression-based property getters:

// Create a property getter lookup
var getterLookup = SearchHelper.CreatePropertyGetterLookup<Product>();

// Get an expression to access a property
var nameGetter = getterLookup("name"); // Returns Expression<Func<Product, object>> for the Name property

Custom Search Delegate

Create a fully customized search function:

// Create a custom searcher with transformation
var searcherWithMapping = SearchHelper.CreateSearcher<Product, ProductDto>(
    query => query.Select(p => new ProductDto 
    { 
        Id = p.Id, 
        Name = p.Name, 
        DisplayPrice = $"${p.Price:F2}" 
    }).ToListAsync(),
    query => query.CountAsync(),
    maxLength: 100 // Override default max page size
);

Performance Considerations

  • Projection: Use projection in your select delegate to minimize data transfer
  • MaxLength: Set appropriate max length to prevent oversized queries
  • includeTotal: Only set to true when pagination information is needed

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
4.0.0 240 5/16/2025
3.0.0 273 11/24/2024
2.2.1 319 11/21/2024
2.2.0 108 11/21/2024
2.1.0 104 11/21/2024
2.0.0 150 10/17/2024
1.1.0 100 10/17/2024
1.0.1 105 10/15/2024
1.0.0 104 9/26/2024