Code Examples

Learn RedBase by example. Browse working code samples for LINQ queries, bulk operations, tree structures, and more.

Data Model - EmployeeProps Click to view data model used in all examples
Tag with priority for categorization.
/// <summary>
/// Tag with priority for categorization.
/// </summary>
public class Tag
{
    public string Name { get; set; } = string.Empty;
    public int Priority { get; set; }
    public string? Description { get; set; }
}

/// <summary>
/// Project metrics for analytics.
/// </summary>
[RedbScheme("ProjectMetrics")]
public class ProjectMetricsProps
{
    public long ProjectId { get; set; }
    public long? TasksCompleted { get; set; }
    public long? TasksTotal { get; set; }
    public long? BugsFixed { get; set; }
    public double? Budget { get; set; }
    public long? TeamSize { get; set; }
    public Tag[]? Tags { get; set; }
    public string[]? Technologies { get; set; }
}

/// <summary>
/// Address with city, street and building details.
/// </summary>
public class Address
{
    public string City { get; set; } = string.Empty;
    public string Street { get; set; } = string.Empty;
    public BuildingInfo? Building { get; set; }
}

/// <summary>
/// Building information with floor and amenities.
/// </summary>
public class BuildingInfo
{
    public int Floor { get; set; }
    public string Name { get; set; } = string.Empty;
    public string[]? Amenities { get; set; }
    public int[]? AccessCodes { get; set; }
    public string[]? ParkingSpots { get; set; }
    public int[]? ElevatorFloors { get; set; }
}

/// <summary>
/// Contact detail key-value pair.
/// </summary>
public class ContactDetail
{
    public string Label { get; set; } = string.Empty;
    public string Value { get; set; } = string.Empty;
}

/// <summary>
/// Contact information (email, phone, etc).
/// </summary>
public class Contact
{
    public string Type { get; set; } = string.Empty;
    public string Value { get; set; } = string.Empty;
    public bool IsVerified { get; set; }
    public int[]? NotificationHours { get; set; }
    public ContactDetail[]? Metadata { get; set; }
}

/// <summary>
/// Department info with tags, metrics and nested budget data.
/// </summary>
public class Department
{
    public string Name { get; set; } = string.Empty;
    public int HeadCount { get; set; }
    public string[] Projects { get; set; } = [];
    public Tag[] Leaders { get; set; } = [];
    public Dictionary<string, int>? BudgetByYear { get; set; }
}

/// <summary>
/// Employee props - main model for examples.
/// Demonstrates all supported types:
/// - Simple types (int, string, DateTime, long, decimal)
/// - Arrays (string[], int[])
/// - Business classes (Address with nested BuildingInfo)
/// - Array of business classes (Contact[])
/// - RedbObject references (CurrentProject, PastProjects[])
/// - Dictionary types (various key and value types)
/// </summary>
[RedbScheme("Employee")]
public class EmployeeProps
{
    // Basic info
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public int Age { get; set; }
    public DateTime HireDate { get; set; }
    public string Position { get; set; } = string.Empty;
    public decimal Salary { get; set; }
    public string Department { get; set; } = string.Empty;
    public string? EmployeeCode { get; set; }

    // Skills and certifications (arrays)
    public string[]? Skills { get; set; }
    public int[]? SkillLevels { get; set; }
    public string[]? Certifications { get; set; }
    public int[]? CertificationYears { get; set; }

    // Addresses (business classes)
    public Address? HomeAddress { get; set; }
    public Address? WorkAddress { get; set; }
    public Address? EmergencyAddress { get; set; }

    // Contacts (array of business classes)
    public Contact[]? Contacts { get; set; }

    // Project references (RedbObject)
    public RedbObject<ProjectMetricsProps>? CurrentProject { get; set; }
    public RedbObject<ProjectMetricsProps>[]? PastProjects { get; set; }

    // Phone directory: extension -> phone number
    public Dictionary<string, string>? PhoneDirectory { get; set; }

    // Office locations by city
    public Dictionary<string, Address>? OfficeLocations { get; set; }

    // Bonus history by year
    public Dictionary<int, decimal>? BonusByYear { get; set; }

    // Department details with complex nested data (Pro)
    public Dictionary<string, Department>? DepartmentHistory { get; set; }

    // Composite key: (year, quarter) -> performance score (Pro)
    public Dictionary<(int Year, string Quarter), string>? PerformanceReviews { get; set; }

    // Project metrics by code (Pro)
    public Dictionary<string, RedbObject<ProjectMetricsProps>>? ProjectMetrics { get; set; }
}

/// <summary>
/// Department props for tree examples (corporate hierarchy).
/// Used with TreeRedbObject for organizational structure.
/// Similar to CategoryTestProps in ConsoleTest.
/// </summary>
[RedbScheme("Department")]
public class DepartmentProps
{
    /// <summary>Department name (e.g. "IT Department Moscow").</summary>
    public string Name { get; set; } = string.Empty;

    /// <summary>Description (e.g. "Software development team").</summary>
    public string Description { get; set; } = string.Empty;

    /// <summary>Is department active?</summary>
    public bool IsActive { get; set; } = true;

    /// <summary>Budget in USD.</summary>
    public decimal Budget { get; set; }

    /// <summary>Department code (e.g. "IT-MSK-DEV").</summary>
    public string Code { get; set; } = string.Empty;
}

/// <summary>
/// City props for list examples (linked objects).
/// Used as target object for RedbListItem.Object reference.
/// </summary>
[RedbScheme("City")]
public class CityProps
{
    /// <summary>City name.</summary>
    public string Name { get; set; } = string.Empty;
    
    /// <summary>Population count.</summary>
    public int Population { get; set; }
    
    /// <summary>Region or district.</summary>
    public string Region { get; set; } = string.Empty;
    
    /// <summary>Is capital city?</summary>
    public bool IsCapital { get; set; }
    
    /// <summary>GPS coordinates [lat, lon].</summary>
    public double[] Coordinates { get; set; } = [];
}

/// <summary>
/// Person props demonstrating ListItem fields.
/// Shows single ListItem and List of ListItems usage.
/// </summary>
[RedbScheme("Person")]
public class PersonProps
{
    /// <summary>Person name.</summary>
    public string Name { get; set; } = string.Empty;
    
    /// <summary>Age in years.</summary>
    public int Age { get; set; }
    
    /// <summary>Email address.</summary>
    public string Email { get; set; } = string.Empty;
    
    /// <summary>Single ListItem field (e.g. status from dictionary).</summary>
    public RedbListItem? Status { get; set; }
    
    /// <summary>Array of ListItems (e.g. roles from dictionary).</summary>
    public List<RedbListItem>? Roles { get; set; }
}

WhereInRedb - Base Field

Filter by list of base field values.
WHERE o._id IN (...) without JOIN - very fast.
var sw = Stopwatch.StartNew();

// First get some IDs to filter by
var sampleIds = await redb.Query<EmployeeProps>()
    .Take(5)
    .Select(x => x.id)
    .ToListAsync();

if (sampleIds.Count == 0)
{
    sw.Stop();
    return Fail("E141", "WhereInRedb - Base Field", ExampleTier.Free, sw.ElapsedMilliseconds,
        "No employees found. Run E000 first.");
}

var query = redb.Query<EmployeeProps>()
    .WhereInRedb(x => x.Id, sampleIds);

// Uncomment to see generated SQL:
// var sql = await query.ToSqlStringAsync();
// Console.WriteLine(sql);

var results = await query.ToListAsync();

sw.Stop();
WhereInWhereInRedbBaseFilterIRedbQueryable.WhereInRedb
MS 98ms(5)
MS Pro 30ms(5)
PG 44ms(5)
PG Pro 22ms(5)

GroupByArray - Array Elements

Groups objects by elements of an array field using GroupByArray.
Expands arrays to create one group per element (e.g., group by each Contact.Type).
var sw = Stopwatch.StartNew();

// Group by each Contact.Type (email, phone, etc.)
// This expands the Contacts[] array and groups by contact type
var byContactType = await redb.Query<EmployeeProps>()
    .Where(e => e.Contacts != null)
    .GroupByArray(e => e.Contacts!, c => c.Type)
    .SelectAsync(g => new
    {
        ContactType = g.Key,
        EmployeeCount = Agg.Count(g)
    });

// Uncomment to see generated SQL:
// var sql = await redb.Query<EmployeeProps>()
//     .GroupByArray(e => e.Contacts!, c => c.Type)
//     .ToSqlStringAsync(g => new { g.Key, Agg.Count(g) });
// Console.WriteLine(sql);

sw.Stop();

var output = byContactType.OrderByDescending(g => g.EmployeeCount)
    .Select(g => $"{g.ContactType}: {g.EmployeeCount} employees").ToArray();
GroupByArrayArrayExpandIRedbQueryable.GroupByArray
MS 148ms(2)
MS Pro 173ms(2)
PG 66ms(2)
PG Pro 70ms(2)

SumRedbAsync - Base Field Sum

SumRedbAsync - sum of base IRedbObject field WITHOUT JOIN to _values.
Much faster than SumAsync for base fields like Id.
var sw = Stopwatch.StartNew();

// Sum of base field Id (from _objects table, NO JOIN with _values!)
var sumId = await redb.Query<EmployeeProps>().SumRedbAsync(x => x.Id);

// Compare with regular count
var count = await redb.Query<EmployeeProps>().CountAsync();

sw.Stop();
SumRedbAsyncAggregationNoJoinIRedbQueryable.SumRedbAsync
MS 7ms(5603)
MS Pro 7ms(5086)
PG 9ms(13769)
PG Pro 11ms(13977)

Min/MaxRedbAsync - Date Range

MinRedbAsync/MaxRedbAsync - min/max of base IRedbObject fields WITHOUT JOIN.
Perfect for finding date ranges (DateCreate, DateModify).
var sw = Stopwatch.StartNew();

// Min/Max of DateCreate (from _objects table, NO JOIN!)
var minDate = await redb.Query<EmployeeProps>().MinRedbAsync(x => x.DateCreate);
var maxDate = await redb.Query<EmployeeProps>().MaxRedbAsync(x => x.DateCreate);

sw.Stop();

var range = minDate.HasValue && maxDate.HasValue 
    ? (maxDate.Value - minDate.Value).Days 
    : 0;
MinRedbAsyncMaxRedbAsyncDateRangeIRedbQueryable.MinRedbAsyncIRedbQueryable.MaxRedbAsync
MS 7ms(2)
MS Pro 8ms(2)
PG 7ms(2)
PG Pro 19ms(2)

AverageRedbAsync - Base Field Avg

AverageRedbAsync - average of base IRedbObject field WITHOUT JOIN.
Useful for statistics on Id distribution.
var sw = Stopwatch.StartNew();

// Average of base field Id (from _objects table, NO JOIN!)
var avgId = await redb.Query<EmployeeProps>().AverageRedbAsync(x => x.Id);

// Get min/max for context
var minId = await redb.Query<EmployeeProps>().MinRedbAsync(x => x.Id);
var maxId = await redb.Query<EmployeeProps>().MaxRedbAsync(x => x.Id);

sw.Stop();
AverageRedbAsyncAggregationNoJoinIRedbQueryable.AverageRedbAsync
MS 13ms(1)
MS Pro 12ms(1)
PG 12ms(1)
PG Pro 22ms(1)

AggregateRedbAsync - Batch Base

AggregateRedbAsync - batch aggregation of base IRedbObject fields in ONE query.
Combines Sum, Min, Max, Avg, Count on base fields WITHOUT JOIN.
var sw = Stopwatch.StartNew();

// Multiple aggregations on base fields in ONE query (NO JOIN!)
var stats = await redb.Query<EmployeeProps>()
    .AggregateRedbAsync(x => new
    {
        MaxId = Agg.Max(x.Id),
        MinDateCreate = Agg.Min(x.DateCreate),
        MaxDateModify = Agg.Max(x.DateModify),
        AvgId = Agg.Average(x.Id),
        Count = Agg.Count()
    });

sw.Stop();
AggregateRedbAsyncBatchNoJoinIRedbQueryable.AggregateRedbAsyncAgg.SumAgg.MinAgg.Max
MS 6ms(5603)
MS Pro 7ms(5086)
PG 10ms(13769)
PG Pro 11ms(13977)

Window - PartitionByRedb

PartitionByRedb in Window Functions - partition by base IRedbObject field.
SQL: PARTITION BY o._id_scheme (NO JOIN for partition key!)
var sw = Stopwatch.StartNew();

// Window with PartitionByRedb (base field) + OrderByDesc (Props field)
var ranked = await redb.Query<EmployeeProps>()
    .Take(50)
    .WithWindow(w => w
        .PartitionByRedb(x => x.SchemeId)  // Base field - NO JOIN!
        .OrderByDesc(x => x.Salary))       // Props field
    .SelectAsync(x => new
    {
        x.Name,
        SchemeId = x.SchemeId,
        Salary = x.Props.Salary,
        RankInScheme = Win.RowNumber()
    });

sw.Stop();

var first = ranked.FirstOrDefault();
PartitionByRedbWindowNoJoinIWindowBuilder.PartitionByRedbWin.RowNumber
MS 38ms(50)
MS Pro 54ms(50)
PG 46ms(50)
PG Pro 40ms(50)

Window - OrderByRedb

OrderByRedb in Window Functions - order by base IRedbObject field.
SQL: ORDER BY o._date_create (NO JOIN for order key!)
var sw = Stopwatch.StartNew();

// Window with OrderByRedb (base field) + Frame
var ordered = await redb.Query<EmployeeProps>()
    .Take(50)
    .WithWindow(w => w
        .OrderByRedb(x => x.DateCreate)           // Base field - NO JOIN!
        .Frame(Frame.Rows().UnboundedPreceding()))
    .SelectAsync(x => new
    {
        x.Name,
        Created = x.DateCreate,
        Salary = x.Props.Salary,
        RunningCount = Win.Count()
    });

sw.Stop();

var last = ordered.LastOrDefault();
OrderByRedbWindowNoJoinIWindowBuilder.OrderByRedbWin.CountFrame.Rows
MS 25ms(50)
MS Pro 53ms(50)
PG 4ms(50)
PG Pro 38ms(50)

Select - Projection

Select projection - load only specific fields instead of full object.
Reduces data transfer and improves performance.
var sw = Stopwatch.StartNew();

// Project only 3 fields from 20+ available
var projected = await redb.Query<EmployeeProps>()
    .Take(100)
    .Select(x => new
    {
        x.id,
        x.Props.FirstName,
        x.Props.Salary
    })
    .ToListAsync();

// Compare with full load
var swFull = Stopwatch.StartNew();
var full = await redb.Query<EmployeeProps>().Take(100).ToListAsync();
swFull.Stop();

sw.Stop();

var first = projected.FirstOrDefault();
SelectProjectionPerformanceIRedbQueryable.Select
MS 1486ms(100)
MS Pro 130ms(100)
PG 563ms(100)
PG Pro 56ms(100)

Array Aggregation

Array aggregation - aggregate all elements of array field or specific index.
SkillLevels[] contains numeric values (1-5) for aggregation.
var sw = Stopwatch.StartNew();

// Aggregate ALL elements of SkillLevels[] array
var allElements = await redb.Query<EmployeeProps>()
    .AggregateAsync(x => new
    {
        TotalSkillPoints = Agg.Sum(x.Props.SkillLevels.Select(s => s)),
        AvgSkillLevel = Agg.Average(x.Props.SkillLevels.Select(s => s)),
        Count = Agg.Count()
    });

// Aggregate specific index: SkillLevels[0] (first skill)
var firstElement = await redb.Query<EmployeeProps>()
    .AggregateAsync(x => new
    {
        SumFirstSkill = Agg.Sum(x.Props.SkillLevels[0]),
        AvgFirstSkill = Agg.Average(x.Props.SkillLevels[0])
    });

sw.Stop();
ArrayAggregationSumAgg.SumAgg.Average
MS 104ms(5588)
MS Pro 103ms(4900)
PG 91ms(13400)
PG Pro 68ms(13600)