Fix TypeError in meta_filter when using numeric metadata (#12286)

The filter_out function in metadata_utils.py was using a list of tuples
to evaluate conditions. Python eagerly evaluates all tuple elements when
constructing the list, causing "input in value" to be evaluated even
when the operator is "=". When input and value are floats (after numeric
conversion), this causes TypeError: "argument of type 'float' is not
iterable".

This change replaces the tuple list with if-elif chain, ensuring only
the matching condition is evaluated.

### What problem does this PR solve?

Fixes #12285

When using comparison operators like `=`, `>`, `<` with numeric
metadata, the `filter_out` function throws `TypeError("argument of type
'float' is not iterable")`. This is because Python eagerly evaluates all
tuple elements when constructing a list, causing `input in value` to be
evaluated even when the operator is `=`.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)
- [ ] New Feature (non-breaking change which adds functionality)
- [ ] Documentation Update
- [ ] Refactoring
- [ ] Performance Improvement
- [ ] Other (please describe):
This commit is contained in:
tasumu
2025-12-30 12:56:48 +09:00
committed by GitHub
parent 4a6d37f0e8
commit 59884ab0fb

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
import ast
import logging import logging
from typing import Any, Callable, Dict from typing import Any, Callable, Dict
@ -49,8 +50,8 @@ def meta_filter(metas: dict, filters: list[dict], logic: str = "and"):
try: try:
if isinstance(input, list): if isinstance(input, list):
input = input[0] input = input[0]
input = float(input) input = ast.literal_eval(input)
value = float(value) value = ast.literal_eval(value)
except Exception: except Exception:
pass pass
if isinstance(input, str): if isinstance(input, str):
@ -58,28 +59,41 @@ def meta_filter(metas: dict, filters: list[dict], logic: str = "and"):
if isinstance(value, str): if isinstance(value, str):
value = value.lower() value = value.lower()
for conds in [ matched = False
(operator == "contains", input in value if not isinstance(input, list) else all([i in value for i in input])), try:
(operator == "not contains", input not in value if not isinstance(input, list) else all([i not in value for i in input])), if operator == "contains":
(operator == "in", input in value if not isinstance(input, list) else all([i in value for i in input])), matched = input in value if not isinstance(input, list) else all(i in value for i in input)
(operator == "not in", input not in value if not isinstance(input, list) else all([i not in value for i in input])), elif operator == "not contains":
(operator == "start with", str(input).lower().startswith(str(value).lower()) if not isinstance(input, list) else "".join([str(i).lower() for i in input]).startswith(str(value).lower())), matched = input not in value if not isinstance(input, list) else all(i not in value for i in input)
(operator == "end with", str(input).lower().endswith(str(value).lower()) if not isinstance(input, list) else "".join([str(i).lower() for i in input]).endswith(str(value).lower())), elif operator == "in":
(operator == "empty", not input), matched = input in value if not isinstance(input, list) else all(i in value for i in input)
(operator == "not empty", input), elif operator == "not in":
(operator == "=", input == value), matched = input not in value if not isinstance(input, list) else all(i not in value for i in input)
(operator == "", input != value), elif operator == "start with":
(operator == ">", input > value), matched = str(input).lower().startswith(str(value).lower()) if not isinstance(input, list) else "".join([str(i).lower() for i in input]).startswith(str(value).lower())
(operator == "<", input < value), elif operator == "end with":
(operator == "", input >= value), matched = str(input).lower().endswith(str(value).lower()) if not isinstance(input, list) else "".join([str(i).lower() for i in input]).endswith(str(value).lower())
(operator == "", input <= value), elif operator == "empty":
]: matched = not input
try: elif operator == "not empty":
if all(conds): matched = bool(input)
ids.extend(docids) elif operator == "=":
break matched = input == value
except Exception: elif operator == "":
pass matched = input != value
elif operator == ">":
matched = input > value
elif operator == "<":
matched = input < value
elif operator == "":
matched = input >= value
elif operator == "":
matched = input <= value
except Exception:
pass
if matched:
ids.extend(docids)
return ids return ids
for k, v2docs in metas.items(): for k, v2docs in metas.items():