@@ -245,121 +245,97 @@ def apply_collections_filter(search: Search, collection_ids: List[str]):
245245 @staticmethod
246246 def apply_datetime_filter (
247247 search : Search , interval : Optional [Union [DateTimeType , str ]]
248- ):
248+ ) -> Search :
249249 """Apply a filter to search on datetime, start_datetime, and end_datetime fields.
250250
251251 Args:
252- search (Search): The search object to filter.
253- interval: Optional[Union[DateTimeType, str]]
252+ search: The search object to filter.
253+ interval: Optional datetime interval to filter by. Can be:
254+ - A single datetime string (e.g., "2023-01-01T12:00:00")
255+ - A datetime range string (e.g., "2023-01-01/2023-12-31")
256+ - A datetime object
257+ - A tuple of (start_datetime, end_datetime)
254258
255259 Returns:
256- Search: The filtered search object.
260+ The filtered search object.
257261 """
262+ if not interval :
263+ return search
264+
258265 should = []
259- datetime_search = return_date (interval )
266+ try :
267+ datetime_search = return_date (interval )
268+ except (ValueError , TypeError ) as e :
269+ # Handle invalid interval formats if return_date fails
270+ logger .error (f"Invalid interval format: { interval } , error: { e } " )
271+ return search
260272
261- # If the request is a single datetime return
262- # items with datetimes equal to the requested datetime OR
263- # the requested datetime is between their start and end datetimes
264273 if "eq" in datetime_search :
265- should .extend (
266- [
267- Q (
268- "bool" ,
269- filter = [
270- Q (
271- "term" ,
272- properties__datetime = datetime_search ["eq" ],
273- ),
274- ],
275- ),
276- Q (
277- "bool" ,
278- filter = [
279- Q (
280- "range" ,
281- properties__start_datetime = {
282- "lte" : datetime_search ["eq" ],
283- },
284- ),
285- Q (
286- "range" ,
287- properties__end_datetime = {
288- "gte" : datetime_search ["eq" ],
289- },
290- ),
291- ],
292- ),
293- ]
294- )
295-
296- # If the request is a date range return
297- # items with datetimes within the requested date range OR
298- # their startdatetime ithin the requested date range OR
299- # their enddatetime ithin the requested date range OR
300- # the requested daterange within their start and end datetimes
274+ # For exact matches, include:
275+ # 1. Items with matching exact datetime
276+ # 2. Items with datetime:null where the time falls within their range
277+ should = [
278+ Q (
279+ "bool" ,
280+ filter = [
281+ Q ("exists" , field = "properties.datetime" ),
282+ Q ("term" , ** {"properties__datetime" : datetime_search ["eq" ]}),
283+ ],
284+ ),
285+ Q (
286+ "bool" ,
287+ must_not = [Q ("exists" , field = "properties.datetime" )],
288+ filter = [
289+ Q ("exists" , field = "properties.start_datetime" ),
290+ Q ("exists" , field = "properties.end_datetime" ),
291+ Q (
292+ "range" ,
293+ properties__start_datetime = {"lte" : datetime_search ["eq" ]},
294+ ),
295+ Q (
296+ "range" ,
297+ properties__end_datetime = {"gte" : datetime_search ["eq" ]},
298+ ),
299+ ],
300+ ),
301+ ]
301302 else :
302- should .extend (
303- [
304- Q (
305- "bool" ,
306- filter = [
307- Q (
308- "range" ,
309- properties__datetime = {
310- "gte" : datetime_search ["gte" ],
311- "lte" : datetime_search ["lte" ],
312- },
313- ),
314- ],
315- ),
316- Q (
317- "bool" ,
318- filter = [
319- Q (
320- "range" ,
321- properties__start_datetime = {
322- "gte" : datetime_search ["gte" ],
323- "lte" : datetime_search ["lte" ],
324- },
325- ),
326- ],
327- ),
328- Q (
329- "bool" ,
330- filter = [
331- Q (
332- "range" ,
333- properties__end_datetime = {
334- "gte" : datetime_search ["gte" ],
335- "lte" : datetime_search ["lte" ],
336- },
337- ),
338- ],
339- ),
340- Q (
341- "bool" ,
342- filter = [
343- Q (
344- "range" ,
345- properties__start_datetime = {
346- "lte" : datetime_search ["gte" ]
347- },
348- ),
349- Q (
350- "range" ,
351- properties__end_datetime = {
352- "gte" : datetime_search ["lte" ]
353- },
354- ),
355- ],
356- ),
357- ]
358- )
359-
360- search = search .query (Q ("bool" , filter = [Q ("bool" , should = should )]))
361-
362- return search
303+ # For date ranges, include:
304+ # 1. Items with datetime in the range
305+ # 2. Items with datetime:null that overlap the search range
306+ should = [
307+ Q (
308+ "bool" ,
309+ filter = [
310+ Q ("exists" , field = "properties.datetime" ),
311+ Q (
312+ "range" ,
313+ properties__datetime = {
314+ "gte" : datetime_search ["gte" ],
315+ "lte" : datetime_search ["lte" ],
316+ },
317+ ),
318+ ],
319+ ),
320+ Q (
321+ "bool" ,
322+ must_not = [Q ("exists" , field = "properties.datetime" )],
323+ filter = [
324+ Q ("exists" , field = "properties.start_datetime" ),
325+ Q ("exists" , field = "properties.end_datetime" ),
326+ Q (
327+ "range" ,
328+ properties__start_datetime = {"lte" : datetime_search ["lte" ]},
329+ ),
330+ Q (
331+ "range" ,
332+ properties__end_datetime = {"gte" : datetime_search ["gte" ]},
333+ ),
334+ ],
335+ ),
336+ ]
337+
338+ return search .query (Q ("bool" , should = should , minimum_should_match = 1 ))
363339
364340 @staticmethod
365341 def apply_bbox_filter (search : Search , bbox : List ):
0 commit comments