From 59884ab0fb1b1298761182867956166ebb47681e Mon Sep 17 00:00:00 2001 From: tasumu <135967531+tasumu@users.noreply.github.com> Date: Tue, 30 Dec 2025 12:56:48 +0900 Subject: [PATCH] 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): --- common/metadata_utils.py | 62 ++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/common/metadata_utils.py b/common/metadata_utils.py index d7bb3c818..fdca6b935 100644 --- a/common/metadata_utils.py +++ b/common/metadata_utils.py @@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # +import ast import logging from typing import Any, Callable, Dict @@ -49,8 +50,8 @@ def meta_filter(metas: dict, filters: list[dict], logic: str = "and"): try: if isinstance(input, list): input = input[0] - input = float(input) - value = float(value) + input = ast.literal_eval(input) + value = ast.literal_eval(value) except Exception: pass if isinstance(input, str): @@ -58,28 +59,41 @@ def meta_filter(metas: dict, filters: list[dict], logic: str = "and"): if isinstance(value, str): value = value.lower() - for conds in [ - (operator == "contains", input in value if not isinstance(input, list) else all([i in value for i in input])), - (operator == "not contains", input not in value if not isinstance(input, list) else all([i not in value for i in input])), - (operator == "in", 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])), - (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())), - (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())), - (operator == "empty", not input), - (operator == "not empty", input), - (operator == "=", input == value), - (operator == "≠", input != value), - (operator == ">", input > value), - (operator == "<", input < value), - (operator == "≥", input >= value), - (operator == "≤", input <= value), - ]: - try: - if all(conds): - ids.extend(docids) - break - except Exception: - pass + matched = False + try: + if operator == "contains": + matched = input in value if not isinstance(input, list) else all(i in value for i in input) + elif operator == "not contains": + matched = input not in value if not isinstance(input, list) else all(i not in value for i in input) + elif operator == "in": + matched = input in value if not isinstance(input, list) else all(i in value for i in input) + elif operator == "not in": + matched = input not in value if not isinstance(input, list) else all(i not in value for i in input) + elif operator == "start with": + 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()) + elif operator == "end with": + 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()) + elif operator == "empty": + matched = not input + elif operator == "not empty": + matched = bool(input) + elif operator == "=": + matched = input == value + elif operator == "≠": + 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 for k, v2docs in metas.items():