Skip to content
Closed
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 @@ -988,6 +988,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
3 changes: 2 additions & 1 deletion apps/server/src/orchestration-v2/ThreadSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
import { threadSearchPullRequests } from "@t3tools/shared/threadPullRequests";
import {
indexedMessageCondition,
foldedMessageSearchText,
messageSearchText,
nonPlaceholderMessageCondition,
} from "./messageSearchIndex.ts";
Expand Down Expand Up @@ -245,7 +246,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(${messageSearchText(sql)}), token.value) = 0
WHERE instr(lower(${foldedMessageSearchText(sql)}), token.value) = 0
)
AND NOT EXISTS (
SELECT 1 FROM json_each(${shortSpellings}) AS token
Expand Down
4 changes: 4 additions & 0 deletions apps/server/src/orchestration-v2/messageSearchIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export const readableSearchText = (text: string) => {
export const messageSearchText = (sql: SqlClient.SqlClient) =>
sql.literal(readableText("messages"));

/** A message's readable text with apostrophes folded, as the index stores it. */
export const foldedMessageSearchText = (sql: SqlClient.SqlClient) =>
sql.literal(foldApostrophes(readableText("messages")));

/**
* A message's stored text with apostrophes folded, what the index held before
* migration 81. Migration 69's backfill uses it. `payload` is the message's
Expand Down
Loading