Skip to content

Commit 68831d5

Browse files
committed
feat: add support for dynamic search rules
1 parent ada25db commit 68831d5

5 files changed

Lines changed: 269 additions & 0 deletions

File tree

.code-samples.meilisearch.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,3 +610,24 @@ update_embedders_1: |-
610610
})
611611
reset_embedders_1: |-
612612
client.index('INDEX_NAME').reset_embedders()
613+
list_dynamic_search_rules_1: |-
614+
client.get_dynamic_search_rules()
615+
get_dynamic_search_rule_1: |-
616+
client.get_dynamic_search_rule('RULE_UID')
617+
patch_dynamic_search_rule_1: |-
618+
client.create_or_update_dynamic_search_rule('RULE_UID', {
619+
'description': 'Rule description',
620+
'priority': 10,
621+
'active': True,
622+
'conditions': [
623+
{'scope': 'query', 'isEmpty': True}
624+
],
625+
'actions': [
626+
{
627+
'selector': {'indexUid': 'movies', 'id': '123'},
628+
'action': {'type': 'pin', 'position': 1}
629+
}
630+
],
631+
})
632+
delete_dynamic_search_rule_1: |-
633+
client.delete_dynamic_search_rule('RULE_UID')

meilisearch/client.py

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from meilisearch.models.key import Key, KeysResults
3434
from meilisearch.models.task import Batch, BatchResults, Task, TaskInfo, TaskResults
3535
from meilisearch.models.webhook import Webhook, WebhooksResults
36+
from meilisearch.models.dynamic_search_rule import DynamicSearchRule, DynamicSearchRuleResults
3637
from meilisearch.task import TaskHandler
3738

3839

@@ -600,6 +601,105 @@ def delete_webhook(self, webhook_uuid: str) -> int:
600601
"""
601602
response = self.http.delete(f"{self.config.paths.webhooks}/{webhook_uuid}")
602603
return response.status_code
604+
605+
# DYNAMIC SEARCH RULES ROUTES
606+
607+
def get_dynamic_search_rules(self) -> DynamicSearchRuleResults:
608+
"""Get all dynamic search rules.
609+
610+
Returns
611+
-------
612+
rules:
613+
DynamicSearchRuleResults instance containing list of rules and pagination info.
614+
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/list-dynamic-search-rules
615+
616+
Raises
617+
------
618+
MeilisearchApiError
619+
An error containing details about why Meilisearch can't process your request.
620+
Meilisearch error codes are described here:
621+
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
622+
"""
623+
rules = self.http.post(self.config.paths.dynamic_search_rules, {})
624+
return DynamicSearchRuleResults(**rules)
625+
626+
def get_dynamic_search_rule(self, uid: str) -> DynamicSearchRule:
627+
"""Get information about a specific dynamic search rule.
628+
629+
Parameters
630+
----------
631+
uid:
632+
The uid of the dynamic search rule to retrieve.
633+
634+
Returns
635+
-------
636+
rule:
637+
The dynamic search rule information.
638+
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/get-a-dynamic-search-rule
639+
640+
Raises
641+
------
642+
MeilisearchApiError
643+
An error containing details about why Meilisearch can't process your request.
644+
Meilisearch error codes are described here:
645+
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
646+
"""
647+
rule = self.http.get(f"{self.config.paths.dynamic_search_rules}/{uid}")
648+
return DynamicSearchRule(**rule)
649+
650+
def create_or_update_dynamic_search_rule(
651+
self, uid: str, options: Mapping[str, Any]
652+
) -> DynamicSearchRule:
653+
"""Create or update a dynamic search rule.
654+
655+
Parameters
656+
----------
657+
uid:
658+
The uid of the dynamic search rule to create or update.
659+
options:
660+
The dynamic search rule fields to create or update.
661+
662+
Returns
663+
-------
664+
rule:
665+
The created or updated dynamic search rule.
666+
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/create-or-update-a-dynamic-search-rule
667+
668+
Raises
669+
------
670+
MeilisearchApiError
671+
An error containing details about why Meilisearch can't process your request.
672+
Meilisearch error codes are described here:
673+
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
674+
"""
675+
rule = self.http.patch(f"{self.config.paths.dynamic_search_rules}/{uid}", options)
676+
return DynamicSearchRule(**rule)
677+
678+
679+
def delete_dynamic_search_rule(self, uid: str) -> int:
680+
"""Delete a dynamic search rule.
681+
682+
Parameters
683+
----------
684+
uid:
685+
The uid of the dynamic search rule to delete.
686+
687+
Returns
688+
-------
689+
status_code:
690+
The Response status code. 204 signifies a successful delete.
691+
https://www.meilisearch.com/docs/reference/api/dynamic-search-rules/delete-a-dynamic-search-rule
692+
693+
Raises
694+
------
695+
MeilisearchApiError
696+
An error containing details about why Meilisearch can't process your request.
697+
Meilisearch error codes are described here:
698+
https://www.meilisearch.com/docs/reference/errors/error_codes#meilisearch-errors
699+
"""
700+
response = self.http.delete(f"{self.config.paths.dynamic_search_rules}/{uid}")
701+
return response.status_code
702+
603703

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

meilisearch/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Paths:
5050
network = "network"
5151
experimental_features = "experimental-features"
5252
webhooks = "webhooks"
53+
dynamic_search_rules = "dynamic-search-rules"
5354
export = "export"
5455

5556
def __init__(
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from typing import Any, Dict, List, Optional
2+
3+
from camel_converter.pydantic_base import CamelBase
4+
from pydantic import ConfigDict
5+
6+
7+
class DynamicSearchRule(CamelBase):
8+
"""Model for a Meilisearch dynamic search rule."""
9+
10+
model_config = ConfigDict(arbitrary_types_allowed=True)
11+
12+
uid: str
13+
description: Optional[str] = None
14+
priority: Optional[int] = None
15+
active: Optional[bool] = None
16+
conditions: Optional[List[Dict[str, Any]]] = None
17+
actions: Optional[List[Dict[str, Any]]] = None
18+
19+
20+
class DynamicSearchRuleResults(CamelBase):
21+
"""Model for dynamic search rules list results."""
22+
23+
model_config = ConfigDict(arbitrary_types_allowed=True)
24+
results: List[DynamicSearchRule]
25+
offset: int
26+
limit: int
27+
total: int
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"""Tests for dynamic search rule management endpoints."""
2+
3+
import pytest
4+
5+
from meilisearch.errors import MeilisearchApiError
6+
7+
8+
def test_get_dynamic_search_rules_empty(client):
9+
"""Test getting dynamic search rules when none exist."""
10+
rules = client.get_dynamic_search_rules()
11+
assert rules.results is not None
12+
assert isinstance(rules.results, list)
13+
assert len(rules.results) == 0
14+
15+
16+
def test_create_or_update_dynamic_search_rule(client):
17+
"""Test creating a dynamic search rule."""
18+
rule_data = {
19+
"description": "Test rule for promotion",
20+
"priority": 10,
21+
"active": True,
22+
"conditions": [
23+
{"scope": "query", "isEmpty": True}
24+
],
25+
"actions": [
26+
{
27+
"selector": {"indexUid": "movies", "id": "123"},
28+
"action": {"type": "pin", "position": 1}
29+
}
30+
],
31+
}
32+
33+
rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)
34+
35+
assert rule.uid == "test-rule"
36+
assert rule.description == rule_data["description"]
37+
assert rule.priority == 10
38+
assert rule.active is True
39+
40+
41+
def test_get_dynamic_search_rule(client):
42+
"""Test getting a single dynamic search rule."""
43+
# Create a rule first
44+
rule_data = {
45+
"description": "Test rule",
46+
"active": True,
47+
}
48+
49+
created_rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)
50+
51+
# Get the rule
52+
rule = client.get_dynamic_search_rule(created_rule.uid)
53+
54+
assert rule.uid == created_rule.uid
55+
assert rule.description == rule_data["description"]
56+
57+
58+
def test_get_dynamic_search_rule_not_found(client):
59+
"""Test getting a dynamic search rule that doesn't exist."""
60+
with pytest.raises(MeilisearchApiError):
61+
client.get_dynamic_search_rule("non-existent-uid")
62+
63+
64+
def test_update_dynamic_search_rule(client):
65+
"""Test updating a dynamic search rule."""
66+
# Create a rule first
67+
rule_data = {
68+
"description": "Original description",
69+
"priority": 10,
70+
"active": True,
71+
"conditions": [
72+
{"scope": "query", "isEmpty": True}
73+
],
74+
"actions": [
75+
{
76+
"selector": {"indexUid": "movies", "id": "123"},
77+
"action": {"type": "pin", "position": 1}
78+
}
79+
],
80+
}
81+
82+
created_rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)
83+
84+
# Update the rule
85+
update_data = {
86+
"description": "Updated description",
87+
"priority": 5,
88+
}
89+
90+
updated_rule = client.create_or_update_dynamic_search_rule(created_rule.uid, update_data)
91+
92+
assert updated_rule.uid == created_rule.uid
93+
assert updated_rule.description == update_data["description"]
94+
assert updated_rule.priority == 5
95+
96+
97+
def test_delete_dynamic_search_rule(client):
98+
"""Test deleting a dynamic search rule."""
99+
# Create a rule first
100+
rule_data = {
101+
"description": "Rule to delete",
102+
"active": True,
103+
}
104+
105+
created_rule = client.create_or_update_dynamic_search_rule("test-rule", rule_data)
106+
107+
# Delete the rule
108+
status_code = client.delete_dynamic_search_rule(created_rule.uid)
109+
110+
assert status_code == 204
111+
112+
# Verify it's deleted
113+
with pytest.raises(MeilisearchApiError):
114+
client.get_dynamic_search_rule(created_rule.uid)
115+
116+
117+
def test_delete_dynamic_search_rule_not_found(client):
118+
"""Test deleting a dynamic search rule that doesn't exist."""
119+
with pytest.raises(MeilisearchApiError):
120+
client.delete_dynamic_search_rule("non-existent-uid")

0 commit comments

Comments
 (0)