@@ -105,8 +105,29 @@ export const SearchContext = createContext<SearchContextData>(
105
105
* SearchProvider component
106
106
*/
107
107
class SearchProvider extends Component < SearchProviderProps , SearchContextData > {
108
+ private entryScape : EntryScape ;
109
+ private getCacheKeys ( request : SearchRequest ) {
110
+ const cacheKeyBase = `${ request . language || "" } _${
111
+ request . esRdfTypes ?. [ 0 ] || ""
112
+ } `;
113
+ return {
114
+ data : `${ cacheKeyBase } _facets-cache` ,
115
+ timestamp : `${ cacheKeyBase } _facets-cache-ts` ,
116
+ } ;
117
+ }
118
+
108
119
constructor ( props : SearchProviderProps ) {
109
120
super ( props ) ;
121
+ const { t, lang } = props . i18n ;
122
+
123
+ this . entryScape = new EntryScape (
124
+ props . entryscapeUrl || "https://admin.dataportal.se/store" ,
125
+ lang ,
126
+ t ,
127
+ props . facetSpecification ,
128
+ props . hitSpecifications ,
129
+ ) ;
130
+
110
131
this . state = {
111
132
...defaultSearchSettings ,
112
133
request : {
@@ -115,6 +136,20 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
115
136
} ;
116
137
}
117
138
139
+ // Update EntryScape instance when language changes
140
+ componentDidUpdate ( prevProps : SearchProviderProps ) {
141
+ if ( prevProps . i18n . lang !== this . props . i18n . lang ) {
142
+ const { t, lang } = this . props . i18n ;
143
+ this . entryScape = new EntryScape (
144
+ this . props . entryscapeUrl || "https://admin.dataportal.se/store" ,
145
+ lang ,
146
+ t ,
147
+ this . props . facetSpecification ,
148
+ this . props . hitSpecifications ,
149
+ ) ;
150
+ }
151
+ }
152
+
118
153
/**
119
154
* On component mount
120
155
*
@@ -173,7 +208,6 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
173
208
174
209
if ( dcatmeta && dcatmeta . templates && dcatmeta . templates . length > 0 ) {
175
210
this . setState ( {
176
- ...this . state ,
177
211
dcatmeta : dcatmeta ,
178
212
} ) ;
179
213
}
@@ -184,21 +218,11 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
184
218
* This is for accurate facets count
185
219
*/
186
220
updateFacetStatsGrouped = async ( ) : Promise < void > => {
187
- const { t, lang } = this . props . i18n ;
188
-
189
221
// Only continue if we have allFacets and a SearchResult
190
222
if ( ! this . state . allFacets || ! this . state . result ?. facets ) {
191
223
return ;
192
224
}
193
225
194
- const entryScape = new EntryScape (
195
- this . props . entryscapeUrl || "https://admin.dataportal.se/store" ,
196
- lang ,
197
- t ,
198
- this . props . facetSpecification ,
199
- this . props . hitSpecifications ,
200
- ) ;
201
-
202
226
const facetValues = this . state . request . facetValues as SearchFacetValue [ ] ;
203
227
if ( ! facetValues ?. length ) {
204
228
return ;
@@ -223,7 +247,7 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
223
247
const searchPromises = Object . keys ( groupedFacets ) . map ( async ( group ) => {
224
248
const facetsNotInGroup = facetValues . filter ( ( f ) => f . facet !== group ) ;
225
249
226
- const res = await entryScape . solrSearch ( {
250
+ const res = await this . entryScape . solrSearch ( {
227
251
...this . state . request ,
228
252
takeFacets : 100 ,
229
253
fetchFacets : true ,
@@ -441,85 +465,74 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
441
465
* Will cache and fetch from localStorage, cache expires in 5 mins
442
466
*/
443
467
fetchAllFacets = async ( ) : Promise < void > => {
444
- const { t, lang } = this . props . i18n ;
445
- const store_cache_key = `${ this . state . request . language || "" } _${
446
- this . state . request . esRdfTypes
447
- ? this . state . request . esRdfTypes [ 0 ] . toString ( )
448
- : ""
449
- } _facets-cache`;
450
- const store_cache_key_stamp = `${ this . state . request . language || "" } _${
451
- this . state . request . esRdfTypes
452
- ? this . state . request . esRdfTypes [ 0 ] . toString ( )
453
- : ""
454
- } _facets-cache-ts`;
468
+ const { request, dcatmeta } = this . state ;
469
+ const CACHE_KEYS = this . getCacheKeys ( request ) ;
470
+
471
+ const CACHE_EXPIRY_MINS = 5 ;
455
472
456
473
this . setState ( { loadingFacets : true } ) ;
457
474
458
- // Check cache
459
- if ( hasLocalStore && hasWindow ) {
460
- const ls_AllFacets = localStorage . getItem ( store_cache_key ) ;
461
- const ls_Stamp = localStorage . getItem ( store_cache_key_stamp ) ;
475
+ try {
476
+ // Check cache if browser environment
477
+ if ( hasWindow && hasLocalStore ) {
478
+ const cachedData = localStorage . getItem ( CACHE_KEYS . data ) ;
479
+ const cachedTimestamp = localStorage . getItem ( CACHE_KEYS . timestamp ) ;
480
+
481
+ if ( cachedData && cachedTimestamp ) {
482
+ const allFacets = JSON . parse ( cachedData ) as {
483
+ [ facet : string ] : SearchFacet ;
484
+ } ;
485
+ const cacheAge =
486
+ ( Date . now ( ) - new Date ( JSON . parse ( cachedTimestamp ) ) . getTime ( ) ) /
487
+ 60000 ;
488
+
489
+ if ( cacheAge <= CACHE_EXPIRY_MINS ) {
490
+ this . setState ( { allFacets, loadingFacets : false } ) ;
491
+ return ;
492
+ }
462
493
463
- if ( ls_AllFacets && ls_Stamp ) {
464
- const allFacets = JSON . parse ( ls_AllFacets ) as {
465
- [ facet : string ] : SearchFacet ;
466
- } ;
467
- const stampAllFacets = new Date ( JSON . parse ( ls_Stamp ) ) ;
468
- const diff = ( new Date ( ) . getTime ( ) - stampAllFacets . getTime ( ) ) / 60000 ;
469
-
470
- if ( diff > 5 ) {
471
- // Cache expired, remove it
472
- localStorage . removeItem ( store_cache_key ) ;
473
- localStorage . removeItem ( store_cache_key_stamp ) ;
474
- } else {
475
- // Use cached data
476
- this . setState ( { allFacets, loadingFacets : false } ) ;
477
- return ;
494
+ // Clear expired cache
495
+ localStorage . removeItem ( CACHE_KEYS . data ) ;
496
+ localStorage . removeItem ( CACHE_KEYS . timestamp ) ;
478
497
}
479
498
}
480
- }
481
499
482
- try {
483
- const entryScape = new EntryScape (
484
- this . props . entryscapeUrl || "https://admin.dataportal.se/store" ,
485
- lang ,
486
- t ,
487
- this . props . facetSpecification ,
488
- this . props . hitSpecifications ,
489
- ) ;
490
-
491
- const res = await entryScape . solrSearch ( {
500
+ const searchResult = await this . entryScape . solrSearch ( {
492
501
query : "*" ,
493
502
fetchFacets : true ,
494
503
take : 1 ,
495
- takeFacets : this . state . request . takeFacets || 30 ,
504
+ takeFacets : request . takeFacets || 30 ,
496
505
} ) ;
497
506
498
- if ( ! res . esFacets || ! this . state . dcatmeta ) return ;
507
+ if ( ! searchResult . esFacets || ! dcatmeta ) {
508
+ throw new Error ( "Missing required facets or DCAT metadata" ) ;
509
+ }
499
510
500
- const facets = await entryScape . getFacets (
501
- res . esFacets ,
502
- this . state . dcatmeta ,
511
+ const facets = await this . entryScape . getFacets (
512
+ searchResult . esFacets ,
513
+ dcatmeta ,
503
514
) ;
504
515
505
- if ( facets ) {
506
- this . setState ( { allFacets : facets } ) ;
516
+ if ( ! facets ) {
517
+ throw new Error ( "Failed to process facets" ) ;
518
+ }
519
+
520
+ this . setState ( { allFacets : facets } ) ;
507
521
508
- if ( hasLocalStore && hasWindow ) {
509
- localStorage . setItem ( store_cache_key , JSON . stringify ( facets ) ) ;
522
+ // Cache results if browser environment
523
+ if ( hasWindow && hasLocalStore ) {
524
+ try {
525
+ localStorage . setItem ( CACHE_KEYS . data , JSON . stringify ( facets ) ) ;
510
526
localStorage . setItem (
511
- store_cache_key_stamp ,
527
+ CACHE_KEYS . timestamp ,
512
528
JSON . stringify ( new Date ( ) ) ,
513
529
) ;
530
+ } catch ( cacheError ) {
531
+ console . warn ( "Failed to cache facets:" , cacheError ) ;
514
532
}
515
- } else {
516
- this . setState ( {
517
- request : this . state . request ,
518
- loadingFacets : false ,
519
- } ) ;
520
533
}
521
534
} catch ( error ) {
522
- console . error ( "Failed to fetch all facets:" , error ) ;
535
+ console . error ( "Failed to fetch facets:" , error ) ;
523
536
} finally {
524
537
this . setState ( { loadingFacets : false } ) ;
525
538
}
@@ -533,32 +546,25 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
533
546
*
534
547
*/
535
548
searchInFacets = async ( query : string , facetkey : string ) : Promise < void > => {
536
- const { t, lang } = this . props . i18n ;
537
- const store_cache_key = `${ this . state . request . language || "" } _${
538
- this . state . request . esRdfTypes
539
- ? this . state . request . esRdfTypes [ 0 ] . toString ( )
540
- : ""
541
- } _facets-cache`;
542
- const store_cache_key_stamp = `${ store_cache_key } -ts` ;
549
+ const { request } = this . state ;
550
+ const CACHE_KEYS = this . getCacheKeys ( request ) ;
543
551
544
552
const facets = { ...this . state . allFacets } ;
545
553
546
- const entryScape = new EntryScape (
547
- this . props . entryscapeUrl || "https://admin.dataportal.se/store" ,
548
- lang ,
549
- t ,
550
- undefined , // Optional configuration
551
- {
552
- "http://xmlns.com/foaf/0.1/Agent" : {
553
- path : `` ,
554
- titleResource : "http://xmlns.com/foaf/0.1/name" ,
555
- descriptionResource : "" ,
556
- } ,
554
+ // Store original specifications
555
+ const originalSpecs = { ...this . entryScape . hitSpecifications } ;
556
+
557
+ // Update specifications for this search
558
+ this . entryScape . hitSpecifications = {
559
+ "http://xmlns.com/foaf/0.1/Agent" : {
560
+ path : `` ,
561
+ titleResource : "http://xmlns.com/foaf/0.1/name" ,
562
+ descriptionResource : "" ,
557
563
} ,
558
- ) ;
564
+ } ;
559
565
560
566
try {
561
- const res = await entryScape . solrSearch ( {
567
+ const res = await this . entryScape . solrSearch ( {
562
568
titleQuery : query && query . length > 0 ? query : "*" ,
563
569
fetchFacets : false ,
564
570
take : 100 ,
@@ -595,9 +601,9 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
595
601
this . setState ( { allFacets : facets } ) ;
596
602
597
603
if ( hasLocalStore && hasWindow ) {
598
- window . localStorage . setItem ( store_cache_key , JSON . stringify ( facets ) ) ;
604
+ window . localStorage . setItem ( CACHE_KEYS . data , JSON . stringify ( facets ) ) ;
599
605
window . localStorage . setItem (
600
- store_cache_key_stamp ,
606
+ CACHE_KEYS . timestamp ,
601
607
JSON . stringify ( new Date ( ) ) ,
602
608
) ;
603
609
}
@@ -607,6 +613,8 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
607
613
console . error ( "Error searching in facets:" , error ) ;
608
614
} finally {
609
615
this . setState ( { loadingFacets : false } ) ;
616
+ // Restore original specifications
617
+ this . entryScape . hitSpecifications = originalSpecs ;
610
618
}
611
619
} ;
612
620
@@ -849,31 +857,21 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
849
857
setStateToLocation : Boolean = true ,
850
858
reSortOnDone : Boolean = true ,
851
859
) : Promise < void > => {
852
- const { t, lang } = this . props . i18n ;
853
-
854
860
this . setState ( { loadingHits : true } ) ;
855
861
856
862
if ( setStateToLocation ) {
857
863
this . setStateToLocation ( ) ;
858
864
}
859
865
860
866
try {
861
- const entryScape = new EntryScape (
862
- this . props . entryscapeUrl || "https://admin.dataportal.se/store" ,
863
- lang ,
864
- t ,
865
- this . props . facetSpecification ,
866
- this . props . hitSpecifications ,
867
- ) ;
868
-
869
867
// Separate search request without facets
870
868
const searchRequest = {
871
869
...this . state . request ,
872
870
fetchFacets : false , // Disable facet fetching for initial search
873
871
} ;
874
872
875
873
// Execute search immediately
876
- const searchResults = await entryScape . solrSearch (
874
+ const searchResults = await this . entryScape . solrSearch (
877
875
searchRequest ,
878
876
this . state . dcatmeta ,
879
877
) ;
@@ -885,7 +883,7 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
885
883
}
886
884
887
885
// Update UI with search results immediately
888
- await this . setState ( {
886
+ this . setState ( {
889
887
loadingHits : false ,
890
888
result : {
891
889
...this . state . result ,
@@ -908,24 +906,26 @@ class SearchProvider extends Component<SearchProviderProps, SearchContextData> {
908
906
take : 1 , // Minimize result set since we only need facets
909
907
} ;
910
908
911
- const facetResults = await entryScape . solrSearch (
909
+ const facetResults = await this . entryScape . solrSearch (
912
910
facetRequest ,
913
911
this . state . dcatmeta ,
914
912
) ;
915
913
916
914
if ( facetResults . esFacets ) {
917
- const facets = await entryScape . getFacets (
915
+ const facets = await this . entryScape . getFacets (
918
916
facetResults . esFacets ,
919
917
this . state . dcatmeta ! ,
920
918
) ;
921
919
922
- await this . setState ( {
920
+ this . setState ( {
923
921
loadingFacets : false ,
924
922
allFacets : facets ,
925
923
} ) ;
926
924
925
+ await this . mergeAllFacetsAndResult ( ) ;
926
+ await this . updateFacetStatsGrouped ( ) ;
927
+
927
928
if ( reSortOnDone ) {
928
- await this . mergeAllFacetsAndResult ( ) ;
929
929
this . sortAllFacets ( ) ;
930
930
}
931
931
}
0 commit comments