Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions apps/server/src/orchestration-v2/ThreadSearch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,35 @@ it.layer(layerTest)("ThreadSearch across scripts and without the index", (it) =>
);
});

it.layer(layerTest)("ThreadSearch without the index", (it) => {
it.effect("finds a curly apostrophe for a straight one and back", () =>
Effect.gen(function* () {
const projections = yield* ProjectionStore.ProjectionStoreV2;
const search = yield* ThreadSearch.ThreadSearch;
const sql = yield* SqlClient.SqlClient;
const project = ProjectId.make("project:scan-apostrophes");
const curly = ThreadId.make("thread:scan-apostrophe-curly");
const straight = ThreadId.make("thread:scan-apostrophe-straight");
yield* createProject(project);
yield* Effect.forEach(
[
thread(curly, project),
message(curly, "scan-apostrophe-curly", "user", "the gannet’s nest"),
thread(straight, project),
message(straight, "scan-apostrophe-straight", "user", "a kittiwake's nest"),
],
projections.apply,
{ discard: true },
);
yield* sql`DROP TABLE orchestration_v2_message_search`;
const threadIds = (query: string) =>
search.search({ query }).pipe(Effect.map(({ matches }) => matches.map((m) => m.threadId)));
assert.deepEqual(yield* threadIds("gannet's"), [curly]);
assert.deepEqual(yield* threadIds("kittiwake’s"), [straight]);
}),
);
});

it.layer(layerTest)("ThreadSearch.searchOlderSettled", (it) => {
it.effect("returns older settled title matches in scope, then older message hits", () =>
Effect.gen(function* () {
Expand Down
8 changes: 6 additions & 2 deletions apps/server/src/orchestration-v2/ThreadSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ import {
type ThreadSearchSubject,
} from "@t3tools/shared/paletteSearch";
import { threadSearchPullRequests } from "@t3tools/shared/threadPullRequests";
import { indexedMessageCondition, nonPlaceholderMessageCondition } from "./messageSearchIndex.ts";
import {
indexedMessageCondition,
indexedMessageText,
nonPlaceholderMessageCondition,
} from "./messageSearchIndex.ts";
import { loadOlderSettledThreadsPage } from "./ShellStream.ts";

// When the user last wrote in the thread aliased `threads`, as the clients
Expand Down Expand Up @@ -222,7 +226,7 @@ export const messageHitsQuery = (
AND messages.message_id NOT LIKE 'async-answer:%'
AND NOT EXISTS (
SELECT 1 FROM json_each(${substrings}) AS token
WHERE instr(lower(json_extract(messages.payload_json, '$.text')), token.value) = 0
WHERE instr(lower(${indexedMessageText(sql, "messages.payload_json")}), token.value) = 0
)
-- The index's own rowid, so FTS5 returns hits newest first and stops at
-- the limit; ordering by the messages table's rowid sorts every hit.
Expand Down
Loading