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
2 changes: 1 addition & 1 deletion .github/workflows/test-and-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
services:
typesense:
image: typesense/typesense:27.1
image: typesense/typesense:28.0
ports:
- 8108:8108
volumes:
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ per-file-ignores =
tests/*.py: S101, WPS226, WPS118, WPS202, WPS204, WPS218, WPS211, WPS604, WPS431, WPS210, WPS201, WPS437
src/typesense/types/*.py: B950, WPS215, WPS111, WPS462, WPS322, WPS428, WPS114, WPS110, WPS202
src/typesense/documents.py: WPS320, E704, D102, WPS428, WPS220
src/typesense/stemming_dictionaries.py: WPS320, E704, D102, WPS428, WPS220
src/typesense/api_call.py: WPS110, WPS211
src/typesense/request_handler.py: WPS110, WPS211

Expand Down
2 changes: 1 addition & 1 deletion src/typesense/api_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ def _make_request_and_process_response(
url: str,
entity_type: typing.Type[TEntityDict],
as_json: bool,
**kwargs: typing.Any,
**kwargs: SessionFunctionKwargs[TParams, TBody],
) -> typing.Union[TEntityDict, str]:
"""Make the API request and process the response."""
request_response = self.request_handler.make_request(
Expand Down
5 changes: 4 additions & 1 deletion src/typesense/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from typesense.keys import Keys
from typesense.multi_search import MultiSearch
from typesense.operations import Operations
from typesense.stemming import Stemming
from typesense.stopwords import Stopwords

TDoc = typing.TypeVar("TDoc", bound=DocumentSchema)
Expand All @@ -56,7 +57,7 @@ class Client:

This class serves as the entry point for all Typesense operations. It initializes
and provides access to various components of the Typesense SDK, such as collections,
multi-search, keys, aliases, analytics, operations, debug, stopwords,
multi-search, keys, aliases, analytics, stemming, operations, debug, stopwords,
and conversation models.

Attributes:
Expand All @@ -67,6 +68,7 @@ class Client:
keys (Keys): Instance for managing API keys.
aliases (Aliases): Instance for managing collection aliases.
analytics (Analytics): Instance for analytics operations.
stemming (Stemming): Instance for stemming dictionary operations.
operations (Operations): Instance for various Typesense operations.
debug (Debug): Instance for debug operations.
stopwords (Stopwords): Instance for managing stopwords.
Expand Down Expand Up @@ -96,6 +98,7 @@ def __init__(self, config_dict: ConfigDict) -> None:
self.keys = Keys(self.api_call)
self.aliases = Aliases(self.api_call)
self.analytics = Analytics(self.api_call)
self.stemming = Stemming(self.api_call)
self.operations = Operations(self.api_call)
self.debug = Debug(self.api_call)
self.stopwords = Stopwords(self.api_call)
Expand Down
31 changes: 28 additions & 3 deletions src/typesense/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
HealthCheckResponse,
LogSlowRequestsTimeParams,
OperationResponse,
SchemaChangesResponse,
SnapshotParameters,
)

Expand All @@ -48,8 +49,9 @@ class Operations:
"""

resource_path: typing.Final[str] = "/operations"
healht_path: typing.Final[str] = "/health"
health_path: typing.Final[str] = "/health"
config_path: typing.Final[str] = "/config"
schema_changes: typing.Final[str] = "/schema_changes"

def __init__(self, api_call: ApiCall):
"""
Expand All @@ -60,6 +62,23 @@ def __init__(self, api_call: ApiCall):
"""
self.api_call = api_call

@typing.overload
def perform(
self,
operation_name: typing.Literal["schema_changes"],
query_params: None = None,
) -> typing.List[SchemaChangesResponse]:
"""
Perform a vote operation.

Args:
operation_name (Literal["schema_changes"]): The name of the operation.
query_params (None, optional): Query parameters (not used for vote operation).

Returns:
OperationResponse: The response from the vote operation.
"""

@typing.overload
def perform(
self,
Expand Down Expand Up @@ -150,7 +169,13 @@ def perform(
def perform(
self,
operation_name: typing.Union[
typing.Literal["snapshot, vote, db/compact, cache/clear"],
typing.Literal[
"snapshot",
"vote",
"db/compact",
"cache/clear",
"schema_changes",
],
str,
],
query_params: typing.Union[
Expand Down Expand Up @@ -189,7 +214,7 @@ def is_healthy(self) -> bool:
bool: True if the server is healthy, False otherwise.
"""
call_resp = self.api_call.get(
Operations.healht_path,
Operations.health_path,
as_json=True,
entity_type=HealthCheckResponse,
)
Expand Down
50 changes: 50 additions & 0 deletions src/typesense/stemming.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"""
Module for managing stemming dictionaries in Typesense.

This module provides a class for managing stemming dictionaries in Typesense,
including creating, updating, and retrieving them.

Classes:
- Stemming: Handles operations related to stemming dictionaries.

Attributes:
- StemmingDictionaries: The StemmingDictionaries object for managing stemming dictionaries.

Methods:
- __init__: Initializes the Stemming object.

The Stemming class interacts with the Typesense API to manage stemming dictionary operations.
It provides access to the StemmingDictionaries object for managing stemming dictionaries.

For more information on stemming dictionaries, refer to the Stemming
[documentation](https://typesense.org/docs/28.0/api/stemming.html)

This module uses type hinting and is compatible with Python 3.11+ as well as earlier
versions through the use of the typing_extensions library.
"""

from typesense.api_call import ApiCall
from typesense.stemming_dictionaries import StemmingDictionaries


class Stemming(object):
"""
Class for managing stemming dictionaries in Typesense.

This class provides methods to interact with stemming dictionaries, including
creating, updating, and retrieving them.

Attributes:
dictionaries (StemmingDictionaries): The StemmingDictionaries object for managing
stemming dictionaries.
"""

def __init__(self, api_call: ApiCall):
"""
Initialize the Stemming object.

Args:
api_call (ApiCall): The API call object for making requests.
"""
self.api_call = api_call
self.dictionaries = StemmingDictionaries(api_call)
187 changes: 187 additions & 0 deletions src/typesense/stemming_dictionaries.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"""
Module for interacting with the stemming dictionaries endpoint of the Typesense API.

This module provides a class for managing stemming dictionaries in Typesense, including creating
and updating them.

Classes:
- StemmingDictionaries: Handles operations related to stemming dictionaries.

Methods:
- __init__: Initializes the StemmingDictionaries object.
- __getitem__: Retrieves or creates a StemmingDictionary object for a given dictionary_id.
- upsert: Creates or updates a stemming dictionary.
- _upsert_list: Creates or updates a list of stemming dictionaries.
- _dump_to_jsonl: Dumps a list of StemmingDictionaryCreateSchema objects to a JSONL string.
- _parse_response: Parses the response from the upsert operation.
- _upsert_raw: Performs the raw upsert operation.
- _endpoint_path: Constructs the API endpoint path for this specific stemming dictionary.

The StemmingDictionaries class interacts with the Typesense API to manage stemming dictionary
operations.
It provides methods to create, update, and retrieve stemming dictionaries, as well as
access individual StemmingDictionary objects.

For more information on stemming dictionaries,
refer to the Stemming [documentation](https://typesense.org/docs/28.0/api/stemming.html)
"""

import sys

if sys.version_info >= (3, 11):
import typing
else:
import typing_extensions as typing

import json

from typesense.api_call import ApiCall
from typesense.stemming_dictionary import StemmingDictionary
from typesense.types.stemming import (
StemmingDictionariesRetrieveSchema,
StemmingDictionaryCreateSchema,
)


class StemmingDictionaries:
"""
Class for managing stemming dictionaries in Typesense.

This class provides methods to interact with stemming dictionaries, including
creating, updating, and retrieving them.

Attributes:
api_call (ApiCall): The API call object for making requests.
stemming_dictionaries (Dict[str, StemmingDictionary]): A dictionary of
StemmingDictionary objects.
"""

resource_path: typing.Final[str] = "/stemming/dictionaries"

def __init__(self, api_call: ApiCall):
"""
Initialize the StemmingDictionaries object.

Args:
api_call (ApiCall): The API call object for making requests.
"""
self.api_call = api_call
self.stemming_dictionaries: typing.Dict[str, StemmingDictionary] = {}

def __getitem__(self, dictionary_id: str) -> StemmingDictionary:
"""
Get or create an StemmingDictionary object for a given rule_id.

Args:
rule_id (str): The ID of the analytics rule.

Returns:
StemmingDictionary: The StemmingDictionary object for the given ID.
"""
if not self.stemming_dictionaries.get(dictionary_id):
self.stemming_dictionaries[dictionary_id] = StemmingDictionary(
self.api_call,
dictionary_id,
)
return self.stemming_dictionaries[dictionary_id]

def retrieve(self) -> StemmingDictionariesRetrieveSchema:
"""
Retrieve the list of stemming dictionaries.

Returns:
StemmingDictionariesRetrieveSchema: The list of stemming dictionaries.
"""
response: StemmingDictionariesRetrieveSchema = self.api_call.get(
self._endpoint_path(),
entity_type=StemmingDictionariesRetrieveSchema,
)
return response

@typing.overload
def upsert(
self,
dictionary_id: str,
word_root_combinations: typing.Union[str, bytes],
) -> str: ...

@typing.overload
def upsert(
self,
dictionary_id: str,
word_root_combinations: typing.List[StemmingDictionaryCreateSchema],
) -> typing.List[StemmingDictionaryCreateSchema]: ...

def upsert(
self,
dictionary_id: str,
word_root_combinations: typing.Union[
typing.List[StemmingDictionaryCreateSchema],
str,
bytes,
],
) -> typing.Union[str, typing.List[StemmingDictionaryCreateSchema]]:
if isinstance(word_root_combinations, (str, bytes)):
return self._upsert_raw(dictionary_id, word_root_combinations)

return self._upsert_list(dictionary_id, word_root_combinations)

def _upsert_list(
self,
dictionary_id: str,
word_root_combinations: typing.List[StemmingDictionaryCreateSchema],
) -> typing.List[StemmingDictionaryCreateSchema]:
word_combos_in_jsonl = self._dump_to_jsonl(word_root_combinations)
response = self._upsert_raw(dictionary_id, word_combos_in_jsonl)
return self._parse_response(response)

def _dump_to_jsonl(
self,
word_root_combinations: typing.List[StemmingDictionaryCreateSchema],
) -> str:
word_root_strs = [json.dumps(combo) for combo in word_root_combinations]

return "\n".join(word_root_strs)

def _parse_response(
self,
response: str,
) -> typing.List[StemmingDictionaryCreateSchema]:
object_list: typing.List[StemmingDictionaryCreateSchema] = []

for line in response.split("\n"):
try:
decoded = json.loads(line)
except json.JSONDecodeError:
raise ValueError(f"Failed to parse JSON from response: {line}")
object_list.append(decoded)
return object_list

def _upsert_raw(
self,
dictionary_id: str,
word_root_combinations: typing.Union[bytes, str],
) -> str:
response: str = self.api_call.post(
self._endpoint_path("import"),
body=word_root_combinations,
as_json=False,
entity_type=str,
params={"id": dictionary_id},
)
return response

def _endpoint_path(self, action: typing.Union[str, None] = None) -> str:
"""
Construct the API endpoint path for this specific stemming dictionary.

Args:
action (str, optional): The action to perform on the stemming dictionary.
Defaults to None.

Returns:
str: The constructed endpoint path.
"""
if action:
return f"{StemmingDictionaries.resource_path}/{action}"
return StemmingDictionaries.resource_path
Loading