59 lines
1.9 KiB
C#
59 lines
1.9 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
|
|
public class ObservationQueryService : IObservationQueryService
|
|
{
|
|
private readonly AppDbContext _db;
|
|
|
|
public ObservationQueryService(AppDbContext db) => _db = db;
|
|
|
|
public async Task<CursorPage<Observation>> GetHistoryAsync(
|
|
Guid encounterId,
|
|
string? code,
|
|
DateTimeOffset? from,
|
|
DateTimeOffset? to,
|
|
int limit,
|
|
string? cursorToken)
|
|
{
|
|
limit = Math.Clamp(limit, 1, 100);
|
|
var cursor = ObservationCursor.Decode(cursorToken);
|
|
|
|
var query = _db.Observations
|
|
.AsNoTracking()
|
|
.Where(o => o.EncounterId == encounterId);
|
|
|
|
if (!string.IsNullOrEmpty(code))
|
|
query = query.Where(o => o.ObservationCode == code);
|
|
|
|
if (from.HasValue)
|
|
query = query.Where(o => o.RecordedAt >= from.Value);
|
|
|
|
if (to.HasValue)
|
|
query = query.Where(o => o.RecordedAt <= to.Value);
|
|
|
|
if (cursor is not null)
|
|
{
|
|
// Keyset condition for ORDER BY recorded_at DESC, id DESC:
|
|
// next page starts just below the cursor position
|
|
var cursorTime = cursor.RecordedAt;
|
|
var cursorId = cursor.Id;
|
|
query = query.Where(o =>
|
|
o.RecordedAt < cursorTime ||
|
|
(o.RecordedAt == cursorTime && o.Id.CompareTo(cursorId) < 0));
|
|
}
|
|
|
|
var items = await query
|
|
.OrderByDescending(o => o.RecordedAt)
|
|
.ThenByDescending(o => o.Id)
|
|
.Take(limit + 1) // fetch one extra to know if there is a next page
|
|
.ToListAsync();
|
|
|
|
var hasMore = items.Count > limit;
|
|
if (hasMore) items.RemoveAt(limit);
|
|
|
|
var nextCursor = hasMore
|
|
? new ObservationCursor(items[^1].RecordedAt, items[^1].Id).Encode()
|
|
: null;
|
|
|
|
return new CursorPage<Observation>(items, nextCursor, hasMore);
|
|
}
|
|
} |