@@ -85,7 +85,6 @@ export class EntityExtractor {
8585 }
8686
8787 try {
88- const entities : ExtractedEntity [ ] = [ ] ;
8988 const entityMap = new Map < string , ExtractedEntity > ( ) ;
9089
9190 // Use compromise for entity extraction
@@ -190,7 +189,8 @@ export class EntityExtractor {
190189 entityMap . set ( key , {
191190 type : 'Concept' ,
192191 name,
193- confidence : 0.5 , // Lower confidence for general concepts
192+ // Give higher confidence to important concepts
193+ confidence : 0.5 ,
194194 mentions : 1 ,
195195 } ) ;
196196 }
@@ -199,13 +199,15 @@ export class EntityExtractor {
199199 }
200200
201201 // Convert map to array and sort by confidence and mentions
202- return Array . from ( entityMap . values ( ) ) . sort ( ( a , b ) => {
202+ const result = Array . from ( entityMap . values ( ) ) . sort ( ( a , b ) => {
203203 // Sort by confidence first, then by mentions
204204 if ( b . confidence !== a . confidence ) {
205205 return b . confidence - a . confidence ;
206206 }
207207 return b . mentions - a . mentions ;
208208 } ) ;
209+
210+ return result ;
209211 } catch ( error ) {
210212 console . error ( 'Error extracting entities:' , error ) ;
211213 return [ ] ;
@@ -220,6 +222,11 @@ export class EntityExtractor {
220222 private extractTechnologyTerms ( text : string ) : string [ ] {
221223 const techTerms : string [ ] = [ ] ;
222224
225+ // Helper function to escape special regex characters
226+ const escapeRegExp = ( string : string ) : string => {
227+ return string . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
228+ } ;
229+
223230 // Common technology keywords
224231 const techKeywords = [
225232 'api' ,
@@ -323,50 +330,67 @@ export class EntityExtractor {
323330
324331 // Check for tech names (specific technologies)
325332 for ( const tech of techNames ) {
326- const regex = new RegExp ( `\\b${ tech } \\b` , 'gi' ) ;
327- if ( regex . test ( text ) ) {
328- techTerms . push ( tech ) ;
333+ // Escape special regex characters
334+ const escapedTech = escapeRegExp ( tech ) ;
335+ try {
336+ const regex = new RegExp ( `\\b${ escapedTech } \\b` , 'gi' ) ;
337+ if ( regex . test ( text ) ) {
338+ techTerms . push ( tech ) ;
339+ }
340+ } catch ( error ) {
341+ console . error (
342+ `Error creating regex for technology term "${ tech } ":` ,
343+ error
344+ ) ;
345+ // Continue with other terms even if one fails
329346 }
330347 }
331348
332349 // Extract phrases containing tech keywords
333350 const sentences = text . split ( / [ . ! ? ] + / ) ;
334351 for ( const sentence of sentences ) {
335352 for ( const keyword of techKeywords ) {
336- const regex = new RegExp ( `\\b${ keyword } \\b` , 'i' ) ;
337- if ( regex . test ( sentence ) ) {
338- // Extract noun phrases around the keyword
339- const words = sentence . split ( / \s + / ) ;
340- const keywordIndex = words . findIndex ( ( word ) =>
341- word . toLowerCase ( ) . includes ( keyword )
342- ) ;
343-
344- if ( keywordIndex >= 0 ) {
345- // Try to extract a meaningful phrase (up to 3 words)
346- let phrase = words [ keywordIndex ] ;
347-
348- // Add preceding word if it looks like an adjective or proper noun
349- if (
350- keywordIndex > 0 &&
351- ! words [ keywordIndex - 1 ] . match (
352- / ^ ( t h e | a | a n | t h i s | t h a t | t h e s e | t h o s e | m y | y o u r | o u r | t h e i r ) $ / i
353- )
354- ) {
355- phrase = words [ keywordIndex - 1 ] + ' ' + phrase ;
353+ try {
354+ // Escape special regex characters
355+ const escapedKeyword = escapeRegExp ( keyword ) ;
356+ const regex = new RegExp ( `\\b${ escapedKeyword } \\b` , 'i' ) ;
357+ if ( regex . test ( sentence ) ) {
358+ // Extract noun phrases around the keyword
359+ const words = sentence . split ( / \s + / ) ;
360+ const keywordIndex = words . findIndex ( ( word ) =>
361+ word . toLowerCase ( ) . includes ( keyword )
362+ ) ;
363+
364+ if ( keywordIndex >= 0 ) {
365+ // Try to extract a meaningful phrase (up to 3 words)
366+ let phrase = words [ keywordIndex ] ;
367+
368+ // Add preceding word if it looks like an adjective or proper noun
369+ if (
370+ keywordIndex > 0 &&
371+ ! words [ keywordIndex - 1 ] . match (
372+ / ^ ( t h e | a | a n | t h i s | t h a t | t h e s e | t h o s e | m y | y o u r | o u r | t h e i r ) $ / i
373+ )
374+ ) {
375+ phrase = words [ keywordIndex - 1 ] + ' ' + phrase ;
376+ }
377+
378+ // Add following word if it looks like it could be part of the phrase
379+ if (
380+ keywordIndex < words . length - 1 &&
381+ ! words [ keywordIndex + 1 ] . match (
382+ / ^ ( i s | a r e | w a s | w e r e | w i l l | w o u l d | c o u l d | s h o u l d | a n d | o r | b u t ) $ / i
383+ )
384+ ) {
385+ phrase = phrase + ' ' + words [ keywordIndex + 1 ] ;
386+ }
387+
388+ techTerms . push ( phrase . trim ( ) ) ;
356389 }
357-
358- // Add following word if it looks like it could be part of the phrase
359- if (
360- keywordIndex < words . length - 1 &&
361- ! words [ keywordIndex + 1 ] . match (
362- / ^ ( i s | a r e | w a s | w e r e | w i l l | w o u l d | c o u l d | s h o u l d | a n d | o r | b u t ) $ / i
363- )
364- ) {
365- phrase = phrase + ' ' + words [ keywordIndex + 1 ] ;
366- }
367-
368- techTerms . push ( phrase . trim ( ) ) ;
369390 }
391+ } catch ( error ) {
392+ console . error ( `Error processing keyword "${ keyword } ":` , error ) ;
393+ // Continue with other keywords even if one fails
370394 }
371395 }
372396 }
0 commit comments