diff --git a/src/arcadedb/_native/database.py b/src/arcadedb/_native/database.py index 21b0592e..1bb479bf 100644 --- a/src/arcadedb/_native/database.py +++ b/src/arcadedb/_native/database.py @@ -1347,10 +1347,20 @@ def drop_property(self, type_name, property_name): ("HASH", False): "NOTUNIQUE_HASH", } + # Index types whose engine factory refuses a unique index (LSMTreeFullTextIndex, LSMSparseVectorIndex, + # LSMTreeGeoIndex: "... cannot be unique"). LSM_TREE and HASH take it; LSM_VECTOR does not check it. + _NEVER_UNIQUE = frozenset({"FULL_TEXT", "LSM_SPARSE_VECTOR", "GEOSPATIAL"}) + def create_index( self, type_name, property_names, unique=False, index_type="LSM_TREE" ): kind = getattr(index_type, "value", index_type) + if unique and str(kind).upper() in self._NEVER_UNIQUE: + # Refuse before any statement runs, as the engine does, and worded like the JPype path + raise ArcadeDBError( + f"Failed to create index on '{type_name}[{','.join(property_names)}]': " + f"{str(kind).upper()} index cannot be unique" + ) kind = self._INDEX_KIND.get((kind, bool(unique)), kind) cols = ", ".join(f"`{p}`" for p in property_names) self._run(f"CREATE INDEX ON `{type_name}` ({cols}) {kind}") @@ -1364,7 +1374,8 @@ def exists_index(self, index_name): ) def drop_index(self, index_name, force=False): - self._run(f"DROP INDEX `{index_name}`") + # force skips the existence check, as on the JVM build (a missing index is then a no-op) + self._run(f"DROP INDEX `{index_name}`{' IF EXISTS' if force else ''}") # -- reads, as small objects with the engine's accessors (schema_objects.py) diff --git a/src/arcadedb/_native/records.py b/src/arcadedb/_native/records.py index c727ff3c..6d815024 100644 --- a/src/arcadedb/_native/records.py +++ b/src/arcadedb/_native/records.py @@ -81,7 +81,9 @@ def get_rid(self) -> str: return self._rid or _UNSAVED def get_identity(self): - return RID(self.get_rid()) + # a row can already carry @rid as an RID (vector-search rows of some engine builds): wrapping it again made str() fail + rid = self.get_rid() + return rid if isinstance(rid, RID) else RID(rid) def get_type_name(self) -> str: self._check_open() diff --git a/tests/test_native_point_read.py b/tests/test_native_point_read.py index c4cd53c3..a86943d9 100644 --- a/tests/test_native_point_read.py +++ b/tests/test_native_point_read.py @@ -324,3 +324,14 @@ def test_exec_query_replies_as_call_bin_does(temp_db): assert (short[1] is None) == (long[1] is None) if flags == 4: assert short[1] is not None and np.array_equal(short[1], long[1]) + + +def test_identity_of_a_record_whose_rid_came_as_an_rid_object(): + # the vector-search rows of engine e353330c5c carry @rid as an RID, not text: get_identity() wrapped it twice and + # str(record.get_identity()) raised "__str__ returned non-string" + from arcadedb._native.records import Document as NativeDocument + + for rid in (codec.RID("#3:7"), "#3:7"): + identity = NativeDocument(None, "T", rid, {}).get_identity() + assert str(identity) == "#3:7" + assert identity == codec.RID("#3:7") diff --git a/tests/test_schema.py b/tests/test_schema.py index 37b37504..1826fc1b 100644 --- a/tests/test_schema.py +++ b/tests/test_schema.py @@ -341,6 +341,17 @@ def test_create_full_text_index(self, test_db): ) assert idx is not None + def test_create_unique_full_text_index_is_refused(self, test_db): + """The engine refuses a unique full-text index; no index is left behind.""" + schema = test_db.schema + schema.create_document_type("T") + schema.create_property("T", "p", PropertyType.STRING) + + with pytest.raises(ArcadeDBError): + schema.create_index("T", ["p"], unique=True, index_type=IndexType.FULL_TEXT) + + assert [i for i in schema.get_indexes() if i.getTypeName() == "T"] == [] + def test_create_lsm_tree_index(self, test_db): """Test creating an LSM tree index explicitly.""" schema = test_db.schema @@ -435,6 +446,34 @@ def test_drop_index(self, test_db): schema.drop_index(index_name) assert schema.exists_index(index_name) is False + def test_drop_missing_index_force_is_a_no_op(self, test_db): + """force=True skips the existence check: a missing index is not an error.""" + schema = test_db.schema + schema.create_vertex_type("User") + schema.create_property("User", "temp", PropertyType.STRING) + schema.create_index("User", ["temp"]) + before = sorted(i.getName() for i in schema.get_indexes()) + + schema.drop_index("Nope", force=True) + + assert sorted(i.getName() for i in schema.get_indexes()) == before + with pytest.raises(ArcadeDBError): + schema.drop_index("Nope", force=False) + + def test_drop_existing_index_force(self, test_db): + """force=True still drops an index that exists.""" + schema = test_db.schema + schema.create_vertex_type("User") + schema.create_property("User", "temp", PropertyType.STRING) + schema.create_index("User", ["temp"]) + name = next( + i.getName() for i in schema.get_indexes() if i.getTypeName() == "User" + ) + + schema.drop_index(name, force=True) + + assert schema.exists_index(name) is False + def test_drop_non_existent_index_fails(self, test_db): """Test dropping non-existent index raises error.""" schema = test_db.schema