MillerByte.Logging.Api

Data Retrieval & Queries

Last updated: 2/22/2026

The IApiLoggingQueryService interface provides powerful methods for querying logged sessions and actions. Introduced in v1.2.0, this follows the CQRS pattern with separate read and write interfaces for better separation of concerns.

New in v1.2.0: Data retrieval API with advanced filtering, pagination, and multi-database support. For writing operations, see IApiLoggingService.

Injecting the Service

Add IApiLoggingQueryService to your controller or service:

public class AnalyticsController : ControllerBase
{
	private readonly IApiLoggingQueryService _queryService;
	
	public AnalyticsController(IApiLoggingQueryService queryService)
	{
		_queryService = queryService;
	}
}

Querying Sessions

Get paginated sessions with filtering and sorting:

[HttpGet("sessions")]
public async Task<IActionResult> GetSessions(
	[FromQuery] int page = 0,
	[FromQuery] int pageSize = 25,
	[FromQuery] string? userId = null,
	[FromQuery] bool? isActive = null)
{
	var parameters = new QueryParameters
	{
		PageIndex = page,
		PageSize = pageSize,
		Filters = new FilterState
		{
			UserId = userId,
			IsActive = isActive,
			DateRange = new DateRange
			{
				From = DateTime.UtcNow.AddDays(-7), // Last 7 days
				To = DateTime.UtcNow
			}
		},
		Sorting = new List<SortingParameter>
		{
			new SortingParameter { Id = "LoginTime", Desc = true }
		}
	};
	
	var result = await _queryService.GetSessionsAsync(parameters);
	return Ok(result);
}

Querying Actions

Get actions with advanced filtering:

[HttpGet("actions")]
public async Task<IActionResult> GetActions(
	[FromQuery] string? sessionId = null,
	[FromQuery] string? searchText = null,
	[FromQuery] List<int>? statusCodes = null)
{
	var parameters = new QueryParameters
	{
		PageIndex = 0,
		PageSize = 50,
		Filters = new FilterState
		{
			SessionId = sessionId,
			StatusCodes = statusCodes, // e.g., [200, 404, 500]
			SearchText = searchText, // Searches controller, action, route, method, etc.
			Methods = new List<string> { "POST", "PUT", "DELETE" }
		},
		Sorting = new List<SortingParameter>
		{
			new SortingParameter { Id = "TimeStamp", Desc = true }
		}
	};
	
	var result = await _queryService.GetActionsAsync(parameters);
	return Ok(result);
}

Querying Users (v1.2.1+)

Get aggregated user analytics across all their sessions:

[HttpGet("users")]
public async Task<IActionResult> GetUsers(
	[FromQuery] int page = 0,
	[FromQuery] string? searchText = null)
{
	var parameters = new QueryParameters
	{
		PageIndex = page,
		PageSize = 25,
		Filters = new FilterState
		{
			SearchText = searchText
		},
		Sorting = new List<SortingParameter>
		{
			new SortingParameter { Id = "LastActivity", Desc = true }
		}
	};
	
	var result = await _queryService.GetUsersAsync(parameters);
	return Ok(result);
}

Each UserSummary contains:

PropertyTypeDescription
UserIdstringThe user's identifier
TenantIdstringThe user's tenant
TotalSessionsintTotal session count
ActiveSessionsintCurrently active sessions
TotalActionsintTotal API calls across all sessions
LastActivityDateTimeMost recent activity timestamp
FirstSeenDateTimeFirst recorded session timestamp

Get Single Item by ID

// Get specific session
var session = await _queryService.GetSessionByIdAsync("session-id-123");

// Get specific action
var action = await _queryService.GetActionByIdAsync("action-id-456");

Available Filters

For Sessions

FilterTypeDescription
UserIdstring?Exact match on user ID
TenantIdstring?Exact match on tenant ID
IsActivebool?Filter by session active state (null = all)
SearchTextstring?Multi-field search (UserId, TenantId, Id, EnvironmentName)
DateRangeDateRange?Filter by LoginTime

For Actions

FilterTypeDescription
UserIdstring?Exact match on user ID
TenantIdstring?Exact match on tenant ID
SessionIdstring?Exact match on session ID
StatusCodesList<int>?HTTP status codes (e.g., [200, 404, 500])
MethodsList<string>?HTTP methods (e.g., ["GET", "POST"])
SearchTextstring?Multi-field search (UserId, SessionId, Controller, Action, Route, Method, CorrelationId)
DateRangeDateRange?Filter by TimeStamp

Multi-Database Queries

Query from different databases using optional parameters:

// Query from default database
var defaultSessions = await _queryService.GetSessionsAsync(parameters);

// Query sessions from a tenant-specific database
var premiumSessions = await _queryService.GetSessionsAsync(
	parameters,
	databaseName: "logs-tenant-premium",
	collectionName: "premium-sessions");

// Query a specific action from audit database
var auditAction = await _queryService.GetActionByIdAsync(
	"action-id",
	databaseName: "audit-logs",
	collectionName: "critical-actions");

// Query users from a tenant-specific database
var tenantUsers = await _queryService.GetUsersAsync(
	parameters,
	databaseName: "logs-tenant-premium");

Use cases for multi-database queries:

  • Multi-tenant applications with separate databases per tenant
  • Different retention policies (audit logs vs. debug logs)
  • Read replicas for analytics queries
  • Compliance requirements (PCI data in separate database)

TanStack Table Integration

The query models are designed to work seamlessly with TanStack Table and the @millerbyte/react-logging package:

// API endpoint that supports TanStack Table state
[HttpPost("actions/query")]
public async Task<IActionResult> QueryActions([FromBody] QueryParameters parameters)
{
	// Parameters automatically map from TanStack Table's state
	var result = await _queryService.GetActionsAsync(parameters);
	return Ok(result);
}

Performance Tips

  • Pagination: Always use pagination for large datasets. Default page size is 25, max is 1000 to prevent excessive memory usage.
  • Indexes: Ensure MongoDB indexes exist on frequently filtered fields (UserId, TenantId, SessionId, TimeStamp, StatusCode).
  • Search Text: SearchText performs regex searches which can be slower. Limit to 200 characters and use specific filters when possible.
  • Date Ranges: Always specify date ranges for time-based queries to leverage index scans.
  • Parallel Queries: Count and data queries run in parallel automatically for better performance (~40-50% faster).

Recommended MongoDB Indexes

// Sessions collection
db.LoginSessions.createIndex({ "UserId": 1, "LoginTime": -1 });
db.LoginSessions.createIndex({ "TenantId": 1, "IsActive": 1 });
db.LoginSessions.createIndex({ "IsActive": 1, "LastActivityTime": -1 });

// Actions collection
db.ApiActions.createIndex({ "UserId": 1, "TimeStamp": -1 });
db.ApiActions.createIndex({ "SessionId": 1, "TimeStamp": -1 });
db.ApiActions.createIndex({ "ResponseInfo.StatusCode": 1 });
db.ApiActions.createIndex({ "TenantId": 1, "TimeStamp": -1 });

Next Steps