Skip to content

Commit fc6e910

Browse files
authored
Fix a bug that the relevance cut-off featuer is not blocked for TENSOR or LEXICAL search (#1265)
1 parent 0284a32 commit fc6e910

File tree

6 files changed

+168
-8
lines changed

6 files changed

+168
-8
lines changed

src/marqo/tensor_search/models/api_models.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,10 @@ def get_context_documents(self) -> Optional[SearchContextDocuments]:
335335
@root_validator(pre=False)
336336
def _validate_relevance_cutoff_only_works_for_hybrid_search(cls, values):
337337
"""Validate that relevance cutoff is only provided for hybrid search"""
338-
relevance_cutoff = values.get('relevanceCutoff')
338+
relevance_cutoff = values.get('relevance_cutoff')
339339
search_method = values.get('searchMethod')
340340
if relevance_cutoff is not None and search_method.upper() != SearchMethod.HYBRID:
341-
raise ValueError(f"RelevanceCutoff can only be provided for 'HYBRID' search, but "
341+
raise ValueError(f"relevanceCutoff can only be provided for 'HYBRID' search, but "
342342
f"received search method '{search_method}'")
343343
return values
344344

tests/api_tests/v1/tests/api_tests/test_relevance_cutoff_feature.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,33 @@ def test_relevance_cutoff_basic_relative_max_score(self):
9393
ids = [hit["_id"] for hit in response["hits"]]
9494
self.assertEqual({'h1', 'h2'}, set(ids))
9595

96+
def test_relevance_cutoff_feature_is_blocked_for_lexical_or_tensor_search(self):
97+
"""
98+
Tests that relevance cutoff feature is blocked for lexical or tensor search.
99+
"""
100+
docs = [
101+
{"_id": "h1", "content": "Machine learning algorithms in artificial intelligence enable systems"},
102+
]
103+
self.client.index(self.unstructured_index_name).add_documents(
104+
docs, tensor_fields=["content"]
105+
)
106+
107+
for search_method in ["LEXICAL", "TENSOR"]:
108+
with self.subTest(f"Test sort by with search method {search_method}"):
109+
with self.assertRaises(MarqoWebError) as cm:
110+
self.client.index(self.unstructured_index_name).search(
111+
q="test",
112+
search_method=search_method,
113+
relevance_cutoff={
114+
"method": "gap_detection"
115+
},
116+
)
117+
118+
self.assertIn(
119+
f"relevanceCutoff can only be provided for",
120+
str(cm.exception)
121+
)
122+
96123
def test_relevance_cutoff_gap_detection_method(self):
97124
"""
98125
Tests relevance cutoff with gap_detection method.

tests/api_tests/v1/tests/api_tests/test_sort_by_feature.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,32 @@ def test_sort_by_is_blocked_by_structured_index(self):
6262
str(cm.exception)
6363
)
6464

65+
def test_sort_by_feature_is_blocked_for_lexical_or_tensor_search(self):
66+
"""
67+
Tests that sort by feature is blocked for lexical or tensor search.
68+
"""
69+
for search_method in ["LEXICAL", "TENSOR"]:
70+
with self.subTest(f"Test sort by with search method {search_method}"):
71+
with self.assertRaises(MarqoWebError) as cm:
72+
self.client.index(self.unstructured_index_name).search(
73+
q="test",
74+
search_method=search_method,
75+
sort_by={
76+
"fields": [
77+
{
78+
"fieldName": "title",
79+
"order": "asc",
80+
"missing": "last"
81+
}
82+
]
83+
}
84+
)
85+
86+
self.assertIn(
87+
f"sortBy can only be provided for",
88+
str(cm.exception)
89+
)
90+
6591
def test_sort_by_and_global_modifiers_can_not_be_used_together(self):
6692
"""
6793
Tests that sort by feature cannot be used with global modifiers.

tests/integ_tests/tensor_search/integ_tests/test_search_relevance_cutoff_feature.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import json
22
import pytest
33

4+
from fastapi.exceptions import RequestValidationError
5+
46
from marqo.core.exceptions import UnsupportedFeatureError
57
from marqo.core.models.add_docs_params import AddDocsParams
68
from marqo.core.models.marqo_index import *
@@ -234,6 +236,42 @@ def test_relevance_cutoff_is_blocked_by_structured_index(self):
234236
self.assertIn("The 'relevanceCutoff' feature is only supported for unstructured indexes created",
235237
str(context.exception))
236238

239+
def test_relevance_cutoff_is_blocked_by_tensor_search(self):
240+
"""Test that relevance cutoff is blocked for tensor or lexical search."""
241+
with self.assertRaises(RequestValidationError) as context:
242+
_ = search(
243+
index_name=self.unstructured_index_name,
244+
marqo_config=self.config,
245+
device="cpu",
246+
search_query_dict={
247+
"q": "machine learning artificial intelligence algorithms",
248+
"searchMethod": SearchMethod.TENSOR,
249+
"relevanceCutoff": {
250+
"method": "relative_max_score",
251+
"parameters": {"relativeScoreFactor": 0.5},
252+
},
253+
}
254+
)
255+
self.assertIn("relevanceCutoff can only be provided for", str(context.exception.errors()))
256+
257+
def test_relevance_cutoff_is_blocked_by_lexical_search(self):
258+
"""Test that relevance cutoff is blocked for lexical search."""
259+
with self.assertRaises(RequestValidationError) as context:
260+
_ = search(
261+
index_name=self.unstructured_index_name,
262+
marqo_config=self.config,
263+
device="cpu",
264+
search_query_dict={
265+
"q": "machine learning artificial intelligence algorithms",
266+
"searchMethod": SearchMethod.LEXICAL,
267+
"relevanceCutoff": {
268+
"method": "relative_max_score",
269+
"parameters": {"relativeScoreFactor": 0.5},
270+
},
271+
}
272+
)
273+
self.assertIn("relevanceCutoff can only be provided for", str(context.exception.errors()))
274+
237275
def test_relevance_cutoff_relative_max_score_low_threshold(self):
238276
"""Test that relative_max_score cutoff with low threshold."""
239277
result = self._search_helper(

tests/integ_tests/tensor_search/integ_tests/test_search_sort_by_feature.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import json
2+
import pytest
3+
import semver
4+
from unittest.mock import patch, MagicMock
25

6+
from fastapi.exceptions import RequestValidationError
7+
8+
from marqo.core.exceptions import UnsupportedFeatureError
39
from marqo.core.models.add_docs_params import AddDocsParams
410
from marqo.core.models.marqo_index import *
11+
from marqo.core.models.marqo_index import MarqoIndex
512
from marqo.tensor_search.api import search
613
from marqo.tensor_search.enums import SearchMethod
714
from tests.integ_tests.marqo_test import MarqoTestCase
8-
from marqo.core.exceptions import UnsupportedFeatureError
9-
from unittest.mock import patch, MagicMock
10-
from marqo.core.models.marqo_index import MarqoIndex
11-
import semver
12-
import pytest
1315

1416

1517
class TestSearchSortByFeature(MarqoTestCase):
@@ -243,6 +245,38 @@ def _help_sort_function(cls, query: Optional[str] = ' '.join([f"content{i}" for
243245
}
244246
).body.decode('utf-8'))
245247

248+
def test_sort_by_is_blocked_by_tensor_search(self):
249+
with self.assertRaises(RequestValidationError) as context:
250+
_ = search(
251+
index_name=self.index_name,
252+
marqo_config=self.config,
253+
device="cpu",
254+
search_query_dict={
255+
"q": "machine learning artificial intelligence algorithms",
256+
"searchMethod": SearchMethod.TENSOR,
257+
"sortBy": {
258+
"fields": [{"fieldName": "sort_field_1"}],
259+
}
260+
}
261+
)
262+
self.assertIn("sortBy can only be provided for", str(context.exception.errors()))
263+
264+
def test_sort_by_is_blocked_by_lexical_search(self):
265+
with self.assertRaises(RequestValidationError) as context:
266+
_ = search(
267+
index_name=self.index_name,
268+
marqo_config=self.config,
269+
device="cpu",
270+
search_query_dict={
271+
"q": "machine learning artificial intelligence algorithms",
272+
"searchMethod": SearchMethod.LEXICAL,
273+
"sortBy": {
274+
"fields": [{"fieldName": "sort_field_1"}],
275+
}
276+
}
277+
)
278+
self.assertIn("sortBy can only be provided for", str(context.exception.errors()))
279+
246280
def test_simple_sort_with_default_settings(self):
247281
"""
248282
The simple sort test check based on default values:

tests/unit_tests/marqo/api/models/test_relevance_cutoff_model.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
MeanStdParameters,
99
RelevanceCutoffModel
1010
)
11+
from marqo.tensor_search.models.api_models import SearchQuery
12+
from marqo.tensor_search.enums import SearchMethod
1113

1214

1315
class TestRelevanceCutoffModel(TestCase):
@@ -94,4 +96,37 @@ def test_relative_score_parameter_constraints(self):
9496
def test_std_dev_parameter_constraints(self):
9597
# stdDevFactor must be a numeric value
9698
with self.assertRaises(ValidationError):
97-
MeanStdParameters(stdDevFactor="test")
99+
MeanStdParameters(stdDevFactor="test")
100+
101+
def test_relevance_cutoff_with_tensor_search_fails(self):
102+
"""Test that relevance cutoff fails with TENSOR search method"""
103+
relevance_cutoff = RelevanceCutoffModel(
104+
method=RelevanceCutoffMethod.RelativeMaxScore,
105+
parameters=RelativeMaxScoreParameters(relativeScoreFactor=0.75)
106+
)
107+
108+
with self.assertRaises(ValidationError) as cm:
109+
SearchQuery(
110+
q="test query",
111+
searchMethod=SearchMethod.TENSOR,
112+
relevanceCutoff=relevance_cutoff
113+
)
114+
115+
self.assertIn("relevanceCutoff can only be provided for 'HYBRID' search", str(cm.exception))
116+
self.assertIn("TENSOR", str(cm.exception))
117+
118+
def test_relevance_cutoff_with_lexical_search_fails(self):
119+
"""Test that relevance cutoff fails with LEXICAL search method"""
120+
relevance_cutoff = RelevanceCutoffModel(
121+
method=RelevanceCutoffMethod.GapDetection
122+
)
123+
124+
with self.assertRaises(ValidationError) as cm:
125+
SearchQuery(
126+
q="test query",
127+
searchMethod=SearchMethod.LEXICAL,
128+
relevanceCutoff=relevance_cutoff
129+
)
130+
131+
self.assertIn("relevanceCutoff can only be provided for 'HYBRID' search", str(cm.exception))
132+
self.assertIn("LEXICAL", str(cm.exception))

0 commit comments

Comments
 (0)