22from typing import Any , Dict , List , Optional , Tuple , TypedDict , Union , cast
33
44import structlog
5+ from django .core .cache import cache
56from elasticsearch_dsl import A
67from elasticsearch_dsl import Q as ESQ
78from elasticsearch_dsl import Search
1415from api .views .paginated_elastic_view import PaginatedElasticSearchAPIView
1516from search .documents import UseCaseDocument
1617
18+ METADATA_CACHE_TTL = 60 * 30 # 30 minutes
19+
1720logger = structlog .get_logger (__name__ )
1821
1922
@@ -144,9 +147,7 @@ def __init__(self, **kwargs: Any) -> None:
144147 super ().__init__ (** kwargs )
145148 self .searchable_fields : List [str ]
146149 self .aggregations : Dict [str , str ]
147- self .searchable_fields , self .aggregations = (
148- self .get_searchable_and_aggregations ()
149- )
150+ self .searchable_fields , self .aggregations = self .get_searchable_and_aggregations ()
150151 self .logger = structlog .get_logger (__name__ )
151152
152153 @trace_method (
@@ -155,6 +156,12 @@ def __init__(self, **kwargs: Any) -> None:
155156 )
156157 def get_searchable_and_aggregations (self ) -> Tuple [List [str ], Dict [str , str ]]:
157158 """Get searchable fields and aggregations for the search."""
159+ cached : Optional [Tuple [List [str ], Dict [str , str ]]] = cache .get (
160+ "usecase_search_metadata_config"
161+ )
162+ if cached :
163+ return cached
164+
158165 searchable_fields = [
159166 "title" ,
160167 "summary" ,
@@ -181,7 +188,9 @@ def get_searchable_and_aggregations(self) -> Tuple[List[str], Dict[str, str]]:
181188 for metadata in filterable_metadata :
182189 aggregations [f"metadata.{ metadata .label } " ] = "terms" # type: ignore
183190
184- return searchable_fields , aggregations
191+ result = (searchable_fields , aggregations )
192+ cache .set ("usecase_search_metadata_config" , result , timeout = METADATA_CACHE_TTL )
193+ return result
185194
186195 @trace_method (name = "add_aggregations" , attributes = {"component" : "search_usecase" })
187196 def add_aggregations (self , search : Search ) -> Search :
@@ -199,18 +208,21 @@ def add_aggregations(self, search: Search) -> Search:
199208 )
200209
201210 if aggregate_fields :
202- metadata_qs = Metadata .objects .filter (filterable = True )
203- filterable_metadata = [str (meta .label ) for meta in metadata_qs ] # type: ignore
211+ filterable_metadata : List [str ] = cache .get ("usecase_filterable_metadata_labels" ) or []
212+ if not filterable_metadata :
213+ metadata_qs = Metadata .objects .filter (filterable = True )
214+ filterable_metadata = [str (meta .label ) for meta in metadata_qs ] # type: ignore
215+ cache .set (
216+ "usecase_filterable_metadata_labels" ,
217+ filterable_metadata ,
218+ timeout = METADATA_CACHE_TTL ,
219+ )
204220
205221 metadata_bucket = search .aggs .bucket ("metadata" , "nested" , path = "metadata" )
206222 composite_agg = A (
207223 "composite" ,
208224 sources = [
209- {
210- "metadata_label" : {
211- "terms" : {"field" : "metadata.metadata_item.label" }
212- }
213- },
225+ {"metadata_label" : {"terms" : {"field" : "metadata.metadata_item.label" }}},
214226 {"metadata_value" : {"terms" : {"field" : "metadata.value" }}},
215227 ],
216228 size = 10000 ,
@@ -219,13 +231,7 @@ def add_aggregations(self, search: Search) -> Search:
219231 "filter" ,
220232 { # type: ignore[arg-type]
221233 "bool" : {
222- "must" : [
223- {
224- "terms" : {
225- "metadata.metadata_item.label" : filterable_metadata
226- }
227- }
228- ]
234+ "must" : [{"terms" : {"metadata.metadata_item.label" : filterable_metadata }}]
229235 }
230236 },
231237 )
@@ -235,12 +241,8 @@ def add_aggregations(self, search: Search) -> Search:
235241
236242 return search
237243
238- @trace_method (
239- name = "generate_q_expression" , attributes = {"component" : "search_usecase" }
240- )
241- def generate_q_expression (
242- self , query : str
243- ) -> Optional [Union [ESQuery , List [ESQuery ]]]:
244+ @trace_method (name = "generate_q_expression" , attributes = {"component" : "search_usecase" })
245+ def generate_q_expression (self , query : str ) -> Optional [Union [ESQuery , List [ESQuery ]]]:
244246 """Generate Elasticsearch Query expression."""
245247 if query :
246248 queries : List [ESQuery ] = []
@@ -256,9 +258,7 @@ def generate_q_expression(
256258 ESQ ("wildcard" , ** {field : {"value" : f"*{ query } *" }}),
257259 ESQ (
258260 "fuzzy" ,
259- ** {
260- field : {"value" : query , "fuzziness" : "AUTO" }
261- },
261+ ** {field : {"value" : query , "fuzziness" : "AUTO" }},
262262 ),
263263 ],
264264 ),
@@ -281,18 +281,14 @@ def generate_q_expression(
281281 ESQ ("wildcard" , ** {field : {"value" : f"*{ query } *" }}),
282282 ESQ (
283283 "fuzzy" ,
284- ** {
285- field : {"value" : query , "fuzziness" : "AUTO" }
286- },
284+ ** {field : {"value" : query , "fuzziness" : "AUTO" }},
287285 ),
288286 ],
289287 ),
290288 )
291289 )
292290 else :
293- queries .append (
294- ESQ ("fuzzy" , ** {field : {"value" : query , "fuzziness" : "AUTO" }})
295- )
291+ queries .append (ESQ ("fuzzy" , ** {field : {"value" : query , "fuzziness" : "AUTO" }}))
296292 else :
297293 queries = [ESQ ("match_all" )]
298294
@@ -301,8 +297,13 @@ def generate_q_expression(
301297 @trace_method (name = "add_filters" , attributes = {"component" : "search_usecase" })
302298 def add_filters (self , filters : Dict [str , str ], search : Search ) -> Search :
303299 """Add filters to the search query."""
304- non_filter_metadata = Metadata .objects .filter (filterable = False ).all ()
305- excluded_labels : List [str ] = [e .label for e in non_filter_metadata ] # type: ignore
300+ excluded_labels : List [str ] = cache .get ("usecase_non_filter_metadata_labels" ) or []
301+ if not excluded_labels :
302+ non_filter_metadata = Metadata .objects .filter (filterable = False ).all ()
303+ excluded_labels = [e .label for e in non_filter_metadata ] # type: ignore
304+ cache .set (
305+ "usecase_non_filter_metadata_labels" , excluded_labels , timeout = METADATA_CACHE_TTL
306+ )
306307
307308 for filter in filters :
308309 if filter in excluded_labels :
@@ -335,9 +336,7 @@ def add_filters(self, filters: Dict[str, str], search: Search) -> Search:
335336 search = search .filter (
336337 "nested" ,
337338 path = "metadata" ,
338- query = {
339- "bool" : {"must" : {"term" : {f"metadata.value" : filters [filter ]}}}
340- },
339+ query = {"bool" : {"must" : {"term" : {f"metadata.value" : filters [filter ]}}}},
341340 )
342341 return search
343342
0 commit comments