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: push author filter into SQL WHERE clause before LIMIT

The author filter was a post-filter applied after LIMIT 40, so for
common words like "up", the target author's docs could rank outside
the top 40 and get cut before filtering. Now the condition is in the
SQL itself using (? = '' OR d.did = ?) which is a no-op when empty.

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

zzstoatzz 97758ab6 12277d0a

+36 -16
+36 -16
backend/src/server/search.zig
··· 360 360 return false; 361 361 } 362 362 363 + /// Inject an author DID condition into a SQL query's WHERE clause before ORDER BY. 364 + /// Uses the (? = '' OR col = ?) pattern: no-op when author_val is empty. 365 + fn addAuthorCondition(alloc: Allocator, stmt: db.Client.Statement, col: []const u8, author_val: []const u8) !db.Client.Statement { 366 + const order_idx = std.mem.indexOf(u8, stmt.sql, "ORDER BY") orelse return stmt; 367 + const new_sql = try std.fmt.allocPrint(alloc, "{s}AND (? = '' OR {s} = ?) {s}", .{ stmt.sql[0..order_idx], col, stmt.sql[order_idx..] }); 368 + const new_args = try alloc.alloc([]const u8, stmt.args.len + 2); 369 + @memcpy(new_args[0..stmt.args.len], stmt.args); 370 + new_args[stmt.args.len] = author_val; 371 + new_args[stmt.args.len + 1] = author_val; 372 + return .{ .sql = new_sql, .args = new_args }; 373 + } 374 + 363 375 /// Keyword search: FTS5 via local SQLite or Turso fallback. 364 376 fn searchKeyword(alloc: Allocator, query: []const u8, tag_filter: ?[]const u8, platform_filter: ?[]const u8, since_filter: ?[]const u8, author_filter: ?[]const u8) ![]const u8 { 365 377 // try local SQLite first (faster for FTS queries) ··· 431 443 var statements: [3]db.Client.Statement = undefined; 432 444 var stmt_count: usize = 0; 433 445 446 + // author condition: inject into SQL so LIMIT applies after author filtering 447 + const author_val: []const u8 = if (author_filter) |af| af else ""; 448 + 434 449 // query 0: documents by content (always present if we have any filter) 435 450 const doc_sql = getDocQuerySql(has_query, has_tag, has_platform, has_since); 436 451 const doc_args = try getDocQueryArgs(alloc, fts_query, tag_filter, platform_filter, since_filter, has_query, has_tag, has_platform, has_since); 437 452 if (doc_sql) |sql| { 438 - statements[stmt_count] = .{ .sql = sql, .args = doc_args }; 453 + statements[stmt_count] = try addAuthorCondition(alloc, .{ .sql = sql, .args = doc_args }, "d.did", author_val); 439 454 stmt_count += 1; 440 455 } 441 456 442 457 // query 1: documents by publication base_path (subdomain search) 443 458 const run_basepath = has_query and !has_tag; 444 459 if (run_basepath) { 460 + var base_stmt: db.Client.Statement = undefined; 445 461 if (has_platform and has_since) { 446 - statements[stmt_count] = .{ .sql = DocsByPubBasePathAndPlatformAndSince.positional, .args = &.{ fts_query, platform_filter.?, since_filter.? } }; 462 + base_stmt = .{ .sql = DocsByPubBasePathAndPlatformAndSince.positional, .args = &.{ fts_query, platform_filter.?, since_filter.? } }; 447 463 } else if (has_platform) { 448 - statements[stmt_count] = .{ .sql = DocsByPubBasePathAndPlatform.positional, .args = &.{ fts_query, platform_filter.? } }; 464 + base_stmt = .{ .sql = DocsByPubBasePathAndPlatform.positional, .args = &.{ fts_query, platform_filter.? } }; 449 465 } else if (has_since) { 450 - statements[stmt_count] = .{ .sql = DocsByPubBasePathAndSince.positional, .args = &.{ fts_query, since_filter.? } }; 466 + base_stmt = .{ .sql = DocsByPubBasePathAndSince.positional, .args = &.{ fts_query, since_filter.? } }; 451 467 } else { 452 - statements[stmt_count] = .{ .sql = DocsByPubBasePath.positional, .args = &.{fts_query} }; 468 + base_stmt = .{ .sql = DocsByPubBasePath.positional, .args = &.{fts_query} }; 453 469 } 470 + statements[stmt_count] = try addAuthorCondition(alloc, base_stmt, "d.did", author_val); 454 471 stmt_count += 1; 455 472 } 456 473 457 474 // query 2: publications (only when no tag/platform filter) 458 475 const run_pubs = tag_filter == null and platform_filter == null and has_query; 459 476 if (run_pubs) { 460 - statements[stmt_count] = .{ .sql = PubSearch.positional, .args = &.{fts_query} }; 477 + statements[stmt_count] = try addAuthorCondition(alloc, .{ .sql = PubSearch.positional, .args = &.{fts_query} }, "p.did", author_val); 461 478 stmt_count += 1; 462 479 } 463 480 ··· 527 544 return error.UnsupportedQuery; 528 545 } 529 546 547 + // author condition: pass DID or "" (empty = no-op via SQL "? = '' OR d.did = ?") 548 + const author_val: []const u8 = if (author_filter) |af| af else ""; 549 + 530 550 var output: std.Io.Writer.Allocating = .init(alloc); 531 551 errdefer output.deinit(); 532 552 ··· 553 573 \\ COALESCE(d.cover_image, '') as cover_image 554 574 \\FROM documents_fts f 555 575 \\JOIN documents d ON f.uri = d.uri 556 - \\WHERE documents_fts MATCH ? AND d.platform = ? 576 + \\WHERE documents_fts MATCH ? AND d.platform = ? AND (? = '' OR d.did = ?) 557 577 \\ORDER BY rank LIMIT 40 558 - , .{ fts_query, platform }); 578 + , .{ fts_query, platform, author_val, author_val }); 559 579 defer rows.deinit(); 560 580 561 581 while (rows.next()) |row| { ··· 581 601 \\FROM documents d 582 602 \\JOIN publications p ON d.publication_uri = p.uri 583 603 \\JOIN publications_fts pf ON p.uri = pf.uri 584 - \\WHERE publications_fts MATCH ? AND d.platform = ? 604 + \\WHERE publications_fts MATCH ? AND d.platform = ? AND (? = '' OR d.did = ?) 585 605 \\ORDER BY rank LIMIT 40 586 - , .{ fts_query, platform }); 606 + , .{ fts_query, platform, author_val, author_val }); 587 607 defer bp_rows.deinit(); 588 608 589 609 while (bp_rows.next()) |row| { ··· 608 628 \\ COALESCE(d.cover_image, '') as cover_image 609 629 \\FROM documents_fts f 610 630 \\JOIN documents d ON f.uri = d.uri 611 - \\WHERE documents_fts MATCH ? 631 + \\WHERE documents_fts MATCH ? AND (? = '' OR d.did = ?) 612 632 \\ORDER BY rank LIMIT 40 613 - , .{fts_query}); 633 + , .{ fts_query, author_val, author_val }); 614 634 defer rows.deinit(); 615 635 616 636 { ··· 643 663 \\FROM documents d 644 664 \\JOIN publications p ON d.publication_uri = p.uri 645 665 \\JOIN publications_fts pf ON p.uri = pf.uri 646 - \\WHERE publications_fts MATCH ? 666 + \\WHERE publications_fts MATCH ? AND (? = '' OR d.did = ?) 647 667 \\ORDER BY rank LIMIT 40 648 - , .{fts_query}); 668 + , .{ fts_query, author_val, author_val }); 649 669 defer bp_rows.deinit(); 650 670 651 671 { ··· 678 698 \\ p.rkey, p.base_path, p.platform 679 699 \\FROM publications_fts f 680 700 \\JOIN publications p ON f.uri = p.uri 681 - \\WHERE publications_fts MATCH ? 701 + \\WHERE publications_fts MATCH ? AND (? = '' OR p.did = ?) 682 702 \\ORDER BY rank LIMIT 10 683 - , .{fts_query}); 703 + , .{ fts_query, author_val, author_val }); 684 704 defer pub_rows.deinit(); 685 705 686 706 {