|
1 | | -from llama_index.core.vector_stores.types import BasePydanticVectorStore |
| 1 | +from llama_index.core.vector_stores.types import ( |
| 2 | + BasePydanticVectorStore, |
| 3 | + FilterOperator, |
| 4 | + MetadataFilter, |
| 5 | +) |
2 | 6 | from llama_index.vector_stores.postgres import PGVectorStore |
| 7 | +from llama_index.vector_stores.postgres.base import PGVectorStore as _PGVectorStore |
3 | 8 |
|
4 | 9 |
|
5 | 10 | def test_class(): |
6 | 11 | names_of_base_classes = [b.__name__ for b in PGVectorStore.__mro__] |
7 | 12 | assert BasePydanticVectorStore.__name__ in names_of_base_classes |
| 13 | + |
| 14 | + |
| 15 | +def _make_store() -> PGVectorStore: |
| 16 | + """Create a minimal PGVectorStore using __new__ so no DB connection is needed.""" |
| 17 | + store = _PGVectorStore.__new__(_PGVectorStore) |
| 18 | + # _to_postgres_operator only needs the operator enum, so we delegate to the |
| 19 | + # real implementation; we just avoid touching any DB-related attributes. |
| 20 | + return store |
| 21 | + |
| 22 | + |
| 23 | +def test_build_filter_clause_string_with_underscores(): |
| 24 | + """String values that look like numbers (e.g., '2024_123') must use string |
| 25 | + comparison, not a ::float cast. Python's float() accepts underscores, but |
| 26 | + PostgreSQL's ::float cast does not, so using float() as the type check |
| 27 | + caused DataError for such values (issue #15962).""" |
| 28 | + store = _make_store() |
| 29 | + f = MetadataFilter(key="file_name", value="2024_123", operator=FilterOperator.EQ) |
| 30 | + clause = store._build_filter_clause(f) |
| 31 | + sql = clause.text |
| 32 | + assert "::float" not in sql, f"string value should not use ::float cast; got: {sql}" |
| 33 | + assert "'2024_123'" in sql |
| 34 | + |
| 35 | + |
| 36 | +def test_build_filter_clause_int_value(): |
| 37 | + """Integer filter values should use the ::float cast for numeric comparison.""" |
| 38 | + store = _make_store() |
| 39 | + f = MetadataFilter(key="year", value=2024, operator=FilterOperator.EQ) |
| 40 | + clause = store._build_filter_clause(f) |
| 41 | + sql = clause.text |
| 42 | + assert "::float" in sql, f"int value should use ::float cast; got: {sql}" |
| 43 | + assert "2024" in sql |
| 44 | + |
| 45 | + |
| 46 | +def test_build_filter_clause_float_value(): |
| 47 | + """Float filter values should use the ::float cast.""" |
| 48 | + store = _make_store() |
| 49 | + f = MetadataFilter(key="score", value=0.9, operator=FilterOperator.GT) |
| 50 | + clause = store._build_filter_clause(f) |
| 51 | + sql = clause.text |
| 52 | + assert "::float" in sql, f"float value should use ::float cast; got: {sql}" |
| 53 | + |
| 54 | + |
| 55 | +def test_build_filter_clause_plain_number_string(): |
| 56 | + """A string value that happens to be a plain decimal number (e.g., '123') |
| 57 | + should still use string comparison because the user explicitly provided a |
| 58 | + string type.""" |
| 59 | + store = _make_store() |
| 60 | + f = MetadataFilter(key="year", value="123", operator=FilterOperator.EQ) |
| 61 | + clause = store._build_filter_clause(f) |
| 62 | + sql = clause.text |
| 63 | + assert "::float" not in sql, f"string '123' should not use ::float cast; got: {sql}" |
| 64 | + assert "'123'" in sql |
0 commit comments