Skip to content

Commit bbaab5e

Browse files
committed
feat(website): implement search functionality for documentation and blog with backend integration
1 parent 4de3911 commit bbaab5e

File tree

19 files changed

+285
-417
lines changed

19 files changed

+285
-417
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ResponseWithInformation } from '@middlewares/sessionAuth.middleware';
2+
import * as askDocQuestionUtil from '@utils/AI/askDocQuestion/askDocQuestion';
3+
import { formatResponse, type ResponseData } from '@utils/responseData';
4+
import type { Request } from 'express';
5+
6+
export type SearchDocUtilParams = {
7+
input: string;
8+
};
9+
export type SearchDocUtilResult = ResponseData<string[]>;
10+
11+
export const searchDocUtil = async (
12+
req: Request<unknown, unknown, unknown, SearchDocUtilParams>,
13+
res: ResponseWithInformation<SearchDocUtilResult>
14+
) => {
15+
const { input } = req.query;
16+
17+
const response = await askDocQuestionUtil.searchChunkReference(
18+
input,
19+
30,
20+
0.2
21+
);
22+
const docFileList = response.map((doc) => doc.fileKey);
23+
24+
const uniqueDocFileList = Array.from(new Set(docFileList));
25+
26+
const responseData = formatResponse<string[]>({
27+
data: uniqueDocFileList,
28+
});
29+
30+
res.json(responseData);
31+
};

apps/backend/src/export.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export type * from '@controllers/oAuth2.controller';
1313
export type * from '@controllers/organization.controller';
1414
export type * from '@controllers/project.controller';
1515
export type * from '@controllers/projectAccessKey.controller';
16+
export type * from '@controllers/search.controller';
1617
export type * from '@controllers/sessionAuth.controller';
1718
export type * from '@controllers/stripe.controller';
1819
export type * from '@controllers/tag.controller';

apps/backend/src/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,23 @@ import {
1616
import { logAPIRequestURL } from '@middlewares/request.middleware';
1717
import {
1818
type ResponseWithInformation,
19-
checkUser,
19+
checkAdmin,
2020
checkOrganization,
2121
checkProject,
22-
checkAdmin,
22+
checkUser,
2323
} from '@middlewares/sessionAuth.middleware';
2424

2525
// Routes
26+
import { aiRouter } from '@routes/ai.routes';
2627
import { dictionaryRouter } from '@routes/dictionary.routes';
28+
import { eventListenerRouter } from '@routes/eventListener.routes';
2729
import { organizationRouter } from '@routes/organization.routes';
2830
import { projectRouter } from '@routes/project.routes';
29-
import { tagRouter } from '@routes/tags.routes';
31+
import { searchRouter } from '@routes/search.routes';
3032
import { sessionAuthRouter } from '@routes/sessionAuth.routes';
31-
import { userRouter } from '@routes/user.routes';
3233
import { stripeRouter } from '@routes/stripe.routes';
33-
import { aiRouter } from '@routes/ai.routes';
34-
import { eventListenerRouter } from '@routes/eventListener.routes';
34+
import { tagRouter } from '@routes/tags.routes';
35+
import { userRouter } from '@routes/user.routes';
3536

3637
// Webhooks
3738
import { stripeWebhook } from '@webhooks/stripe.webhook';
@@ -187,6 +188,7 @@ app.use('/api/dictionary', dictionaryRouter);
187188
app.use('/api/stripe', stripeRouter);
188189
app.use('/api/ai', aiRouter);
189190
app.use('/api/event-listener', eventListenerRouter);
191+
app.use('/api/search', searchRouter);
190192

191193
// Server
192194
app.listen(process.env.PORT, () => {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { Routes } from '@/types/Routes';
2+
import { searchDocUtil } from '@controllers/search.controller';
3+
import { Router } from 'express';
4+
5+
export const searchRouter: Router = Router();
6+
7+
const baseURL = () => `${process.env.BACKEND_URL}/api/search`;
8+
9+
export const getSearchRoutes = () =>
10+
({
11+
doc: {
12+
urlModel: '/doc',
13+
url: `${baseURL()}/doc`,
14+
method: 'GET',
15+
},
16+
}) satisfies Routes;
17+
18+
searchRouter.get(getSearchRoutes().doc.urlModel, searchDocUtil);

apps/backend/src/utils/AI/askDocQuestion/askDocQuestion.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ indexMarkdownFiles();
212212
* @returns An array of the top matching document chunks' content
213213
*/
214214
export const searchChunkReference = async (
215-
query: string
215+
query: string,
216+
maxResults: number = MAX_RELEVANT_CHUNKS_NB,
217+
minSimilarity: number = MIN_RELEVANT_CHUNKS_SIMILARITY
216218
): Promise<VectorStoreEl[]> => {
217219
// Generate an embedding for the user's query
218220
const queryEmbedding = await generateEmbedding(query);
@@ -223,9 +225,9 @@ export const searchChunkReference = async (
223225
...chunk,
224226
similarity: cosineSimilarity(queryEmbedding, chunk.embedding), // Add similarity score to each doc
225227
}))
226-
.filter((chunk) => chunk.similarity > MIN_RELEVANT_CHUNKS_SIMILARITY) // Filter out documents with low similarity scores
228+
.filter((chunk) => chunk.similarity > minSimilarity) // Filter out documents with low similarity scores
227229
.sort((a, b) => b.similarity - a.similarity) // Sort documents by highest similarity first
228-
.slice(0, MAX_RELEVANT_CHUNKS_NB); // Select the top 6 most similar documents
230+
.slice(0, maxResults); // Select the top 6 most similar documents
229231

230232
// Return the content of the top matching documents
231233
return results;

apps/website/src/app/[locale]/(landing)/blog/search/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { SearchView } from '@components/BlogPage/Search/SearchView';
1+
import { SearchView } from '@components/DocPage/Search/SearchView';
22
import { Container, H1, Loader } from '@intlayer/design-system';
33
import { WebsiteHeader } from '@structuredData/WebsiteHeader';
44
import type { NextPageIntlayer } from 'next-intlayer';

apps/website/src/components/BlogPage/BlogNavList.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
'use client';
22

3-
import { SearchTrigger } from '@components/BlogPage/Search/SearchTrigger';
3+
import { PagesRoutes } from '@/Routes';
4+
import { SearchTrigger } from '@components/DocPage/Search/SearchTrigger';
45
import { Link } from '@components/Link/Link';
56
import {
6-
Container,
77
Accordion,
88
Button,
9-
MaxWidthSmoother,
109
ClickOutsideDiv,
10+
Container,
11+
MaxWidthSmoother,
1112
} from '@intlayer/design-system';
1213
import { useDevice } from '@intlayer/design-system/hooks';
1314
import { cn } from '@utils/cn';
1415
import { ArrowLeftToLine } from 'lucide-react';
1516
import { useIntlayer } from 'next-intlayer';
1617
import { useState, type ComponentProps, type FC } from 'react';
1718
import type { Section } from './types';
18-
import { PagesRoutes } from '@/Routes';
1919

2020
type OptionalLinkProps = ComponentProps<typeof Link>;
2121

apps/website/src/components/BlogPage/Search/SearchTrigger.tsx

Lines changed: 0 additions & 50 deletions
This file was deleted.

apps/website/src/components/BlogPage/Search/SearchView.tsx

Lines changed: 0 additions & 140 deletions
This file was deleted.

apps/website/src/components/BlogPage/Search/searchTrigger.content.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)