11function findRow ( identifier , byId = false ) {
2- const CACHE_EXPIRATION = 300 ; // 5 minutes instead of 10
2+ const CACHE_EXPIRATION = 300 ; // 5 minutes
33 const cache = CacheService . getScriptCache ( ) ;
4- const cacheKey = ( byId ? 'id_' : 'link_' ) + identifier . toLowerCase ( ) . trim ( ) ;
5-
4+ const cacheKey = ( byId ? 'id_' : 'link_' ) + identifier . trim ( ) ;
5+
66 // Check cache
77 const cachedData = cache . get ( cacheKey ) ;
88 if ( cachedData ) {
@@ -19,48 +19,42 @@ function findRow(identifier, byId = false) {
1919 const sheet = spreadsheet . getSheetByName ( 'Form' ) ;
2020 if ( ! sheet ) throw new Error ( 'Form sheet not found' ) ;
2121
22- // Get data more efficiently
2322 const lastRow = sheet . getLastRow ( ) ;
2423 const lastCol = sheet . getLastColumn ( ) ;
2524 if ( lastRow <= 1 ) return null ; // No data rows
26-
27- const data = sheet . getRange ( 1 , 1 , lastRow , lastCol ) . getValues ( ) ;
28- const headers = data [ 0 ] ;
29-
30- // Clean and normalize identifier
31- const cleanIdentifier = identifier . trim ( ) . toLowerCase ( ) ;
25+
26+ const headers = sheet . getRange ( 1 , 1 , 1 , lastCol ) . getValues ( ) [ 0 ] ;
27+ const cleanIdentifier = identifier . trim ( ) ;
3228 const columnName = byId ? 'ID' : 'Link' ;
3329 const columnIndex = headers . findIndex ( h => h . toString ( ) . trim ( ) === columnName ) ;
3430 if ( columnIndex === - 1 ) throw new Error ( `${ columnName } column not found` ) ;
3531
36- // Search for match
37- for ( let i = 1 ; i < data . length ; i ++ ) {
38- const rowValue = String ( data [ i ] [ columnIndex ] ) . trim ( ) . toLowerCase ( ) ;
39- const toCompare = byId ? cleanIdentifier : cleanIdentifier . replace ( / ^ @ / , '' ) ;
40-
41- if ( rowValue === toCompare || ( ! byId && rowValue === `@${ toCompare } ` ) ) {
42- const responseData = { } ;
43- headers . forEach ( ( header , index ) => {
44- // Basic data cleaning
45- responseData [ header ] = data [ i ] [ index ] !== null ?
46- String ( data [ i ] [ index ] ) . trim ( ) : '' ;
47- } ) ;
48-
49- // Validate required fields
50- if ( ! responseData . Name ) {
51- throw new Error ( 'Profile data missing required Name field' ) ;
52- }
53-
54- try {
55- cache . put ( cacheKey , JSON . stringify ( responseData ) , CACHE_EXPIRATION ) ;
56- } catch ( e ) {
57- console . error ( 'Failed to cache data:' , e ) ;
58- }
59-
60- return responseData ;
32+ // Use TextFinder for efficient, case-sensitive search
33+ const colRange = sheet . getRange ( 2 , columnIndex + 1 , lastRow - 1 , 1 ) ; // skip header
34+ const toCompare = byId ? cleanIdentifier : cleanIdentifier . replace ( / ^ @ / , '' ) ;
35+ let found = colRange . createTextFinder ( toCompare ) . matchCase ( true ) . matchEntireCell ( true ) . findNext ( ) ;
36+ // If not found, try with @ prefix (for link only)
37+ if ( ! found && ! byId && ! toCompare . startsWith ( '@' ) ) {
38+ found = colRange . createTextFinder ( '@' + toCompare ) . matchCase ( true ) . matchEntireCell ( true ) . findNext ( ) ;
39+ }
40+ if ( found ) {
41+ const rowIdx = found . getRow ( ) ;
42+ const rowData = sheet . getRange ( rowIdx , 1 , 1 , lastCol ) . getValues ( ) [ 0 ] ;
43+ const responseData = { } ;
44+ headers . forEach ( ( header , index ) => {
45+ responseData [ header ] = rowData [ index ] !== null ? String ( rowData [ index ] ) . trim ( ) : '' ;
46+ } ) ;
47+ // Validate required fields
48+ if ( ! responseData . Name ) {
49+ throw new Error ( 'Profile data missing required Name field' ) ;
50+ }
51+ try {
52+ cache . put ( cacheKey , JSON . stringify ( responseData ) , CACHE_EXPIRATION ) ;
53+ } catch ( e ) {
54+ console . error ( 'Failed to cache data:' , e ) ;
6155 }
56+ return responseData ;
6257 }
63-
6458 return null ;
6559}
6660
0 commit comments