SiftQL 1.0.0-ci.36

This is a prerelease version of SiftQL.
dotnet add package SiftQL --version 1.0.0-ci.36
                    
NuGet\Install-Package SiftQL -Version 1.0.0-ci.36
                    
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="SiftQL" Version="1.0.0-ci.36" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SiftQL" Version="1.0.0-ci.36" />
                    
Directory.Packages.props
<PackageReference Include="SiftQL" />
                    
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 SiftQL --version 1.0.0-ci.36
                    
#r "nuget: SiftQL, 1.0.0-ci.36"
                    
#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.
#:package SiftQL@1.0.0-ci.36
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=SiftQL&version=1.0.0-ci.36&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=SiftQL&version=1.0.0-ci.36&prerelease
                    
Install as a Cake Tool

SiftQL

CI NuGet

Type-safe event filtering, projection, and subscription routing for .NET.

SiftQL lets you define filters as data (serializable expressions), compile them into fast evaluators at runtime, and route events to matching subscribers -- all with a LINQ-style API backed by a source generator.

Install

dotnet add package SiftQL

Quick Start

Filtering

Define a filter with LINQ expressions and evaluate it against any object:

using SiftQL;

var kernel = QueryKernel.For<OrderPlacedEvent>()
    .Where(e => e.Region == "EU" && e.Total > 100.0);

var compiled = FilterCompiler.Compile(typeof(OrderPlacedEvent), kernel.Filter);

compiled.Matches(order); // true or false

Filters are plain data -- serialize them, store them in a database, or send them over the wire.

Projection

Select only the fields you need (GraphQL-style field selection):

var kernel = QueryKernel.For<UserActivity>()
    .Where(e => e.Action == "purchase" && e.Amount > 50.0)
    .Select(e => e.UserId, e => e.Action, e => e.Amount);

Subscription Routing

Route events to subscribers based on their filter criteria:

using SiftQL.Index;

var index = new TypedFilterSubscriptionIndex<Subscription, SensorReading>();

index.Add(alertSub, FilterExpression.Compare(
    "temperature", FilterOperator.GreaterThan, FilterValue.From(80.0)));

// Find subscriptions whose full filter matches a given event
var matches = index.SnapshotMatches(reading);

// Or use SnapshotCandidates when you only need the fast index candidates.
// Candidate lookup can include false positives for unindexed filters or
// filters that were narrowed by one equality condition.
var candidates = index.SnapshotCandidates(reading);

Serialization

Filters are plain data -- serialize them to JSON, store them in a database, and rehydrate later:

using System.Text.Json;
using SiftQL.Expressions;

// Build a filter
var filter = FilterExpression.And(
    FilterExpression.Compare("region", FilterOperator.Equal, FilterValue.From("EU")),
    FilterExpression.Compare("total", FilterOperator.GreaterThan, FilterValue.From(100.0)));

// Serialize to JSON
string json = JsonSerializer.Serialize(filter);
// Store in DB, send over HTTP, write to a file...

// Deserialize back
FilterExpression restored = JsonSerializer.Deserialize<FilterExpression>(json)!;

// Compile and use
var compiled = FilterCompiler.Compile(typeof(OrderPlacedEvent), restored);
compiled.Matches(order); // works exactly the same

The full query pipeline (filter + projection) also round-trips as JSON:

var kernel = QueryKernel.For<OrderPlacedEvent>()
    .Where(e => e.Region == "EU" && e.Total > 100.0)
    .Select(e => e.OrderId, e => e.Total);

// Serialize the entire pipeline
string pipelineJson = JsonSerializer.Serialize(kernel.Pipeline);

// Deserialize on the other side
var pipeline = JsonSerializer.Deserialize<EventPipelineExpression>(pipelineJson)!;

Filter Capabilities

Write filters as LINQ; SiftQL translates them to serializable expressions.

QueryKernel.For<OrderEvent>()
    // comparisons: == != < <= > >=
    .Where(e => e.Region == "EU" && e.Total >= 100m && e.Total <= 500m)
    // string ops (ordinal, or case-insensitive via StringComparison)
    .Where(e => e.Sku.StartsWith("itm_") || e.Sku.EndsWith("-eu"))
    .Where(e => string.Equals(e.Region, "eu-west", StringComparison.OrdinalIgnoreCase))
    .Where(e => !string.IsNullOrEmpty(e.Coupon))
    // IN over a captured list
    .Where(e => accepted.Contains(e.CustomerId))
    // temporal (DateTime / DateTimeOffset / DateOnly), compared by instant
    .Where(e => e.OccurredAt >= cutoff)
    // collections: Any (incl. correlated &&), All, Count
    .Where(e => e.Lines.Any(l => l.Sku == "X" && l.Qty > 2))   // same element ($elemMatch)
    .Where(e => e.Lines.Count() > 0);

The builder API exposes the same surface as data, including Between (single-node inclusive range), Count, Contains, Exists, In, ElemMatch, prefix/suffix string operators, and a case-insensitive flag:

FilterExpression.Between("total", FilterValue.From(100L), FilterValue.From(500L));
FilterExpression.Count("lines", FilterOperator.GreaterThan, FilterValue.From(0L));
FilterExpression.Compare("region", FilterOperator.Equal, FilterValue.From("eu"), ignoreCase: true);

Tooling

// Text DSL for non-.NET clients
FilterExpression f = FilterQuery.Parse("region == \"EU\" and total between [100, 500]");
string text = FilterQuery.Format(f);

// Versioned, tolerant serialization envelope
string json = FilterDocument.Serialize(f);
FilterExpression back = FilterDocument.Deserialize(json);

// Validate untrusted filters with aggregated, path-located errors + quotas
FilterValidationResult result = FilterValidator.Validate(typeof(OrderEvent), json,
    new FilterLimits { MaxNodes = 64, MaxBytes = 16_384 });

// Publish the filterable surface for client filter-builders (JSON Schema)
JsonObject schema = FilterSchema.For(typeof(OrderEvent)).Describe().ToJsonSchema();

// Dedupe / analyze
string key = FilterExpression.ContentSignature(f);     // stable, canonical
bool ok = FilterExpression.IsSatisfiable(f);           // reject contradictions

Subscription routing accelerates equality, In/Or, and range/threshold filters (Between, >, <=, …) in the index; index.GetStatistics() reports indexed vs unindexed placement for observability.

How It Works

  1. Define filters using LINQ expressions or the expression builder API
  2. Compile filters into optimized evaluators (source-generated for your types)
  3. Evaluate events against compiled filters at runtime with zero reflection

The source generator (SiftQL.Generators) ships inside the SiftQL package as an analyzer -- no extra package reference needed.

Packages

Package Description
SiftQL Core library + source generator
SiftQL.Abstractions Shared interfaces and expression types

Requirements

  • .NET 10+

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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.

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
1.0.0-ci.36 87 6/13/2026
1.0.0-ci.33 75 6/11/2026
1.0.0-ci.27 151 6/10/2026
1.0.0-ci.26 74 6/10/2026
1.0.0-ci.25 72 6/10/2026
1.0.0-ci.22 71 6/10/2026
1.0.0-ci.21 66 6/9/2026
1.0.0-ci.19 74 6/9/2026
1.0.0-ci.15 70 6/9/2026
1.0.0-ci.14 85 6/8/2026
1.0.0-ci.13 72 6/8/2026
1.0.0-ci.12 72 6/8/2026
1.0.0-ci.9 69 6/8/2026
1.0.0-ci.8 74 6/8/2026
1.0.0-ci.7 70 6/8/2026
1.0.0-ci.6 75 6/8/2026
1.0.0-ci.4 73 6/7/2026
1.0.0-ci.3 75 6/7/2026
1.0.0-ci.2 75 6/7/2026
1.0.0-ci.1 74 6/7/2026