Skip to content

Commit 4ebbc54

Browse files
Merge pull request ZoneMinder#4601 from pliablepixels/fix-tag-filtering
fix: correct tag filter operator handling for No Tag and Any Tag
2 parents b8e0337 + 79253dc commit 4ebbc54

File tree

2 files changed

+34
-8
lines changed

2 files changed

+34
-8
lines changed

scripts/ZoneMinder/lib/ZoneMinder/Filter.pm

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,11 @@ sub Sql {
166166
$term->{op} = 'EXISTS';
167167
} elsif ( $term->{attr} eq 'Tags' ) {
168168
$fields .= ', (SELECT GROUP_CONCAT(Name) FROM Tags WHERE Id IN (SELECT TagId FROM Events_Tags WHERE Events_Tags.EventId=E.Id)) As Tags';
169-
$self->{Sql} .= 'T.Id';
169+
# Don't prepend T.Id for special tag values (0="No Tag", -1="Any Tag")
170+
# as those use EXISTS/NOT EXISTS subqueries instead
171+
if (!defined($term->{val}) or ($term->{val} ne '0' and $term->{val} ne '-1')) {
172+
$self->{Sql} .= 'T.Id';
173+
}
170174
$from .= ' LEFT JOIN Events_Tags AS ET ON E.Id = ET.EventId LEFT JOIN Tags AS T ON T.Id = ET.TagId';
171175
} elsif ( $term->{attr} =~ /^Monitor/ ) {
172176
if (!($fields =~ /MonitorName/)) {
@@ -316,7 +320,23 @@ sub Sql {
316320
} # end foreach temp_value
317321

318322
if ( $term->{op} ) {
319-
if ( $term->{op} eq '=~' ) {
323+
# Handle special tag values before generic operators to avoid
324+
# LEFT JOIN NULL comparison issues with EXISTS/NOT EXISTS
325+
if ( $term->{attr} eq 'Tags' and defined($term->{val}) and $term->{val} eq '0' ) {
326+
# "No Tag": = means no tags (NOT EXISTS), != means has tags (EXISTS)
327+
if ($term->{op} eq '!=' or $term->{op} eq 'IS NOT') {
328+
$self->{Sql} .= 'EXISTS (SELECT NULL FROM `Events_Tags` AS ET WHERE ET.EventId = E.Id)';
329+
} else {
330+
$self->{Sql} .= 'NOT EXISTS (SELECT NULL FROM `Events_Tags` AS ET WHERE ET.EventId = E.Id)';
331+
}
332+
} elsif ( $term->{attr} eq 'Tags' and defined($term->{val}) and $term->{val} eq '-1' ) {
333+
# "Any Tag": = means has tags (EXISTS), != means no tags (NOT EXISTS)
334+
if ($term->{op} eq '!=' or $term->{op} eq 'IS NOT') {
335+
$self->{Sql} .= 'NOT EXISTS (SELECT NULL FROM `Events_Tags` AS ET WHERE ET.EventId = E.Id)';
336+
} else {
337+
$self->{Sql} .= 'EXISTS (SELECT NULL FROM `Events_Tags` AS ET WHERE ET.EventId = E.Id)';
338+
}
339+
} elsif ( $term->{op} eq '=~' ) {
320340
$self->{Sql} .= ' REGEXP '.$value;
321341
} elsif ( $term->{op} eq '!~' ) {
322342
$self->{Sql} .= ' NOT REGEXP '.$value;
@@ -346,10 +366,6 @@ sub Sql {
346366
$self->{Sql} .= ' LIKE '.$value;
347367
} elsif ( $term->{op} eq 'NOT LIKE' ) {
348368
$self->{Sql} .= ' NOT LIKE '.$value;
349-
} elsif ( $term->{attr} eq 'Tags' and ($term->{op} eq 'LIKE' or $term->{op} eq 'IS') and $term->{val} eq 0) {
350-
$self->{Sql} .= 'NOT EXISTS (SELECT NULL FROM `Events_Tags` AS ET WHERE ET.EventId = E.Id)';
351-
} elsif ( $term->{attr} eq 'Tags' and ($term->{op} eq '=' or $term->{op} eq 'IS') and $term->{val} eq -1) {
352-
$self->{Sql} .= 'EXISTS (SELECT NULL FROM `Events_Tags` AS ET WHERE ET.EventId = E.Id)';
353369
} else {
354370
$self->{Sql} .= ' '.$term->{op}.' '.$value;
355371
}

web/includes/FilterTerm.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,9 +344,19 @@ public function sql() {
344344
}
345345
$sql .= '('.implode(' OR ', $subterms).')';
346346
} elseif (($this->attr === 'Tags') && ($values[0] === "'0'")) {
347-
$sql .= 'NOT EXISTS (SELECT NULL FROM Events_Tags AS ET WHERE ET.EventId = E.Id)';
347+
// "No Tag": = means no tags (NOT EXISTS), != means has tags (EXISTS)
348+
if ($this->op === '!=' || $this->op === 'IS NOT') {
349+
$sql .= 'EXISTS (SELECT NULL FROM Events_Tags AS ET WHERE ET.EventId = E.Id)';
350+
} else {
351+
$sql .= 'NOT EXISTS (SELECT NULL FROM Events_Tags AS ET WHERE ET.EventId = E.Id)';
352+
}
348353
} elseif (($this->attr === 'Tags') && ($values[0] === "'-1'")) {
349-
$sql .= 'EXISTS (SELECT NULL FROM Events_Tags AS ET WHERE ET.EventId = E.Id)';
354+
// "Any Tag": = means has tags (EXISTS), != means no tags (NOT EXISTS)
355+
if ($this->op === '!=' || $this->op === 'IS NOT') {
356+
$sql .= 'NOT EXISTS (SELECT NULL FROM Events_Tags AS ET WHERE ET.EventId = E.Id)';
357+
} else {
358+
$sql .= 'EXISTS (SELECT NULL FROM Events_Tags AS ET WHERE ET.EventId = E.Id)';
359+
}
350360
} else {
351361
$sql .= $this->sql_attr();
352362
if ($this->collate) $sql .= ' COLLATE '.$this->collate;

0 commit comments

Comments
 (0)