Skip to content

Commit

Permalink
Merge pull request #1507 from weaviate/#1504/add-debug-get-object
Browse files Browse the repository at this point in the history
Add support for debug get object endpoint:
  • Loading branch information
tsmith023 authored Jan 10, 2025
2 parents 2c45c1f + cefdfc9 commit c02ed69
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 3 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ per-file-ignores =
weaviate/collections/classes/types.py:A005
weaviate/collections/collections/__init__.py:A005
weaviate/collections/__init__.py:A005
weaviate/debug/types.py:A005
weaviate/types.py:A005
weaviate/warnings.py:A005

Expand Down
38 changes: 38 additions & 0 deletions integration/test_client_debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from integration.conftest import ClientFactory, CollectionFactory

from weaviate.classes.config import DataType, Property
from weaviate.classes.debug import DebugRESTObject


def test_get_object_single_node(
client_factory: ClientFactory, collection_factory: CollectionFactory
) -> None:
client = client_factory()
collection = collection_factory(properties=[Property(name="name", data_type=DataType.TEXT)])

uuid = collection.data.insert({"name": "John Doe"})

debug_obj = client.debug.get_object_over_rest(collection.name, uuid)
assert debug_obj is not None
assert isinstance(debug_obj, DebugRESTObject)
assert str(debug_obj.uuid) == str(uuid)

non_existant_uuid = "00000000-0000-0000-0000-000000000000"
debug_obj = client.debug.get_object_over_rest(collection.name, non_existant_uuid)
assert debug_obj is None


def test_get_object_multi_node(
client_factory: ClientFactory, collection_factory: CollectionFactory
) -> None:
client = client_factory(ports=(8087, 50058))
collection = collection_factory(
ports=(8087, 50058), properties=[Property(name="name", data_type=DataType.TEXT)]
)

uuid = collection.data.insert({"name": "John Doe"})

for node_name in ["node1", "node2", "node3"]:
debug_obj = client.debug.get_object_over_rest(collection.name, uuid, node_name=node_name)
assert debug_obj is not None
assert str(debug_obj.uuid) == str(uuid)
5 changes: 5 additions & 0 deletions weaviate/classes/debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from weaviate.debug.types import DebugRESTObject

__all__ = [
"DebugRESTObject",
]
10 changes: 10 additions & 0 deletions weaviate/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from .connect.base import (
ConnectionParams,
)
from .debug import _Debug, _DebugAsync
from .embedded import EmbeddedOptions
from .rbac import _RolesAsync, _Roles
from .types import NUMBER
Expand Down Expand Up @@ -87,7 +88,12 @@ def __init__(
Use it to retrieve collection objects using `client.collections.get("MyCollection")` or to create new collections using `client.collections.create("MyCollection", ...)`.
"""
self.debug = _Debug(self._connection)
"""This namespace contains functionality used to debug Weaviate clusters. As such, it is deemed experimental and is subject to change.
We can make no guarantees about the stability of this namespace nor the potential for future breaking changes. Use at your own risk."""
self.roles = _Roles(self._connection)
"""This namespace contains all functionality to manage Weaviate's RBAC functionality."""

def __enter__(self) -> "WeaviateClient":
self.connect() # pyright: ignore # gets patched by syncify.convert to be sync
Expand Down Expand Up @@ -146,6 +152,10 @@ def __init__(
Use it to retrieve collection objects using `client.collections.get("MyCollection")` or to create new collections using `await client.collections.create("MyCollection", ...)`.
"""
self.debug = _DebugAsync(self._connection)
"""This namespace contains functionality used to debug Weaviate clusters. As such, it is deemed experimental and is subject to change.
We can make no guarantees about the stability of this namespace nor the potential for future breaking changes. Use at your own risk."""
self.roles = _RolesAsync(self._connection)
"""This namespace contains all functionality to manage Weaviate's RBAC functionality."""

Expand Down
9 changes: 6 additions & 3 deletions weaviate/client.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ from weaviate.collections.collections.sync import _Collections
from .collections.batch.client import _BatchClientWrapper
from .collections.cluster import _Cluster, _ClusterAsync
from .connect import ConnectionV4
from .debug import _Debug, _DebugAsync
from .rbac import _Roles, _RolesAsync
from .types import NUMBER

Expand All @@ -25,9 +26,10 @@ from weaviate.client_base import _WeaviateClientInit

class WeaviateAsyncClient(_WeaviateClientInit):
_connection: ConnectionV4
collections: _CollectionsAsync
backup: _BackupAsync
collections: _CollectionsAsync
cluster: _ClusterAsync
debug: _DebugAsync
roles: _RolesAsync
async def close(self) -> None: ...
async def connect(self) -> None: ...
Expand All @@ -42,10 +44,11 @@ class WeaviateAsyncClient(_WeaviateClientInit):

class WeaviateClient(_WeaviateClientInit):
_connection: ConnectionV4
collections: _Collections
batch: _BatchClientWrapper
backup: _Backup
batch: _BatchClientWrapper
collections: _Collections
cluster: _Cluster
debug: _Debug
roles: _Roles
def close(self) -> None: ...
def connect(self) -> None: ...
Expand Down
7 changes: 7 additions & 0 deletions weaviate/debug/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from .debug import _DebugAsync
from .sync import _Debug

__all__ = [
"_Debug",
"_DebugAsync",
]
51 changes: 51 additions & 0 deletions weaviate/debug/debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from typing import Dict, Optional

from weaviate.classes.config import ConsistencyLevel
from weaviate.connect import ConnectionV4
from weaviate.connect.v4 import _ExpectedStatusCodes
from weaviate.debug.types import DebugRESTObject
from weaviate.types import UUID


class _DebugBase:
def __init__(
self,
connection: ConnectionV4,
) -> None:
self._connection = connection


class _DebugAsync(_DebugBase):
async def get_object_over_rest(
self,
collection: str,
uuid: UUID,
*,
consistency_level: Optional[ConsistencyLevel] = None,
node_name: Optional[str] = None,
tenant: Optional[str] = None,
) -> Optional[DebugRESTObject]:
"""Use the REST API endpoint /objects/{className}/{id} to retrieve an object directly from the database without search.
The key difference between `debug.get_object_over_rest` and `query.fetch_object_by_id` is the underlying protocol.
This method uses REST while that method uses gRPC.
"""
path = f"/objects/{collection}/{str(uuid)}"

params: Dict[str, str] = {}
if consistency_level is not None:
params["consistency"] = consistency_level.value
if node_name is not None:
params["node_name"] = node_name
if tenant is not None:
params["tenant"] = tenant

res = await self._connection.get(
path=path,
params=params,
error_msg="Object was not retrieved",
status_codes=_ExpectedStatusCodes(ok_in=[200, 404], error="get object"),
)
if res.status_code == 404:
return None
return DebugRESTObject(**res.json())
7 changes: 7 additions & 0 deletions weaviate/debug/sync.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from weaviate import syncify
from weaviate.debug.debug import _DebugAsync


@syncify.convert
class _Debug(_DebugAsync):
pass
17 changes: 17 additions & 0 deletions weaviate/debug/sync.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from typing import Optional

from weaviate.classes.config import ConsistencyLevel
from weaviate.debug.debug import _DebugBase
from weaviate.debug.types import DebugRESTObject
from weaviate.types import UUID

class _Debug(_DebugBase):
def get_object_over_rest(
self,
collection: str,
uuid: UUID,
*,
consistency_level: Optional[ConsistencyLevel] = None,
node_name: Optional[str] = None,
tenant: Optional[str] = None,
) -> Optional[DebugRESTObject]: ...
17 changes: 17 additions & 0 deletions weaviate/debug/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from datetime import datetime
from typing import Any, Dict, Optional

from pydantic import BaseModel, Field

from weaviate.types import uuid_package


class DebugRESTObject(BaseModel):
collection: str = Field(..., alias="class")
creation_time: datetime = Field(..., alias="creationTimeUnix")
last_update_time: datetime = Field(..., alias="lastUpdateTimeUnix")
properties: Dict[str, Any] = Field(...)
tenant: Optional[str] = Field(None)
uuid: uuid_package.UUID = Field(..., alias="id")
vector: Optional[list[float]] = Field(None)
vectors: Optional[Dict[str, list[float]]] = Field(None)

0 comments on commit c02ed69

Please sign in to comment.