diff --git a/src/arcadedb/_native/codec.py b/src/arcadedb/_native/codec.py index 6f0503ba..cf6d3f15 100644 --- a/src/arcadedb/_native/codec.py +++ b/src/arcadedb/_native/codec.py @@ -343,7 +343,9 @@ class RID: __slots__ = ("text",) def __init__(self, text): - self.text = text + # An RID of an RID is the same id: a hit of find_nearest(include_vector=True) already carries the row's RID and the record + # wrappers wrap their rid again (save, delete, new_edge); the fast parameter path writes .text into the JSON text (#265) + self.text = text.text if isinstance(text, RID) else text def __str__(self): return self.text diff --git a/tests/conftest.py b/tests/conftest.py index eb8cc7bb..ec3147c7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -65,6 +65,8 @@ collect_ignore.append("test_native_async_callbacks.py") # the short call path of a point read (codec.encode_params, lib.exec_query, first()), observed on the native library collect_ignore.append("test_native_point_read.py") + # an RID built from an RID keeps the text, so the fast named-parameter path of codec.encode_params can encode it (#265) + collect_ignore.append("test_native_rid_params.py") # to_json_list walks the rows with codec.json_native only when a reply restored a tagged value (#219), observed on the native library collect_ignore.append("test_native_json_list.py") # the vector provider the native glue picks by default (ARCADEDB_VECTOR_PROVIDER, #153), observed on the native library diff --git a/tests/test_native_rid_params.py b/tests/test_native_rid_params.py new file mode 100644 index 00000000..f10ee9e0 --- /dev/null +++ b/tests/test_native_rid_params.py @@ -0,0 +1,42 @@ +"""An RID built from an RID is the same id, and it travels as a parameter like that id (#265). + +``find_nearest(include_vector=True)`` hands out records whose rid is already the row's ``RID``, and the native record wrappers wrap +their rid again (``RID(self._rid)`` in save, the update, delete, ``new_edge`` and the edge reads). ``RID.__init__`` kept its argument +as is, so ``.text`` was an ``RID``: the fast named-parameter path of ``codec.encode_params`` raised ``TypeError: Object of type RID +is not JSON serializable`` on save, and the slower paths sent a nested tag. The save itself is covered by +``test_vector_hit_records.py::test_a_hit_can_be_modified_and_saved``; conftest.py collects this file on the native backend only. +""" + +import pytest + +from arcadedb._native.codec import RID, encode_params + + +def test_an_rid_of_an_rid_keeps_the_text(): + inner = RID("#3:7") + outer = RID(inner) + assert type(outer.text) is str + assert outer.text == "#3:7" + assert outer == inner + assert hash(outer) == hash(inner) + assert str(outer) == "#3:7" + assert repr(outer) == "RID('#3:7')" + assert RID(RID(RID("#3:7"))).text == "#3:7" + + +@pytest.mark.parametrize("binary", [False, True]) +def test_a_wrapped_rid_encodes_like_the_rid(binary): + def text(args): + out = encode_params(args, binary=binary) + return out[0] if binary else out + + # a named map of plain values and RIDs (the fast path: save's conflict check, delete, new_edge's src and dst) + assert text([{"rid": RID(RID("#3:7"))}]) == text([{"rid": RID("#3:7")}]) + assert text([{"src": RID(RID("#3:7")), "dst": RID("#3:8")}]) == text( + [{"src": RID("#3:7"), "dst": RID("#3:8")}] + ) + # an RID beside a value that is not plain (the update: rid and row) and a positional RID take the hook path + assert text([{"rid": RID(RID("#3:7")), "row": {"a": [1, 2]}}]) == text( + [{"rid": RID("#3:7"), "row": {"a": [1, 2]}}] + ) + assert text([RID(RID("#3:7")), 1]) == text([RID("#3:7"), 1])