@@ -4,26 +4,31 @@ import { cwd } from "node:process";
44import { htmlToText } from "html-to-text" ;
55
66function smartSearchPlugin ( { endpoint, accessToken } ) {
7+ let isPluginExecuted = false ;
8+
79 return {
810 apply : ( compiler ) => {
911 compiler . hooks . done . tapPromise ( "SmartSearchPlugin" , async ( ) => {
12+ if ( isPluginExecuted ) {
13+ return ;
14+ }
15+
16+ isPluginExecuted = true ;
17+
18+ if ( compiler . options . mode !== "production" ) {
19+ console . log ( "Skipping indexing in non-production mode." ) ;
20+ return ;
21+ }
22+
1023 try {
1124 const pages = await collectPages ( path . join ( cwd ( ) , "src/pages/docs" ) ) ;
1225
13- pages . push ( {
14- id : "test-document" ,
15- data : {
16- title : "Test Document" ,
17- content : "This is a test document for indexing." ,
18- path : "/test-path" ,
19- } ,
20- } ) ;
21-
2226 console . log ( "Docs Pages collected for indexing:" , pages . length ) ;
2327
28+ await deleteExistingDocs ( endpoint , accessToken ) ;
2429 await sendPagesToEndpoint ( pages , endpoint , accessToken ) ;
2530 } catch ( error ) {
26- console . error ( "Error sending pages :" , error ) ;
31+ console . error ( "Error in smartSearchPlugin :" , error ) ;
2732 }
2833 } ) ;
2934 } ,
@@ -32,45 +37,54 @@ function smartSearchPlugin({ endpoint, accessToken }) {
3237
3338async function collectPages ( directory ) {
3439 const pages = [ ] ;
35- const files = await fs . readdir ( directory ) ;
40+ const entries = await fs . readdir ( directory , { withFileTypes : true } ) ;
3641
37- for ( const file of files ) {
38- const filePath = path . join ( directory , file ) ;
39- const stat = await fs . stat ( filePath ) ;
42+ for ( const entry of entries ) {
43+ const entryPath = path . join ( directory , entry . name ) ;
4044
41- if ( stat . isDirectory ( ) ) {
42- const subPages = await collectPages ( filePath ) ;
45+ if ( entry . isDirectory ( ) ) {
46+ const subPages = await collectPages ( entryPath ) ;
4347 pages . push ( ...subPages ) ;
44- } else if ( file . endsWith ( ".mdx" ) ) {
45- const content = await fs . readFile ( filePath , "utf8" ) ;
48+ } else if ( entry . isFile ( ) && entry . name . endsWith ( ".mdx" ) ) {
49+ const content = await fs . readFile ( entryPath , "utf8" ) ;
4650
47- // Safely extract metadata using regex
4851 const metadataMatch = content . match (
49- / e x p o r t c o n s t m e t a d a t a = (?< metadata > { [ \S \s ] + ?} ) ; / ,
52+ / e x p o r t \s + c o n s t \s + m e t a d a t a \s * = \s * (?< metadata > { [ \S \s ] * ?} ) ; / ,
5053 ) ;
54+
5155 let metadata = { } ;
5256
53- if ( metadataMatch ) {
57+ if (
58+ metadataMatch &&
59+ metadataMatch . groups &&
60+ metadataMatch . groups . metadata
61+ ) {
5462 try {
55- // eslint-disable-next-line no-eval
56- metadata = eval ( `(${ metadataMatch . groups . metadata } )` ) ; // Parse the metadata block
63+ metadata = eval ( `(${ metadataMatch . groups . metadata } )` ) ;
5764 } catch ( error ) {
5865 console . error ( "Error parsing metadata:" , error ) ;
66+ continue ;
5967 }
68+ } else {
69+ console . warn ( `No metadata found in ${ entryPath } . Skipping.` ) ;
70+ continue ;
6071 }
6172
6273 const textContent = htmlToText ( content ) ;
63- const id = filePath
64- . replace ( cwd ( ) , "" )
65- . replaceAll ( "/" , "-" )
66- . replace ( ".mdx" , "" ) ;
74+
75+ const cleanedPath = cleanPath ( entryPath ) ;
76+
77+ const id = `mdx:${ cleanedPath } ` ;
78+
79+ console . log ( `Indexing document with ID: ${ id } , path: ${ cleanedPath } ` ) ;
6780
6881 pages . push ( {
6982 id,
7083 data : {
71- title : metadata . title || undefined , // No fallback to "Untitled Document"
84+ title : metadata . title ,
7285 content : textContent ,
73- path : filePath . replace ( cwd ( ) , "" ) ,
86+ path : cleanedPath ,
87+ content_type : "mdx_doc" ,
7488 } ,
7589 } ) ;
7690 }
@@ -79,13 +93,61 @@ async function collectPages(directory) {
7993 return pages ;
8094}
8195
82- const query = `
83- mutation CreateIndexDocument($input: DocumentInput!) {
84- index(input: $input) {
85- success
96+ function cleanPath ( filePath ) {
97+ const relativePath = path . relative ( cwd ( ) , filePath ) ;
98+ return (
99+ "/" +
100+ relativePath
101+ . replace ( / ^ s r c \/ p a g e s \/ / , "" )
102+ . replace ( / ^ p a g e s \/ / , "" )
103+ . replace ( / \/ i n d e x \. m d x $ / , "" )
104+ . replace ( / \. m d x $ / , "" )
105+ ) ;
106+ }
107+
108+ async function deleteExistingDocs ( endpoint , accessToken ) {
109+ const variables = {
110+ filter : {
111+ content_type : "mdx_doc" ,
112+ } ,
113+ } ;
114+
115+ const deleteQuery = `
116+ mutation DeleteDocs($filter: DocumentFilterInput) {
117+ deleteMany(filter: $filter) {
118+ code
119+ message
120+ success
121+ }
122+ }
123+ ` ;
124+
125+ try {
126+ const response = await fetch ( endpoint , {
127+ method : "POST" ,
128+ headers : {
129+ "Content-Type" : "application/json" ,
130+ Authorization : `Bearer ${ accessToken } ` ,
131+ } ,
132+ body : JSON . stringify ( { query : deleteQuery , variables } ) ,
133+ } ) ;
134+
135+ const result = await response . json ( ) ;
136+ if ( result . errors ) {
137+ console . error ( "GraphQL deletion error:" , result . errors ) ;
138+ } else {
139+ console . log ( "Existing MDX documents deleted:" , result . data . deleteMany ) ;
140+ }
141+ } catch ( error ) {
142+ console . error ( "Error deleting existing documents:" , error ) ;
143+ }
144+ }
145+
146+ const bulkIndexQuery = `
147+ mutation BulkIndex($documents: [DocumentInput!]!) {
148+ bulkIndex(input: { documents: $documents }) {
86149 code
87- message
88- document {
150+ documents {
89151 id
90152 data
91153 }
@@ -99,40 +161,39 @@ async function sendPagesToEndpoint(pages, endpoint, accessToken) {
99161 return ;
100162 }
101163
102- for ( const page of pages ) {
103- const documentId = `mdx:${ page . id } ` ;
104- const variables = {
105- input : {
106- id : documentId ,
107- data : {
108- content : page . data . content ,
109- path : page . data . path ,
110- title : page . data . title || undefined , // No fallback to "Untitled Document"
111- } ,
164+ const documents = pages . map ( ( page ) => ( {
165+ id : page . id ,
166+ data : page . data ,
167+ } ) ) ;
168+
169+ const variables = { documents } ;
170+
171+ try {
172+ const response = await fetch ( endpoint , {
173+ method : "POST" ,
174+ headers : {
175+ "Content-Type" : "application/json" ,
176+ Authorization : `Bearer ${ accessToken } ` ,
112177 } ,
113- } ;
114-
115- try {
116- const response = await fetch ( endpoint , {
117- method : "POST" ,
118- headers : {
119- "Content-Type" : "application/json" ,
120- Authorization : `Bearer ${ accessToken } ` ,
121- } ,
122- body : JSON . stringify ( { query, variables } ) ,
123- } ) ;
178+ body : JSON . stringify ( { query : bulkIndexQuery , variables } ) ,
179+ } ) ;
124180
125- const result = await response . json ( ) ;
126- if ( result . errors ) {
127- console . error ( "GraphQL indexing error:" , result . errors ) ;
128- }
129- } catch ( error ) {
181+ if ( ! response . ok ) {
130182 console . error (
131- "Error indexing document:" ,
132- page . data . title || "No title" ,
133- error ,
183+ `Error during bulk indexing: ${ response . status } ${ response . statusText } ` ,
134184 ) ;
185+ return ;
186+ }
187+
188+ const result = await response . json ( ) ;
189+
190+ if ( result . errors ) {
191+ console . error ( "GraphQL bulk indexing error:" , result . errors ) ;
192+ } else {
193+ console . log ( `Indexed ${ documents . length } documents successfully.` ) ;
135194 }
195+ } catch ( error ) {
196+ console . error ( "Error during bulk indexing:" , error ) ;
136197 }
137198}
138199
0 commit comments