Skip to content
Open
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
21 changes: 21 additions & 0 deletions .code-samples.meilisearch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -610,3 +610,24 @@ update_embedders_1: |-
})
reset_embedders_1: |-
client.index('INDEX_NAME').reset_embedders()
list_dynamic_search_rules_1: |-
client.get_dynamic_search_rules()
get_dynamic_search_rule_1: |-
client.get_dynamic_search_rule('RULE_UID')
patch_dynamic_search_rule_1: |-
client.create_or_update_dynamic_search_rule('RULE_UID', {
'description': 'Rule description',
'priority': 10,
'active': True,
'conditions': [
{'scope': 'query', 'isEmpty': True}
],
'actions': [
{
'selector': {'indexUid': 'movies', 'id': '123'},
'action': {'type': 'pin', 'position': 1}
}
],
})
delete_dynamic_search_rule_1: |-
client.delete_dynamic_search_rule('RULE_UID')
98 changes: 98 additions & 0 deletions meilisearch/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
MeilisearchError,
)
from meilisearch.index import Index
from meilisearch.models.dynamic_search_rule import DynamicSearchRule, DynamicSearchRuleResults
from meilisearch.models.key import Key, KeysResults
from meilisearch.models.task import Batch, BatchResults, Task, TaskInfo, TaskResults
from meilisearch.models.webhook import Webhook, WebhooksResults
Expand Down Expand Up @@ -601,6 +602,103 @@ def delete_webhook(self, webhook_uuid: str) -> int:
response = self.http.delete(f"{self.config.paths.webhooks}/{webhook_uuid}")
return response.status_code

# DYNAMIC SEARCH RULES ROUTES

def get_dynamic_search_rules(self) -> DynamicSearchRuleResults:
"""Get all dynamic search rules.

Returns
-------
rules:
DynamicSearchRuleResults instance containing list of rules and pagination info.
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/list-dynamic-search-rules

Raises
------
MeilisearchApiError
An error containing details about why Meilisearch can't process your request.
Meilisearch error codes are described here:
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
"""
rules = self.http.post(self.config.paths.dynamic_search_rules, {})
return DynamicSearchRuleResults(**rules)

def get_dynamic_search_rule(self, uid: str) -> DynamicSearchRule:
"""Get information about a specific dynamic search rule.

Parameters
----------
uid:
The uid of the dynamic search rule to retrieve.

Returns
-------
rule:
The dynamic search rule information.
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/get-a-dynamic-search-rule

Raises
------
MeilisearchApiError
An error containing details about why Meilisearch can't process your request.
Meilisearch error codes are described here:
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
"""
rule = self.http.get(f"{self.config.paths.dynamic_search_rules}/{uid}")
return DynamicSearchRule(**rule)

def create_or_update_dynamic_search_rule(
self, uid: str, options: Mapping[str, Any]
) -> DynamicSearchRule:
"""Create or update a dynamic search rule.

Parameters
----------
uid:
The uid of the dynamic search rule to create or update.
options:
The dynamic search rule fields to create or update.

Returns
-------
rule:
The created or updated dynamic search rule.
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/create-or-update-a-dynamic-search-rule

Raises
------
MeilisearchApiError
An error containing details about why Meilisearch can't process your request.
Meilisearch error codes are described here:
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
"""
rule = self.http.patch(f"{self.config.paths.dynamic_search_rules}/{uid}", options)
return DynamicSearchRule(**rule)

def delete_dynamic_search_rule(self, uid: str) -> int:
"""Delete a dynamic search rule.

Parameters
----------
uid:
The uid of the dynamic search rule to delete.

Returns
-------
status_code:
The Response status code. 204 signifies a successful delete.
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/delete-a-dynamic-search-rule

Raises
------
MeilisearchApiError
An error containing details about why Meilisearch can't process your request.
Meilisearch error codes are described here:
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
"""
response = self.http.delete(f"{self.config.paths.dynamic_search_rules}/{uid}")
return response.status_code

def get_version(self) -> Dict[str, str]:
"""Get version Meilisearch

Expand Down
1 change: 1 addition & 0 deletions meilisearch/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class Paths:
network = "network"
experimental_features = "experimental-features"
webhooks = "webhooks"
dynamic_search_rules = "dynamic-search-rules"
export = "export"

def __init__(
Expand Down
27 changes: 27 additions & 0 deletions meilisearch/models/dynamic_search_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from typing import Any, Dict, List, Optional

from camel_converter.pydantic_base import CamelBase
from pydantic import ConfigDict


class DynamicSearchRule(CamelBase):
"""Model for a Meilisearch dynamic search rule."""

model_config = ConfigDict(arbitrary_types_allowed=True)

uid: str
description: Optional[str] = None
priority: Optional[int] = None
active: Optional[bool] = None
conditions: Optional[List[Dict[str, Any]]] = None
actions: Optional[List[Dict[str, Any]]] = None


class DynamicSearchRuleResults(CamelBase):
"""Model for dynamic search rules list results."""

model_config = ConfigDict(arbitrary_types_allowed=True)
results: List[DynamicSearchRule]
offset: int
limit: int
total: int
118 changes: 118 additions & 0 deletions tests/client/test_client_dynamic_search_rules_meilisearch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
"""Tests for dynamic search rule management endpoints."""

import pytest

from meilisearch.errors import MeilisearchApiError

pytestmark = pytest.mark.usefixtures("enable_dynamic_search_rules")


def test_get_dynamic_search_rules_empty(client):
"""Test getting dynamic search rules when none exist."""
rules = client.get_dynamic_search_rules()
assert rules.results is not None
assert isinstance(rules.results, list)
assert len(rules.results) == 0

Comment thread
w1ndcn marked this conversation as resolved.

def test_create_or_update_dynamic_search_rule(client):
"""Test creating a dynamic search rule."""
rule_data = {
"description": "Test rule for promotion",
"priority": 10,
"active": True,
"conditions": [{"scope": "query", "isEmpty": True}],
"actions": [
{
"selector": {"indexUid": "movies", "id": "123"},
"action": {"type": "pin", "position": 1},
}
],
}

rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)

assert rule.uid == "test-rule"
assert rule.description == rule_data["description"]
assert rule.priority == 10
assert rule.active is True


def test_get_dynamic_search_rule(client):
"""Test getting a single dynamic search rule."""
# Create a rule first
rule_data = {
"description": "Test rule",
"active": True,
}

created_rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)

# Get the rule
rule = client.get_dynamic_search_rule(created_rule.uid)

assert rule.uid == created_rule.uid
assert rule.description == rule_data["description"]


def test_get_dynamic_search_rule_not_found(client):
"""Test getting a dynamic search rule that doesn't exist."""
with pytest.raises(MeilisearchApiError):
client.get_dynamic_search_rule("non-existent-uid")


def test_update_dynamic_search_rule(client):
"""Test updating a dynamic search rule."""
# Create a rule first
rule_data = {
"description": "Original description",
"priority": 10,
"active": True,
"conditions": [{"scope": "query", "isEmpty": True}],
"actions": [
{
"selector": {"indexUid": "movies", "id": "123"},
"action": {"type": "pin", "position": 1},
}
],
}

created_rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)

# Update the rule
update_data = {
"description": "Updated description",
"priority": 5,
}

updated_rule = client.create_or_update_dynamic_search_rule(created_rule.uid, update_data)

assert updated_rule.uid == created_rule.uid
assert updated_rule.description == update_data["description"]
assert updated_rule.priority == 5


def test_delete_dynamic_search_rule(client):
"""Test deleting a dynamic search rule."""
# Create a rule first
rule_data = {
"description": "Rule to delete",
"active": True,
}

created_rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)

# Delete the rule
status_code = client.delete_dynamic_search_rule(created_rule.uid)

assert status_code == 204

# Verify it's deleted
with pytest.raises(MeilisearchApiError):
client.get_dynamic_search_rule(created_rule.uid)


def test_delete_dynamic_search_rule_not_found(client):
"""Test deleting a dynamic search rule that doesn't exist."""
with pytest.raises(MeilisearchApiError):
client.delete_dynamic_search_rule("non-existent-uid")
17 changes: 17 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,20 @@ def enable_network_options():
json={"network": False},
timeout=10,
)


@fixture
def enable_dynamic_search_rules():
requests.patch(
f"{common.BASE_URL}/experimental-features",
headers={"Authorization": f"Bearer {common.MASTER_KEY}"},
json={"dynamicSearchRules": True},
timeout=10,
)
yield
requests.patch(
f"{common.BASE_URL}/experimental-features",
headers={"Authorization": f"Bearer {common.MASTER_KEY}"},
json={"dynamicSearchRules": False},
timeout=10,
Comment thread
w1ndcn marked this conversation as resolved.
)
Loading