Skip to content

Commit e76099e

Browse files
feat: Add logical filters (AND,OR,NOT)
1 parent 518a012 commit e76099e

File tree

4 files changed

+142
-22
lines changed

4 files changed

+142
-22
lines changed

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,28 @@ table_rows = client.table_row_list(project, table_name, ExactDateEqFilter('colum
185185
table_rows = client.table_row_list(project, table_name, ExactDateOpFilter('column', '2023-06-01', op='eq'))
186186
```
187187

188+
```python
189+
from nocodb import filters
190+
191+
# Basic filters...
192+
nick_filter = filters.EqFilter("nickname", "elchicodepython")
193+
country_filter = filters.EqFilter("country", "es")
194+
girlfriend_code = filters.EqFilter("gfcode", "404")
195+
current_mood_code = filters.EqFilter("moodcode", "418")
196+
197+
# Combining filters using logical filters
198+
or_filter = filters.Or(nick_filter, country_filter)
199+
and_filter = filters.And(girlfriend_code, current_mood_code)
200+
201+
# Negating filters with a Not filter
202+
not_me = filters.Not(filters.EqFilter("nickname", "elchicodepython"))
203+
204+
# You can also combine combinations
205+
or_combined_filter = filters.Or(or_filter, and_filter)
206+
and_combined_filter = filters.And(or_filter, and_filter)
207+
208+
```
209+
188210
Credits to @MitPitt for asking this feature.
189211

190212
## Author notes

nocodb/filters/__init__.py

+22-9
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,25 @@
1-
from ..nocodb import WhereFilter
21
from .factory import basic_filter_class_factory
2+
from .logical import And, Not, Or
33

4-
5-
EqFilter = basic_filter_class_factory('eq')
4+
EqFilter = basic_filter_class_factory("eq")
65
EqualFilter = EqFilter
7-
NotEqualFilter = basic_filter_class_factory('neq')
8-
GreaterThanFilter = basic_filter_class_factory('gt')
9-
GreaterOrEqualFilter = basic_filter_class_factory('ge')
10-
LessThanFilter = basic_filter_class_factory('lt')
11-
LessOrEqualFilter = basic_filter_class_factory('le')
12-
LikeFilter = basic_filter_class_factory('like')
6+
NotEqualFilter = basic_filter_class_factory("neq")
7+
GreaterThanFilter = basic_filter_class_factory("gt")
8+
GreaterOrEqualFilter = basic_filter_class_factory("ge")
9+
LessThanFilter = basic_filter_class_factory("lt")
10+
LessOrEqualFilter = basic_filter_class_factory("le")
11+
LikeFilter = basic_filter_class_factory("like")
12+
13+
__all__ = [
14+
"And",
15+
"Not",
16+
"Or",
17+
"EqFilter",
18+
"EqualFilter",
19+
"NotEqualFilter",
20+
"GreaterThanFilter",
21+
"GreaterOrEqualFilter",
22+
"LessThanFilter",
23+
"LessOrEqualFilter",
24+
"LikeFilter",
25+
]

nocodb/filters/filters_test.py

+64-13
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,67 @@
55
from ..nocodb import WhereFilter
66

77

8-
@pytest.mark.parametrize('filter_class, expected_operator', [
9-
(filters.EqFilter, 'eq'),
10-
(filters.EqualFilter, 'eq'),
11-
(filters.NotEqualFilter, 'neq'),
12-
(filters.GreaterOrEqualFilter, 'ge'),
13-
(filters.GreaterThanFilter, 'gt'),
14-
(filters.LessThanFilter, 'lt'),
15-
(filters.LessOrEqualFilter, 'le'),
16-
(filters.LikeFilter, 'like')
17-
])
18-
def test_basic_filters_are_correctly_created(filter_class: WhereFilter, expected_operator: str):
19-
test_filter = filter_class('column', 'value')
20-
assert test_filter.get_where() == f'(column,{expected_operator},value)'
8+
@pytest.mark.parametrize(
9+
"filter_class, expected_operator",
10+
[
11+
(filters.EqFilter, "eq"),
12+
(filters.EqualFilter, "eq"),
13+
(filters.NotEqualFilter, "neq"),
14+
(filters.GreaterOrEqualFilter, "ge"),
15+
(filters.GreaterThanFilter, "gt"),
16+
(filters.LessThanFilter, "lt"),
17+
(filters.LessOrEqualFilter, "le"),
18+
(filters.LikeFilter, "like"),
19+
],
20+
)
21+
def test_basic_filters_are_correctly_created(
22+
filter_class: WhereFilter, expected_operator: str
23+
):
24+
test_filter = filter_class("column", "value")
25+
assert test_filter.get_where() == f"(column,{expected_operator},value)"
26+
27+
28+
def test_or_filter():
29+
nick_filter = filters.EqFilter("nickname", "elchicodepython")
30+
country_filter = filters.EqFilter("country", "es")
31+
nick_or_country_filter = filters.Or(nick_filter, country_filter)
32+
assert (
33+
nick_or_country_filter.get_where()
34+
== "((nickname,eq,elchicodepython)~or(country,eq,es))"
35+
)
36+
37+
38+
def test_and_filter():
39+
nick_filter = filters.EqFilter("nickname", "elchicodepython")
40+
country_filter = filters.EqFilter("country", "es")
41+
nick_or_country_filter = filters.And(nick_filter, country_filter)
42+
assert (
43+
nick_or_country_filter.get_where()
44+
== "((nickname,eq,elchicodepython)~and(country,eq,es))"
45+
)
46+
47+
48+
def test_combined_filter():
49+
nick_filter = filters.EqFilter("nickname", "elchicodepython")
50+
country_filter = filters.EqFilter("country", "es")
51+
girlfriend_code = filters.EqFilter("gfcode", "404")
52+
current_mood_code = filters.EqFilter("moodcode", "418")
53+
or_filter = filters.Or(nick_filter, country_filter)
54+
and_filter = filters.And(girlfriend_code, current_mood_code)
55+
or_combined_filter = filters.Or(or_filter, and_filter)
56+
and_combined_filter = filters.And(or_filter, and_filter)
57+
58+
assert (
59+
or_combined_filter.get_where()
60+
== "(((nickname,eq,elchicodepython)~or(country,eq,es))~or((gfcode,eq,404)~and(moodcode,eq,418)))"
61+
)
62+
assert (
63+
and_combined_filter.get_where()
64+
== "(((nickname,eq,elchicodepython)~or(country,eq,es))~and((gfcode,eq,404)~and(moodcode,eq,418)))"
65+
)
66+
67+
68+
def test_not_filter():
69+
me = filters.EqFilter("nickname", "elchicodepython")
70+
not_me = filters.Not(me)
71+
assert not_me.get_where() == "~not(nickname,eq,elchicodepython)"

nocodb/filters/logical.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from typing import List
2+
from ..nocodb import WhereFilter
3+
4+
5+
class Or(WhereFilter):
6+
def __init__(self, *filters: List[WhereFilter]):
7+
self.__filters = filters
8+
9+
def get_where(self) -> str:
10+
return (
11+
"("
12+
+ "~or".join([filter.get_where() for filter in self.__filters])
13+
+ ")"
14+
)
15+
16+
17+
class And(WhereFilter):
18+
def __init__(self, *filters: List[WhereFilter]):
19+
self.__filters = filters
20+
21+
def get_where(self) -> str:
22+
return (
23+
"("
24+
+ "~and".join([filter.get_where() for filter in self.__filters])
25+
+ ")"
26+
)
27+
28+
29+
class Not(WhereFilter):
30+
def __init__(self, filter: WhereFilter):
31+
self.__filter = filter
32+
33+
def get_where(self) -> str:
34+
return "~not" + self.__filter.get_where()

0 commit comments

Comments
 (0)