search for standard sites pub-search.waow.tech
search zig blog atproto
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: since filter now applies to base_path queries too

base_path queries (publication name matches) were bypassing the since
filter, leaking old results. added since-aware turso query variants
and post-fetch date filtering in searchLocal.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

zzstoatzz 34422e3d 75e30a55

+47 -2
+47 -2
backend/src/search.zig
··· 219 219 \\ORDER BY rank + (julianday('now') - julianday(d.created_at)) / 30.0 LIMIT 40 220 220 ); 221 221 222 + const DocsByPubBasePathAndSince = zql.Query( 223 + \\SELECT d.uri, d.did, d.title, '' as snippet, 224 + \\ d.created_at, d.rkey, 225 + \\ p.base_path, 226 + \\ 1 as has_publication, 227 + \\ d.platform, COALESCE(d.path, '') as path 228 + \\FROM documents d 229 + \\JOIN publications p ON d.publication_uri = p.uri 230 + \\JOIN publications_fts pf ON p.uri = pf.uri 231 + \\WHERE publications_fts MATCH :query AND d.created_at >= :since 232 + \\ORDER BY rank + (julianday('now') - julianday(d.created_at)) / 30.0 LIMIT 40 233 + ); 234 + 235 + const DocsByPubBasePathAndPlatformAndSince = zql.Query( 236 + \\SELECT d.uri, d.did, d.title, '' as snippet, 237 + \\ d.created_at, d.rkey, 238 + \\ p.base_path, 239 + \\ 1 as has_publication, 240 + \\ d.platform, COALESCE(d.path, '') as path 241 + \\FROM documents d 242 + \\JOIN publications p ON d.publication_uri = p.uri 243 + \\JOIN publications_fts pf ON p.uri = pf.uri 244 + \\WHERE publications_fts MATCH :query AND d.platform = :platform AND d.created_at >= :since 245 + \\ORDER BY rank + (julianday('now') - julianday(d.created_at)) / 30.0 LIMIT 40 246 + ); 247 + 222 248 /// Publication search result (internal) 223 249 const Pub = struct { 224 250 uri: []const u8, ··· 349 375 // query 1: documents by publication base_path (subdomain search) 350 376 const run_basepath = has_query and !has_tag; 351 377 if (run_basepath) { 352 - if (has_platform) { 378 + if (has_platform and has_since) { 379 + statements[stmt_count] = .{ .sql = DocsByPubBasePathAndPlatformAndSince.positional, .args = &.{ fts_query, platform_filter.?, since_filter.? } }; 380 + } else if (has_platform) { 353 381 statements[stmt_count] = .{ .sql = DocsByPubBasePathAndPlatform.positional, .args = &.{ fts_query, platform_filter.? } }; 382 + } else if (has_since) { 383 + statements[stmt_count] = .{ .sql = DocsByPubBasePathAndSince.positional, .args = &.{ fts_query, since_filter.? } }; 354 384 } else { 355 385 statements[stmt_count] = .{ .sql = DocsByPubBasePath.positional, .args = &.{fts_query} }; 356 386 } ··· 416 446 fn searchLocal(alloc: Allocator, local: *db.LocalDb, query: []const u8, tag_filter: ?[]const u8, platform_filter: ?[]const u8, since_filter: ?[]const u8) ![]const u8 { 417 447 // only handle basic FTS queries for now (most common case) 418 448 // fall back to Turso for complex filter combinations 419 - if (query.len == 0 or tag_filter != null or since_filter != null) { 449 + if (query.len == 0 or tag_filter != null) { 420 450 return error.UnsupportedQuery; 421 451 } 422 452 ··· 452 482 453 483 while (rows.next()) |row| { 454 484 const doc = Doc.fromLocalRow(row); 485 + if (since_filter) |since| { 486 + if (doc.createdAt.len > 0 and std.mem.order(u8, doc.createdAt, since) == .lt) continue; 487 + } 455 488 if (try isDuplicateAuthorTitle(&seen_authors, alloc, doc.did, doc.title)) continue; 456 489 const uri_dupe = try alloc.dupe(u8, doc.uri); 457 490 try seen_uris.put(uri_dupe, {}); ··· 473 506 474 507 while (bp_rows.next()) |row| { 475 508 const doc = Doc.fromLocalRow(row); 509 + if (since_filter) |since| { 510 + if (doc.createdAt.len > 0 and std.mem.order(u8, doc.createdAt, since) == .lt) continue; 511 + } 476 512 if (!seen_uris.contains(doc.uri) and !try isDuplicateAuthorTitle(&seen_authors, alloc, doc.did, doc.title)) { 477 513 try jw.write(doc.toJson()); 478 514 } ··· 497 533 var doc_count: u32 = 0; 498 534 while (rows.next()) |row| { 499 535 const doc = Doc.fromLocalRow(row); 536 + if (since_filter) |since| { 537 + if (doc.createdAt.len > 0 and std.mem.order(u8, doc.createdAt, since) == .lt) continue; 538 + } 500 539 if (try isDuplicateAuthorTitle(&seen_authors, alloc, doc.did, doc.title)) continue; 501 540 const uri_dupe = try alloc.dupe(u8, doc.uri); 502 541 try seen_uris.put(uri_dupe, {}); ··· 525 564 var bp_count: u32 = 0; 526 565 while (bp_rows.next()) |row| { 527 566 const doc = Doc.fromLocalRow(row); 567 + if (since_filter) |since| { 568 + if (doc.createdAt.len > 0 and std.mem.order(u8, doc.createdAt, since) == .lt) { 569 + bp_count += 1; 570 + continue; 571 + } 572 + } 528 573 if (!seen_uris.contains(doc.uri) and !try isDuplicateAuthorTitle(&seen_authors, alloc, doc.did, doc.title)) { 529 574 try jw.write(doc.toJson()); 530 575 }