Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/arcadedb/_native/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand All @@ -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)

Expand Down
4 changes: 3 additions & 1 deletion src/arcadedb/_native/records.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
11 changes: 11 additions & 0 deletions tests/test_native_point_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
39 changes: 39 additions & 0 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading