diff --git a/src/basic_memory/repository/metadata_filters.py b/src/basic_memory/repository/metadata_filters.py index 764e1669..29ec73e1 100644 --- a/src/basic_memory/repository/metadata_filters.py +++ b/src/basic_memory/repository/metadata_filters.py @@ -84,8 +84,12 @@ def parse_metadata_filters(filters: dict[str, Any]) -> List[ParsedMetadataFilter continue if op in {"$gt", "$gte", "$lt", "$lte"}: - normalized = _normalize_scalar(value) - comparison = "numeric" if _is_numeric_value(normalized) else "text" + if _is_numeric_value(value): + normalized = float(value) + comparison = "numeric" + else: + normalized = _normalize_scalar(value) + comparison = "text" parsed.append( ParsedMetadataFilter(path_parts, op.lstrip("$"), normalized, comparison) ) @@ -94,8 +98,12 @@ def parse_metadata_filters(filters: dict[str, Any]) -> List[ParsedMetadataFilter if op == "$between": if not isinstance(value, list) or len(value) != 2: raise ValueError(f"$between requires [min, max] for '{raw_key}'") - normalized = [_normalize_scalar(v) for v in value] - comparison = "numeric" if _is_numeric_collection(normalized) else "text" + if _is_numeric_collection(value): + normalized = [float(v) for v in value] + comparison = "numeric" + else: + normalized = [_normalize_scalar(v) for v in value] + comparison = "text" parsed.append(ParsedMetadataFilter(path_parts, "between", normalized, comparison)) continue diff --git a/src/basic_memory/repository/postgres_search_repository.py b/src/basic_memory/repository/postgres_search_repository.py index 29a3d04b..aa1d6c93 100644 --- a/src/basic_memory/repository/postgres_search_repository.py +++ b/src/basic_memory/repository/postgres_search_repository.py @@ -332,7 +332,7 @@ async def search( like_param_single = f"{base_param}_{j}_like_single" params[like_param_single] = f"%'{val}'%" tag_conditions.append( - f"({json_expr} @> :{tag_param}::jsonb " + f"({json_expr} @> CAST(:{tag_param} AS jsonb) " f"OR {text_expr} LIKE :{like_param} " f"OR {text_expr} LIKE :{like_param_single})" ) @@ -340,14 +340,11 @@ async def search( continue if filt.op in {"gt", "gte", "lt", "lte", "between"}: - if filt.comparison == "numeric": - numeric_expr = ( - f"CASE WHEN ({text_expr}) ~ '^-?\\\\d+(\\\\.\\\\d+)?$' " - f"THEN ({text_expr})::double precision END" - ) - compare_expr = numeric_expr - else: - compare_expr = text_expr + compare_expr = ( + f"({metadata_expr} #>> '{path}')::double precision" + if filt.comparison == "numeric" + else text_expr + ) if filt.op == "between": min_param = f"meta_val_{idx}_min" diff --git a/tests/repository/test_metadata_filters.py b/tests/repository/test_metadata_filters.py index 40efaccb..a2112fb2 100644 --- a/tests/repository/test_metadata_filters.py +++ b/tests/repository/test_metadata_filters.py @@ -31,12 +31,12 @@ def test_parse_in_operator(): def test_parse_comparison_numeric(): parsed = parse_metadata_filters({"schema.confidence": {"$gt": 0.7}}) - assert parsed == [ParsedMetadataFilter(["schema", "confidence"], "gt", "0.7", "numeric")] + assert parsed == [ParsedMetadataFilter(["schema", "confidence"], "gt", 0.7, "numeric")] def test_parse_between_numeric(): parsed = parse_metadata_filters({"score": {"$between": [0.3, 0.6]}}) - assert parsed == [ParsedMetadataFilter(["score"], "between", ["0.3", "0.6"], "numeric")] + assert parsed == [ParsedMetadataFilter(["score"], "between", [0.3, 0.6], "numeric")] def test_parse_between_text():