Skip to content

Commit 76dc299

Browse files
duration lookup filters (baserow#4140)
* duration lookup filters
1 parent 5f1a456 commit 76dc299

File tree

9 files changed

+740
-19
lines changed

9 files changed

+740
-19
lines changed

backend/src/baserow/contrib/database/apps.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,14 +477,14 @@ def ready(self):
477477
HasNotValueHigherThanFilterType,
478478
HasNotValueLowerOrEqualTHanFilterType,
479479
HasNotValueLowerThanFilterType,
480+
HasValueComparableToFilter,
480481
HasValueContainsViewFilterType,
481482
HasValueContainsWordViewFilterType,
482483
HasValueEqualViewFilterType,
483484
HasValueHigherOrEqualThanFilter,
484485
HasValueLengthIsLowerThanViewFilterType,
485486
HasValueLowerOrEqualThanFilter,
486487
HasValueLowerThanFilter,
487-
hasValueComparableToFilter,
488488
)
489489

490490
view_filter_type_registry.register(HasValueEqualViewFilterType())
@@ -501,7 +501,7 @@ def ready(self):
501501
view_filter_type_registry.register(HasNoneSelectOptionEqualViewFilterType())
502502
view_filter_type_registry.register(HasValueLowerThanFilter())
503503
view_filter_type_registry.register(HasValueLowerOrEqualThanFilter())
504-
view_filter_type_registry.register(hasValueComparableToFilter())
504+
view_filter_type_registry.register(HasValueComparableToFilter())
505505
view_filter_type_registry.register(HasValueHigherOrEqualThanFilter())
506506
view_filter_type_registry.register(HasNotValueHigherOrEqualTHanFilterType())
507507
view_filter_type_registry.register(HasNotValueHigherThanFilterType())

backend/src/baserow/contrib/database/formula/expression_generator/django_expressions.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,3 +269,42 @@ def get_template_data(self, sql_value) -> dict:
269269
data = super().get_template_data(sql_value)
270270
data["comparison_op"] = self.comparison_op.value
271271
return data
272+
273+
274+
class JSONArrayCompareIntervalValueExpr(BaserowFilterExpression):
275+
"""
276+
Base class for expressions that compare an interval value in a JSON array.
277+
Together with the field_name and value, a comparison operator must be provided to be
278+
used in the template.
279+
"""
280+
281+
def __init__(
282+
self,
283+
field_name: F,
284+
value: Value,
285+
comparison_op: ComparisonOperator,
286+
output_field: Field,
287+
):
288+
super().__init__(field_name, value, output_field)
289+
if not isinstance(comparison_op, ComparisonOperator):
290+
raise ValueError(
291+
f"comparison_op must be a ComparisonOperator, not {type(comparison_op)}"
292+
)
293+
self.comparison_op = comparison_op
294+
295+
# fmt: off
296+
template = (
297+
f"""
298+
EXISTS(
299+
SELECT 1
300+
FROM JSONB_ARRAY_ELEMENTS(%(field_name)s) as filtered_field
301+
WHERE (filtered_field ->> 'value')::interval %(comparison_op)s make_interval(secs=>%(value)s)
302+
)
303+
""" # nosec B608 %(value)s %(comparison_op)s
304+
)
305+
# fmt: on
306+
307+
def get_template_data(self, sql_value) -> dict:
308+
data = super().get_template_data(sql_value)
309+
data["comparison_op"] = self.comparison_op.value
310+
return data

backend/src/baserow/contrib/database/formula/types/formula_types.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
)
6161
from baserow.contrib.database.formula.expression_generator.django_expressions import (
6262
ComparisonOperator,
63+
JSONArrayCompareIntervalValueExpr,
6364
JSONArrayCompareNumericValueExpr,
6465
)
6566
from baserow.contrib.database.formula.registries import formula_function_registry
@@ -813,6 +814,31 @@ def get_order_by_in_array_expr(self, field, field_name, order_direction):
813814
field_name, "value", "interval", output_field=models.DurationField()
814815
)
815816

817+
def get_has_numeric_value_comparable_to_filter_query(
818+
self,
819+
field_name: str,
820+
value: str,
821+
model_field: models.Field,
822+
field: "Field",
823+
comparison_op: ComparisonOperator,
824+
) -> "OptionallyAnnotatedQ":
825+
try:
826+
value = int(value)
827+
except (TypeError, ValueError):
828+
return Q()
829+
830+
return get_array_json_filter_expression(
831+
JSONArrayCompareIntervalValueExpr,
832+
field_name,
833+
Value(value),
834+
comparison_op=comparison_op,
835+
)
836+
837+
def get_in_array_is_query(self, field_name, value, model_field, field):
838+
return self.get_has_numeric_value_comparable_to_filter_query(
839+
field_name, value, model_field, field, ComparisonOperator.EQUAL
840+
)
841+
816842

817843
class BaserowFormulaDateType(
818844
HasValueEmptyFilterSupport, HasValueContainsFilterSupport, BaserowFormulaValidType

backend/src/baserow/contrib/database/views/array_view_filters.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
BaserowFormulaBooleanType,
3030
BaserowFormulaCharType,
3131
BaserowFormulaDateType,
32+
BaserowFormulaDurationType,
3233
BaserowFormulaMultipleSelectType,
3334
BaserowFormulaSingleSelectType,
3435
BaserowFormulaURLType,
@@ -55,6 +56,7 @@ class HasEmptyValueViewFilterType(ViewFilterType):
5556
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
5657
FormulaFieldType.array_of(BaserowFormulaMultipleSelectType.type),
5758
FormulaFieldType.array_of(BaserowFormulaMultipleCollaboratorsType.type),
59+
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
5860
),
5961
]
6062

@@ -124,6 +126,7 @@ class HasValueEqualViewFilterType(ComparisonHasValueFilter):
124126
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
125127
FormulaFieldType.array_of(BaserowFormulaMultipleSelectType.type),
126128
FormulaFieldType.array_of(BaserowFormulaMultipleCollaboratorsType.type),
129+
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
127130
),
128131
]
129132

@@ -287,11 +290,12 @@ class HasNoneSelectOptionEqualViewFilterType(
287290
type = "has_none_select_option_equal"
288291

289292

290-
class hasValueComparableToFilter(ComparisonHasValueFilter):
293+
class HasValueComparableToFilter(ComparisonHasValueFilter):
291294
type = "has_value_higher"
292295
compatible_field_types = [
293296
FormulaFieldType.compatible_with_formula_types(
294-
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
297+
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
298+
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
295299
),
296300
]
297301

@@ -305,7 +309,7 @@ def get_filter_expression(self, field_name, value, model_field, field):
305309

306310

307311
class HasNotValueHigherThanFilterType(
308-
NotViewFilterTypeMixin, hasValueComparableToFilter
312+
NotViewFilterTypeMixin, HasValueComparableToFilter
309313
):
310314
type = "has_not_value_higher"
311315

@@ -314,7 +318,8 @@ class HasValueHigherOrEqualThanFilter(ComparisonHasValueFilter):
314318
type = "has_value_higher_or_equal"
315319
compatible_field_types = [
316320
FormulaFieldType.compatible_with_formula_types(
317-
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
321+
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
322+
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
318323
),
319324
]
320325

@@ -341,7 +346,8 @@ class HasValueLowerThanFilter(ComparisonHasValueFilter):
341346
type = "has_value_lower"
342347
compatible_field_types = [
343348
FormulaFieldType.compatible_with_formula_types(
344-
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
349+
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
350+
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
345351
),
346352
]
347353

@@ -362,7 +368,8 @@ class HasValueLowerOrEqualThanFilter(ComparisonHasValueFilter):
362368
type = "has_value_lower_or_equal"
363369
compatible_field_types = [
364370
FormulaFieldType.compatible_with_formula_types(
365-
FormulaFieldType.array_of(BaserowFormulaNumberType.type)
371+
FormulaFieldType.array_of(BaserowFormulaNumberType.type),
372+
FormulaFieldType.array_of(BaserowFormulaDurationType.type),
366373
),
367374
]
368375

0 commit comments

Comments
 (0)