Skip to content

Commit 35ddfc2

Browse files
authored
Added fuzzy search on account name (#1445)
ref https://linear.app/ghost/issue/BER-3056 Added fuzzy search on account name
1 parent ad4f3d7 commit 35ddfc2

File tree

4 files changed

+632
-10
lines changed

4 files changed

+632
-10
lines changed

src/configuration/registrations.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import { SearchController } from '@/http/api/search.controller';
6363
import { SiteController } from '@/http/api/site.controller';
6464
import { AccountFollowsView } from '@/http/api/views/account.follows.view';
6565
import { AccountPostsView } from '@/http/api/views/account.posts.view';
66+
import { AccountSearchView } from '@/http/api/views/account.search.view';
6667
import { AccountView } from '@/http/api/views/account.view';
6768
import { BlocksView } from '@/http/api/views/blocks.view';
6869
import { ExploreView } from '@/http/api/views/explore.view';
@@ -358,6 +359,10 @@ export function registerDependencies(
358359
'accountPostsView',
359360
asClass(AccountPostsView).singleton(),
360361
);
362+
container.register(
363+
'accountSearchView',
364+
asClass(AccountSearchView).singleton(),
365+
);
361366
container.register('blocksView', asClass(BlocksView).singleton());
362367
container.register('replyChainView', asClass(ReplyChainView).singleton());
363368

src/http/api/search.controller.ts

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@ import type { AppContext } from '@/app';
44
import { isHandle } from '@/helpers/activitypub/actor';
55
import { isUri } from '@/helpers/uri';
66
import type { AccountDTO } from '@/http/api/types';
7+
import type { AccountSearchView } from '@/http/api/views/account.search.view';
78
import type { AccountView } from '@/http/api/views/account.view';
89
import { APIRoute, RequireRoles } from '@/http/decorators/route.decorator';
910
import { GhostRole } from '@/http/middleware/role-guard';
1011

11-
type AccountSearchResult = Pick<
12+
export type AccountSearchResult = Pick<
1213
AccountDTO,
1314
| 'id'
1415
| 'name'
@@ -38,7 +39,10 @@ interface SearchResults {
3839
}
3940

4041
export class SearchController {
41-
constructor(private readonly accountView: AccountView) {}
42+
constructor(
43+
private readonly accountView: AccountView,
44+
private readonly accountSearchView: AccountSearchView,
45+
) {}
4246

4347
/**
4448
* Handle a search request
@@ -53,30 +57,60 @@ export class SearchController {
5357
const queryQuery = ctx.req.query('query');
5458
const query = queryQuery ? decodeURIComponent(queryQuery) : '';
5559

56-
// Init search results - At the moment we only support searching for an actor (account)
60+
// Init search results
5761
const results: SearchResults = {
5862
accounts: [],
5963
};
6064

65+
const account = ctx.get('account');
6166
const requestUserContext = {
62-
requestUserAccount: ctx.get('account'),
67+
requestUserAccount: account,
6368
};
6469

65-
let dto: AccountDTO | null = null;
66-
70+
// Account handle search (exact match, single result)
6771
if (isHandle(query)) {
68-
dto = await this.accountView.viewByHandle(
72+
const dto = await this.accountView.viewByHandle(
6973
query,
7074
requestUserContext,
7175
);
76+
77+
if (dto !== null) {
78+
results.accounts.push(toSearchResult(dto));
79+
}
80+
81+
return new Response(JSON.stringify(results), {
82+
headers: {
83+
'Content-Type': 'application/json',
84+
},
85+
status: 200,
86+
});
7287
}
7388

89+
// Account URI search (exact match, single result)
7490
if (isUri(query)) {
75-
dto = await this.accountView.viewByApId(query, requestUserContext);
91+
const dto = await this.accountView.viewByApId(
92+
query,
93+
requestUserContext,
94+
);
95+
96+
if (dto !== null) {
97+
results.accounts.push(toSearchResult(dto));
98+
}
99+
100+
return new Response(JSON.stringify(results), {
101+
headers: {
102+
'Content-Type': 'application/json',
103+
},
104+
status: 200,
105+
});
76106
}
77107

78-
if (dto !== null) {
79-
results.accounts.push(toSearchResult(dto));
108+
// Account name search (partial match, multiple results)
109+
if (query.trim().length >= 2) {
110+
results.accounts = await this.accountSearchView.searchByName(
111+
query,
112+
account.id,
113+
);
80114
}
81115

82116
return new Response(JSON.stringify(results), {

0 commit comments

Comments
 (0)