Skip to content

Commit bc12665

Browse files
Implement pagination in HashSearcher (#160)
- Update DecomposedQuery to include Limit and Offset - Update HashSearcher.Search to apply Skip/Take logic - Ensure stable sorting by ID when paginating - Enable and implement skipped test in HashSearcherTests - Add new test for Offset logic Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent fa1e221 commit bc12665

File tree

4 files changed

+71
-8
lines changed

4 files changed

+71
-8
lines changed

Octans.Core/Querying/HashSearcher.cs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,49 @@ public async Task<HashSet<HashItem>> Search(DecomposedQuery request, Cancellatio
3232
{
3333
if (request.IsEmpty() || request.SystemPredicates.OfType<EverythingPredicate>().Any())
3434
{
35-
var allHashes = await context.Hashes.ToListAsync(cancellationToken);
35+
var query = context.Hashes.AsQueryable();
36+
37+
if (request.Offset > 0)
38+
{
39+
query = query.OrderBy(h => h.Id).Skip(request.Offset);
40+
}
41+
42+
if (request.Limit.HasValue)
43+
{
44+
if (request.Offset == 0)
45+
{
46+
query = query.OrderBy(h => h.Id);
47+
}
48+
query = query.Take(request.Limit.Value);
49+
}
50+
51+
var allHashes = await query.ToListAsync(cancellationToken);
3652
return allHashes.ToHashSet();
3753
}
3854

3955
var matchingIds = await GetMatchingTagIds(request, cancellationToken);
4056

41-
var hashes = await context.Mappings
57+
var hashesQuery = context.Mappings
4258
.Where(m => matchingIds.Contains(m.Tag.Id))
4359
.Include(m => m.Hash)
4460
.Select(m => m.Hash)
45-
.Distinct()
46-
.ToListAsync(cancellationToken);
61+
.Distinct();
62+
63+
if (request.Offset > 0)
64+
{
65+
hashesQuery = hashesQuery.OrderBy(h => h.Id).Skip(request.Offset);
66+
}
67+
68+
if (request.Limit.HasValue)
69+
{
70+
if (request.Offset == 0)
71+
{
72+
hashesQuery = hashesQuery.OrderBy(h => h.Id);
73+
}
74+
hashesQuery = hashesQuery.Take(request.Limit.Value);
75+
}
76+
77+
var hashes = await hashesQuery.ToListAsync(cancellationToken);
4778

4879
return hashes.ToHashSet();
4980
}

Octans.Core/Querying/QueryTagConverter.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ public class DecomposedQuery
9292
public HashSet<string> WildcardDoublesToInclude { get; init; } = [];
9393
public HashSet<string> WildcardDoublesToExclude { get; init; } = [];
9494

95+
public int? Limit { get; init; }
96+
public int Offset { get; init; }
97+
9598
public bool IsEmpty()
9699
{
97100
var hasContent = TagsToInclude.Any() || WildcardNamespacesToInclude.Any() || WildcardSubtagsToInclude.Any() ||

Octans.Tests/Querying/HashSearcherTests.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,39 @@ public async Task CountAsync_ReturnsAll_WhenEmptyQuery()
148148
count.Should().Be(total);
149149
}
150150

151-
[Fact(Skip = "Not implemented yet")]
152-
public void ReturnsOnlyNHashes_WhenALimitOfNIsSpecified()
151+
[Fact]
152+
public async Task ReturnsOnlyNHashes_WhenALimitOfNIsSpecified()
153+
{
154+
await SeedData();
155+
var request = new DecomposedQuery
156+
{
157+
Limit = 2
158+
};
159+
160+
var results = await _sut.Search(request);
161+
162+
results.Should().HaveCount(2);
163+
}
164+
165+
[Fact]
166+
public async Task SkipsNHashes_WhenAnOffsetOfNIsSpecified()
153167
{
154-
throw new NotImplementedException();
168+
await SeedData();
169+
170+
// Get all items ordered by ID to simulate the default sort order in Search
171+
var allItems = await _db.Hashes.OrderBy(h => h.Id).ToListAsync();
172+
173+
var expected = allItems.Skip(2).Take(1).Single();
174+
175+
var request = new DecomposedQuery
176+
{
177+
Offset = 2,
178+
Limit = 1
179+
};
180+
181+
var results = await _sut.Search(request);
182+
183+
results.Single().Id.Should().Be(expected.Id);
155184
}
156185

157186
private async Task SeedData()

TASKS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Network.
2525
- **Task:** Implement the actual logic to fetch data from different downloaders (Galleries/Queries) periodically.
2626

2727
- [ ] **Search & Querying**
28-
- **Task:** Implement search limits/pagination in `HashSearcher` (currently skipped test in `HashSearcherTests`).
28+
- [x] **Task:** Implement search limits/pagination in `HashSearcher` (currently skipped test in `HashSearcherTests`).
2929
- **Task:** Implement parsing for other `system:` predicates in `QueryParser` (e.g. `system:inbox`, `system:archive`, etc.). Only `system:everything` is currently supported.
3030

3131
## 2. User Interface (Client)

0 commit comments

Comments
 (0)