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