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