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
4 changes: 4 additions & 0 deletions meilisearch/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ def multi_search(
https://www.meilisearch.com/docs/reference/api/network
Each query may include showPerformanceDetails to request a performance
trace in the response (results[].performanceDetails, returned as raw data).
Each query may include inlineRule to preview a dynamic search rule
(Meilisearch Enterprise, dynamicSearchRules). Create the client with
custom_headers={"Meili-Include-Metadata": "true"} to include
metadata.inlineRuleConditionOutcomes on queries that send inlineRule.
federation: (optional):
Dictionary containing offset, limit, and optionally showPerformanceDetails
for federated search (top-level performanceDetails in the response).
Expand Down
7 changes: 7 additions & 0 deletions meilisearch/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -367,12 +367,19 @@ def search(self, query: str, opt_params: Mapping[str, Any] | None = None) -> dic
- offset: Number of documents to skip
- showPerformanceDetails: If true, the response includes a
performanceDetails object (raw data; fields may change in future API versions)
- inlineRule: Dynamic search rule applied only to this query.
Requires Meilisearch Enterprise and the dynamicSearchRules
experimental feature. Create the client with
custom_headers={"Meili-Include-Metadata": "true"} to include
metadata.inlineRuleConditionOutcomes in the response.

Returns
-------
results:
Dictionary with hits, offset, limit, processingTime and initial query.
When showPerformanceDetails is true, may include a performanceDetails object.
When Meili-Include-Metadata is true, may include metadata, with
inlineRuleConditionOutcomes when the query sends inlineRule.

Raises
------
Expand Down
116 changes: 116 additions & 0 deletions tests/index/test_inline_rule_meilisearch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""Tests for inline dynamic search rule preview on search and multi-search."""

import pytest

import meilisearch
from tests import common

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

INDEX_UID = "inline_rule_preview"
TASK_TIMEOUT_MS = 30_000

DOCUMENTS = [
{
"id": "1",
"title": "super hero",
"hero": "superman",
"color": "blue",
"category": "shirt",
"stock_intent": "keep",
},
{
"id": "2",
"title": "super hero",
"hero": "batman",
"color": "yellow",
"category": "pants",
"stock_intent": "destock",
},
]


@pytest.fixture
def inline_rule_index(client):
"""Index with the attributes used by inline rule preview."""
task = client.create_index(INDEX_UID, {"primaryKey": "id"})
client.wait_for_task(task.task_uid, timeout_in_ms=TASK_TIMEOUT_MS)
index = client.index(INDEX_UID)
settings_task = index.update_filterable_attributes(
["color", "category", "stock_intent", "hero"]
)
index.wait_for_task(settings_task.task_uid, timeout_in_ms=TASK_TIMEOUT_MS)
documents_task = index.add_documents(DOCUMENTS)
index.wait_for_task(documents_task.task_uid, timeout_in_ms=TASK_TIMEOUT_MS)
return index


@pytest.fixture
def metadata_client():
"""Client that asks Meilisearch to include search metadata."""
return meilisearch.Client(
common.BASE_URL,
common.MASTER_KEY,
custom_headers={"Meili-Include-Metadata": "true"},
)


def test_search_with_inline_rule_pins_document_and_returns_condition_outcomes(metadata_client):
"""A satisfied inline pin rule reorders hits and reports condition outcomes."""
response = metadata_client.index(INDEX_UID).search(
"super hero",
{
"inlineRule": {
"uid": "user-likes-batman",
"conditions": {"query": {"words": "hero"}},
"actions": {"pin": [{"id": "2", "position": 0}]},
}
},
)

assert response["hits"][0]["id"] == "2"
metadata = response["metadata"]
assert isinstance(metadata["queryUid"], str)
assert metadata["indexUid"] == INDEX_UID
assert metadata["primaryKey"] == "id"
assert metadata["inlineRuleConditionOutcomes"] == {
"satisfiesActiveCondition": True,
"satisfiesQueryWordsCondition": "satisfied",
}


def test_multi_search_with_inline_rule_returns_condition_outcomes(metadata_client):
"""Only the query that sends inlineRule includes condition outcomes."""
query = {
"indexUid": INDEX_UID,
"q": "super hero",
"filter": "color = blue AND category = shirt",
}
response = metadata_client.multi_search(
[
query,
{
**query,
"inlineRule": {
"uid": "special-sales",
"conditions": {
"query": {"words": "pants"},
"filter": {
"values": {
"color": "yellow",
"stock_intent": "destock",
}
},
},
},
},
]
)

assert response["results"][0]["hits"] == response["results"][1]["hits"]
assert "inlineRuleConditionOutcomes" not in response["results"][0].get("metadata", {})
assert response["results"][1]["metadata"]["inlineRuleConditionOutcomes"] == {
"satisfiesActiveCondition": True,
"satisfiesQueryWordsCondition": {"missingWord": {"word": "pants"}},
"satisfiesFilterCondition": {"unmetConstraintOnField": {"field": "color"}},
}
Comment thread
Strift marked this conversation as resolved.
Loading