@@ -245,121 +245,97 @@ def apply_collections_filter(search: Search, collection_ids: List[str]):
245
245
@staticmethod
246
246
def apply_datetime_filter (
247
247
search : Search , interval : Optional [Union [DateTimeType , str ]]
248
- ):
248
+ ) -> Search :
249
249
"""Apply a filter to search on datetime, start_datetime, and end_datetime fields.
250
250
251
251
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)
254
258
255
259
Returns:
256
- Search: The filtered search object.
260
+ The filtered search object.
257
261
"""
262
+ if not interval :
263
+ return search
264
+
258
265
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
260
272
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
264
273
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
+ ]
301
302
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 ))
363
339
364
340
@staticmethod
365
341
def apply_bbox_filter (search : Search , bbox : List ):
0 commit comments