From a7d081b7f88fdcd6082d1ad80742a08ab84c6eaf Mon Sep 17 00:00:00 2001 From: Li Wan Date: Thu, 19 Jun 2025 13:17:37 +1000 Subject: [PATCH 1/7] Added sort by --- src/marqo/index.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/marqo/index.py b/src/marqo/index.py index 77954043..ed044683 100644 --- a/src/marqo/index.py +++ b/src/marqo/index.py @@ -225,6 +225,7 @@ def search(self, q: Optional[Union[str, dict]] = None, searchable_attributes: Op rerank_depth: Optional[int] = None, facets: Optional[dict] = None, track_total_hits: Optional[bool] = None, approximate_threshold: Optional[float] = None, + sort_by: Optional[dict] = None ) -> Dict[str, Any]: """Search the index. @@ -268,6 +269,7 @@ def search(self, q: Optional[Union[str, dict]] = None, searchable_attributes: Op track_total_hits: return total number of lexical matches approximate_threshold: hit ratio threshold for deciding if a nearest neighbor search should be performed as an exact search, rather than an approximate search + sort_by: a dictionary of the sort_by parameters to be used for sorting the results. Returns: Dictionary with hits and other metadata @@ -306,7 +308,8 @@ def search(self, q: Optional[Union[str, dict]] = None, searchable_attributes: Op "textQueryPrefix": text_query_prefix, "hybridParameters": hybrid_parameters, "facets": facets, - "trackTotalHits": track_total_hits + "trackTotalHits": track_total_hits, + "sortBy": sort_by } body = {k: v for k, v in body.items() if v is not None} From 8c6c140bbfdf5ebc31d439c4c8e939ed32448591 Mon Sep 17 00:00:00 2001 From: Li Wan Date: Mon, 23 Jun 2025 19:54:36 +1000 Subject: [PATCH 2/7] Add sort-by parameters --- src/marqo/index.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/marqo/index.py b/src/marqo/index.py index ed044683..cc5929ea 100644 --- a/src/marqo/index.py +++ b/src/marqo/index.py @@ -225,7 +225,8 @@ def search(self, q: Optional[Union[str, dict]] = None, searchable_attributes: Op rerank_depth: Optional[int] = None, facets: Optional[dict] = None, track_total_hits: Optional[bool] = None, approximate_threshold: Optional[float] = None, - sort_by: Optional[dict] = None + sort_by: Optional[dict] = None, + relevance_cutoff: Optional[dict] = None ) -> Dict[str, Any]: """Search the index. @@ -269,7 +270,8 @@ def search(self, q: Optional[Union[str, dict]] = None, searchable_attributes: Op track_total_hits: return total number of lexical matches approximate_threshold: hit ratio threshold for deciding if a nearest neighbor search should be performed as an exact search, rather than an approximate search - sort_by: a dictionary of the sort_by parameters to be used for sorting the results. + sort_by: a dictionary of the sort_by parameters to be used for sorting the results + relevance_cutoff: a dictionary of the relevance cutoff parameters Returns: Dictionary with hits and other metadata @@ -309,7 +311,8 @@ def search(self, q: Optional[Union[str, dict]] = None, searchable_attributes: Op "hybridParameters": hybrid_parameters, "facets": facets, "trackTotalHits": track_total_hits, - "sortBy": sort_by + "sortBy": sort_by, + "relevanceCutoff": relevance_cutoff } body = {k: v for k, v in body.items() if v is not None} From 171cf63738796a327fd5333b009de88fb8658d10 Mon Sep 17 00:00:00 2001 From: Li Wan Date: Wed, 9 Jul 2025 20:27:43 +1000 Subject: [PATCH 3/7] Add a unit tests workflow --- .github/workflows/unit-tests.yml | 41 +++++++ unit_tests/__init__.py | 0 unit_tests/marqo_unit_tests.py | 5 + unit_tests/test_index_search_endpoint.py | 132 +++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 .github/workflows/unit-tests.yml create mode 100644 unit_tests/__init__.py create mode 100644 unit_tests/marqo_unit_tests.py create mode 100644 unit_tests/test_index_search_endpoint.py diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 00000000..e2f39f83 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,41 @@ + + +name: Py-marqo Unit Tests +run-name: Py-marqo unit tests + +on: + workflow_dispatch: + pull_request: + branches: + - mainline + - releases/* + push: + branches: + - mainline + - releases/* + +permissions: + contents: read + +jobs: + test: + name: Run Pytest + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.9 + + - name: Install dependencies + run: pip install requirements.dev.txt + + - name: Run UnitTests + run: | + cd py-marqo + export PYTHONPATH="${PYTHONPATH}:$(pwd)/src" + pytest tests/unit_tests \ No newline at end of file diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/unit_tests/marqo_unit_tests.py b/unit_tests/marqo_unit_tests.py new file mode 100644 index 00000000..03a6741c --- /dev/null +++ b/unit_tests/marqo_unit_tests.py @@ -0,0 +1,5 @@ +from unittest import TestCase + + +class MarqoUnitTests(TestCase): + pass \ No newline at end of file diff --git a/unit_tests/test_index_search_endpoint.py b/unit_tests/test_index_search_endpoint.py new file mode 100644 index 00000000..ae188aad --- /dev/null +++ b/unit_tests/test_index_search_endpoint.py @@ -0,0 +1,132 @@ +from unittest.mock import patch, MagicMock + +from marqo.config import Config +from marqo.default_instance_mappings import DefaultInstanceMappings +from marqo.index import Index +from unit_tests.marqo_unit_tests import MarqoUnitTests + + +class TestIndexSearchEndpointSortBy(MarqoUnitTests): + """ + Test class for the Index search endpoint sort_by parameter. + """ + + @classmethod + def setUpClass(cls): + """ + Set up the class by creating a test index. + """ + config = Config( + instance_mappings=DefaultInstanceMappings(url="http://unit-tests-url:8882"), + ) + cls.index = Index(config=config, index_name="test_index") + + @patch('marqo._httprequests.HttpRequests.send_request') + def test_search_by_parameters_one_fields(self, mock_send_request): + mock_response = MagicMock() + mock_send_request.return_value = mock_response + sort_by = {"fields":[{"fieldName": "price", "order": "asc"}]} + self.index.search(q= "test", sort_by= sort_by) + + self.assertEqual(1, mock_send_request.call_count) + body = mock_send_request.call_args[0][2] + self.assertEqual({"fields": [{"fieldName": "price", "order": "asc"}]}, body["sortBy"]) + + @patch('marqo._httprequests.HttpRequests.send_request') + def test_search_by_parameters_three_fields(self, mock_send_request): + mock_response = MagicMock() + mock_send_request.return_value = mock_response + + sort_by = { + "fields": [ + {"fieldName": "price", "order": "asc"}, + {"fieldName": "rating", "order": "desc"}, + {"fieldName": "date", "order": "asc"} + ] + } + + self.index.search(q= "test", sort_by=sort_by) + + self.assertEqual(1, mock_send_request.call_count) + body = mock_send_request.call_args[0][2] + self.assertEqual(sort_by, body["sortBy"]) + + @patch('marqo._httprequests.HttpRequests.send_request') + def test_search_by_parameters_none(self, mock_send_request): + mock_response = MagicMock() + mock_send_request.return_value = mock_response + + sort_by = None + + self.index.search(q= "test", sort_by=sort_by) + + self.assertEqual(1, mock_send_request.call_count) + body = mock_send_request.call_args[0][2] + self.assertNotIn("sortBy", body) + + @patch('marqo._httprequests.HttpRequests.send_request') + def test_search_by_parameters_not_exists(self, mock_send_request): + mock_response = MagicMock() + mock_send_request.return_value = mock_response + + self.index.search(q= "test") + + self.assertEqual(1, mock_send_request.call_count) + body = mock_send_request.call_args[0][2] + self.assertNotIn("sortBy", body) + + +class TestIndexSearchEndpointRelevanceCutoff(MarqoUnitTests): + """ + Test class for the Index search endpoint relevance_cutoff parameter. + """ + @classmethod + def setUpClass(cls): + """ + Set up the class by creating a test index. + """ + config = Config( + instance_mappings=DefaultInstanceMappings(url="http://unit-tests-url:8882"), + ) + cls.index = Index(config=config, index_name="test_index") + + @patch('marqo._httprequests.HttpRequests.send_request') + def test_relevance_cutoff_parameter(self, mock_send_request): + mock_response = MagicMock() + mock_send_request.return_value = mock_response + + relevance_cutoff = { + "method": "mean_std_dev", + "parameters": { + "stdDevFactor": 1.0, + }, + "probeDepth": 1000 + } + + self.index.search(q= "test", relevance_cutoff=relevance_cutoff) + + self.assertEqual(1, mock_send_request.call_count) + body = mock_send_request.call_args[0][2] + self.assertEqual(relevance_cutoff, body["relevanceCutoff"]) + + @patch('marqo._httprequests.HttpRequests.send_request') + def test_relevance_cutoff_parameter_none(self, mock_send_request): + mock_response = MagicMock() + mock_send_request.return_value = mock_response + + relevance_cutoff = None + + self.index.search(q= "test", relevance_cutoff=relevance_cutoff) + body = mock_send_request.call_args[0][2] + self.assertNotIn("relevanceCutoff", body) + + @patch('marqo._httprequests.HttpRequests.send_request') + def test_relevance_cutoff_parameter_not_exists(self, mock_send_request): + mock_response = MagicMock() + mock_send_request.return_value = mock_response + + self.index.search(q= "test") + + self.assertEqual(1, mock_send_request.call_count) + body = mock_send_request.call_args[0][2] + self.assertNotIn("relevanceCutoff", body) From 78b7d60e11110e1973aba3415c4eb2d15d61e342 Mon Sep 17 00:00:00 2001 From: Li Wan Date: Wed, 9 Jul 2025 20:28:19 +1000 Subject: [PATCH 4/7] Add a unit tests workflow --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index e2f39f83..170735e7 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -32,7 +32,7 @@ jobs: python-version: 3.9 - name: Install dependencies - run: pip install requirements.dev.txt + run: pip install -r requirements.dev.txt - name: Run UnitTests run: | From 67cb2db1a051ac211afe6886f459edf40ce8b65e Mon Sep 17 00:00:00 2001 From: Li Wan Date: Wed, 9 Jul 2025 20:29:38 +1000 Subject: [PATCH 5/7] Add a unit tests workflow --- .github/workflows/unit-tests.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 170735e7..f8614b19 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -32,10 +32,11 @@ jobs: python-version: 3.9 - name: Install dependencies + working-directory: py-marqo run: pip install -r requirements.dev.txt - name: Run UnitTests + working-directory: py-marqo run: | - cd py-marqo export PYTHONPATH="${PYTHONPATH}:$(pwd)/src" pytest tests/unit_tests \ No newline at end of file From 867922d91838834cd9606e889be2c0a711f1b2cc Mon Sep 17 00:00:00 2001 From: Li Wan Date: Wed, 9 Jul 2025 20:31:26 +1000 Subject: [PATCH 6/7] Add a unit tests workflow --- .github/workflows/unit-tests.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f8614b19..a2c32f50 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -32,11 +32,9 @@ jobs: python-version: 3.9 - name: Install dependencies - working-directory: py-marqo - run: pip install -r requirements.dev.txt + run: pip install -r requirements-dev.txt - name: Run UnitTests - working-directory: py-marqo run: | export PYTHONPATH="${PYTHONPATH}:$(pwd)/src" pytest tests/unit_tests \ No newline at end of file From ea76ec74d8bb75c8844957aceaf1173e9da6164a Mon Sep 17 00:00:00 2001 From: Li Wan Date: Wed, 9 Jul 2025 20:32:03 +1000 Subject: [PATCH 7/7] Add a unit tests workflow --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index a2c32f50..ea582edd 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -37,4 +37,4 @@ jobs: - name: Run UnitTests run: | export PYTHONPATH="${PYTHONPATH}:$(pwd)/src" - pytest tests/unit_tests \ No newline at end of file + pytest unit_tests \ No newline at end of file