Source code for runic.ogm.driver.arcadedb

"""ArcadeDB dialect and driver factory.

ArcadeDB is accessed via the Bolt protocol using the ``neo4j`` Python driver
with ``encrypted=False``.  The only difference from a generic Bolt connection
is the ``ArcadeDBDialect`` strategy.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any

from runic.ogm.driver.bolt import BoltDriver, BoltEdge, BoltNode

if TYPE_CHECKING:
    from runic.ogm.core.descriptors import FieldInfo


class ArcadeDBNode(BoltNode):
    """BoltNode variant that corrects ArcadeDB's Bolt element_id offset.

    ArcadeDB's Bolt implementation sends ``element_id`` as 2× the value that
    the Cypher ``id()`` function returns.  Dividing by 2 realigns the stored
    primary-key with what ``WHERE id(n) = $pk`` expects.
    """

    @property
    def element_id(self) -> int:
        return int(self._raw.element_id) // 2


[docs] class ArcadeDBDialect: """Strategy for ArcadeDB-specific Cypher generation. Key differences from FalkorDB: - No ``toInteger()`` cast needed for ``id()``-based lookups - No ``vecf32()`` or ``intern()`` wrappers (raw values stored as-is) - Vector KNN via ``CALL vector.neighbors(...)`` - Fulltext search not yet supported (raises ``NotImplementedError``) - ``SET n.prop = point()`` is not supported via Bolt; GeoLocation is stored as a ``{"latitude": x, "longitude": y}`` map instead. """ supports_geo_update: bool = False def generated_id_where(self, alias: str, param: str) -> str: return f"WHERE id({alias}) = ${param}" def cypher_fn_for_field(self, fi: FieldInfo) -> str | None: # noqa: ARG002 # GeoLocation serialised as a plain map dict — no point() wrapper needed. return None def fulltext_call(self, label: str, alias: str, query_param: str) -> str: # noqa: ARG002 raise NotImplementedError( "ArcadeDB fulltext search via Cypher is not yet supported. " "Use ArcadeDB HTTP API or contribute a CALL procedure mapping." ) def vector_knn_start( self, alias: str, labels_str: str, # noqa: ARG002 type_name: str, field_name: str, # noqa: ARG002 ) -> str: return ( f"CALL vector.neighbors('{type_name}[{field_name}]', $__knn_vec, $__knn_k) " f"YIELD node AS {alias}, distance" ) def vector_knn_score_expr(self, alias: str, field_name: str) -> str: # noqa: ARG002 return "distance AS __score" def wrap_node(self, raw: Any) -> ArcadeDBNode: return ArcadeDBNode(raw) def wrap_edge(self, raw: Any) -> BoltEdge: return BoltEdge(raw)
_ARCADE_DIALECT = ArcadeDBDialect()
[docs] def create_arcadedb_driver( host: str, port: int, database: str, username: str, password: str, ) -> BoltDriver: """Create a :class:`~runic.ogm.driver.bolt.BoltDriver` configured for ArcadeDB.""" return BoltDriver.from_params( host=host, port=port, database=database, username=username, password=password, dialect=_ARCADE_DIALECT, encrypted=False, )