Skip to content

Commit 4dcd296

Browse files
authored
consolidate attribute filtering to match non-english and url encoded values (#22002)
1 parent 73c1e12 commit 4dcd296

File tree

2 files changed

+77
-16
lines changed

2 files changed

+77
-16
lines changed

frigate/api/event.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,25 @@
6969
router = APIRouter(tags=[Tags.events])
7070

7171

72+
def _build_attribute_filter_clause(attributes: str):
73+
filtered_attributes = [
74+
attr.strip() for attr in attributes.split(",") if attr.strip()
75+
]
76+
attribute_clauses = []
77+
78+
for attr in filtered_attributes:
79+
attribute_clauses.append(Event.data.cast("text") % f'*:"{attr}"*')
80+
81+
escaped_attr = json.dumps(attr, ensure_ascii=True)[1:-1]
82+
if escaped_attr != attr:
83+
attribute_clauses.append(Event.data.cast("text") % f'*:"{escaped_attr}"*')
84+
85+
if not attribute_clauses:
86+
return None
87+
88+
return reduce(operator.or_, attribute_clauses)
89+
90+
7291
@router.get(
7392
"/events",
7493
response_model=list[EventResponse],
@@ -193,14 +212,9 @@ def events(
193212

194213
if attributes != "all":
195214
# Custom classification results are stored as data[model_name] = result_value
196-
filtered_attributes = attributes.split(",")
197-
attribute_clauses = []
198-
199-
for attr in filtered_attributes:
200-
attribute_clauses.append(Event.data.cast("text") % f'*:"{attr}"*')
201-
202-
attribute_clause = reduce(operator.or_, attribute_clauses)
203-
clauses.append(attribute_clause)
215+
attribute_clause = _build_attribute_filter_clause(attributes)
216+
if attribute_clause is not None:
217+
clauses.append(attribute_clause)
204218

205219
if recognized_license_plate != "all":
206220
filtered_recognized_license_plates = recognized_license_plate.split(",")
@@ -508,7 +522,7 @@ def events_search(
508522
cameras = params.cameras
509523
labels = params.labels
510524
sub_labels = params.sub_labels
511-
attributes = params.attributes
525+
attributes = unquote(params.attributes)
512526
zones = params.zones
513527
after = params.after
514528
before = params.before
@@ -607,13 +621,9 @@ def events_search(
607621

608622
if attributes != "all":
609623
# Custom classification results are stored as data[model_name] = result_value
610-
filtered_attributes = attributes.split(",")
611-
attribute_clauses = []
612-
613-
for attr in filtered_attributes:
614-
attribute_clauses.append(Event.data.cast("text") % f'*:"{attr}"*')
615-
616-
event_filters.append(reduce(operator.or_, attribute_clauses))
624+
attribute_clause = _build_attribute_filter_clause(attributes)
625+
if attribute_clause is not None:
626+
event_filters.append(attribute_clause)
617627

618628
if zones != "all":
619629
zone_clauses = []

frigate/test/http_api/test_http_event.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,57 @@ def test_get_event_list_sort_start_time(self):
168168
assert events[0]["id"] == id
169169
assert events[1]["id"] == id2
170170

171+
def test_get_event_list_match_multilingual_attribute(self):
172+
event_id = "123456.zh"
173+
attribute = "中文标签"
174+
175+
with AuthTestClient(self.app) as client:
176+
super().insert_mock_event(event_id, data={"custom_attr": attribute})
177+
178+
events = client.get("/events", params={"attributes": attribute}).json()
179+
assert len(events) == 1
180+
assert events[0]["id"] == event_id
181+
182+
events = client.get(
183+
"/events", params={"attributes": "%E4%B8%AD%E6%96%87%E6%A0%87%E7%AD%BE"}
184+
).json()
185+
assert len(events) == 1
186+
assert events[0]["id"] == event_id
187+
188+
def test_events_search_match_multilingual_attribute(self):
189+
event_id = "123456.zh.search"
190+
attribute = "中文标签"
191+
mock_embeddings = Mock()
192+
mock_embeddings.search_thumbnail.return_value = [(event_id, 0.05)]
193+
194+
self.app.frigate_config.semantic_search.enabled = True
195+
self.app.embeddings = mock_embeddings
196+
197+
with AuthTestClient(self.app) as client:
198+
super().insert_mock_event(event_id, data={"custom_attr": attribute})
199+
200+
events = client.get(
201+
"/events/search",
202+
params={
203+
"search_type": "similarity",
204+
"event_id": event_id,
205+
"attributes": attribute,
206+
},
207+
).json()
208+
assert len(events) == 1
209+
assert events[0]["id"] == event_id
210+
211+
events = client.get(
212+
"/events/search",
213+
params={
214+
"search_type": "similarity",
215+
"event_id": event_id,
216+
"attributes": "%E4%B8%AD%E6%96%87%E6%A0%87%E7%AD%BE",
217+
},
218+
).json()
219+
assert len(events) == 1
220+
assert events[0]["id"] == event_id
221+
171222
def test_get_good_event(self):
172223
id = "123456.random"
173224

0 commit comments

Comments
 (0)