diff --git a/src/caselawclient/client_helpers/search_helpers.py b/src/caselawclient/client_helpers/search_helpers.py
index 4e70b200..cc4726f9 100644
--- a/src/caselawclient/client_helpers/search_helpers.py
+++ b/src/caselawclient/client_helpers/search_helpers.py
@@ -1,3 +1,5 @@
+from lxml import etree
+
from caselawclient.Client import MarklogicApiClient
from caselawclient.responses.search_response import SearchResponse
from caselawclient.search_parameters import SearchParameters
@@ -14,8 +16,11 @@ def search_judgments_and_parse_response(
:return: The parsed search response as a SearchResponse object
"""
- return SearchResponse.from_response_string(
- api_client.search_judgments_and_decode_response(search_parameters)
+ return SearchResponse(
+ etree.fromstring(
+ api_client.search_judgments_and_decode_response(search_parameters)
+ ),
+ api_client,
)
@@ -30,6 +35,7 @@ def search_and_parse_response(
:return: The parsed search response as a SearchResponse object
"""
- return SearchResponse.from_response_string(
- api_client.search_and_decode_response(search_parameters)
+ return SearchResponse(
+ etree.fromstring(api_client.search_and_decode_response(search_parameters)),
+ api_client,
)
diff --git a/src/caselawclient/responses/search_response.py b/src/caselawclient/responses/search_response.py
index 51c57162..f5cf034c 100644
--- a/src/caselawclient/responses/search_response.py
+++ b/src/caselawclient/responses/search_response.py
@@ -2,6 +2,7 @@
from lxml import etree
+from caselawclient.Client import MarklogicApiClient
from caselawclient.responses.search_result import SearchResult
@@ -13,22 +14,14 @@ class SearchResponse:
NAMESPACES = {"search": "http://marklogic.com/appservices/search"}
""" Namespaces used in XPath expressions."""
- def __init__(self, node: etree._Element) -> None:
+ def __init__(self, node: etree._Element, client: MarklogicApiClient) -> None:
"""
Initializes a SearchResponse instance from an xml node.
:param node: The XML data as an etree element
"""
self.node = node
-
- @staticmethod
- def from_response_string(xml: str) -> "SearchResponse":
- """
- Constructs a SearchResponse instance from an xml response string.
-
- :param xml: The XML data as a string
- """
- return SearchResponse(etree.fromstring(xml))
+ self.client = client
@property
def total(self) -> str:
@@ -51,9 +44,4 @@ def results(self) -> List[SearchResult]:
results = self.node.xpath(
"//search:response/search:result", namespaces=self.NAMESPACES
)
- return [
- SearchResult(
- result,
- )
- for result in results
- ]
+ return [SearchResult(result, self.client) for result in results]
diff --git a/src/caselawclient/responses/search_result.py b/src/caselawclient/responses/search_result.py
index 5bafc45c..629409b7 100644
--- a/src/caselawclient/responses/search_result.py
+++ b/src/caselawclient/responses/search_result.py
@@ -2,6 +2,7 @@
import os
from datetime import datetime
from enum import Enum
+from functools import cached_property
from typing import Dict, Optional
from dateutil import parser as dateparser
@@ -9,7 +10,7 @@
from ds_caselaw_utils.courts import Court, CourtNotFoundException, courts
from lxml import etree
-from caselawclient.Client import api_client
+from caselawclient.Client import MarklogicApiClient
from caselawclient.models.documents import DocumentURIString
from caselawclient.xml_helpers import get_xpath_match_string
@@ -44,20 +45,6 @@ def __init__(self, node: etree._Element, last_modified: str):
self.node = node
self.last_modified = last_modified
- @staticmethod
- def create_from_uri(uri: DocumentURIString) -> "SearchResultMetadata":
- """
- Create a SearchResultMetadata instance from a search result URI.
-
- :param uri: The URI of the search result
-
- :return: The created SearchResultMetadata instance
- """
- response_text = api_client.get_properties_for_search_results([uri])
- last_modified = api_client.get_last_modified(uri)
- root = etree.fromstring(response_text)
- return SearchResultMetadata(root, last_modified)
-
@property
def author(self) -> str:
"""
@@ -162,12 +149,13 @@ class SearchResult:
}
""" Namespace mappings used in XPath expressions. """
- def __init__(self, node: etree._Element):
+ def __init__(self, node: etree._Element, client: MarklogicApiClient):
"""
:param node: The XML element representing the search result
"""
self.node = node
+ self.client = client
@property
def uri(self) -> DocumentURIString:
@@ -259,15 +247,15 @@ def matches(self) -> str:
xslt_transform = etree.XSLT(etree.parse(file_path))
return str(xslt_transform(self.node))
- @property
+ @cached_property
def metadata(self) -> SearchResultMetadata:
"""
- :return: The metadata of the search result
+ :return: A `SearchResultMetadata` instance representing the metadata of this result
"""
-
- return SearchResultMetadata.create_from_uri(
- self.uri,
- )
+ response_text = self.client.get_properties_for_search_results([self.uri])
+ last_modified = self.client.get_last_modified(self.uri)
+ root = etree.fromstring(response_text)
+ return SearchResultMetadata(root, last_modified)
def _get_xpath_match_string(self, path: str) -> str:
return get_xpath_match_string(self.node, path, namespaces=self.NAMESPACES)
diff --git a/tests/client/test_search_and_decode_response.py b/tests/client/test_search_and_decode_response.py
index 62a1a515..eb235e91 100644
--- a/tests/client/test_search_and_decode_response.py
+++ b/tests/client/test_search_and_decode_response.py
@@ -1,61 +1,71 @@
from unittest.mock import patch
-from caselawclient.Client import api_client
+from caselawclient.Client import MarklogicApiClient
from caselawclient.search_parameters import SearchParameters
-@patch("caselawclient.Client.api_client.advanced_search")
-def test_search_judgments_and_decode_response(
- mock_advanced_search,
- valid_search_response_xml,
- generate_mock_search_response,
-):
- """
- Given the search parameters for search_judgments_and_decode_response are valid
- And a mocked api_client response with search results
- When search_judgments_and_decode_response function is called with the mocked API client
- and input parameters
- Then the API client's advanced_search method should be called once with the
- appropriate parameters
- And the search_judgments_and_decode_response function should return the response
- xml string with all the search results
- """
- mock_advanced_search.return_value = generate_mock_search_response(
- valid_search_response_xml
- )
-
- search_response = api_client.search_judgments_and_decode_response(
- SearchParameters(
- query="test query",
- court="test court",
- judge="test judge",
- party="test party",
- neutral_citation="test citation",
- specific_keyword="test keyword",
- date_from="2022-01-01",
- date_to="2022-01-31",
- page=1,
- page_size=10,
+class TestSearchAndDecodeResponse:
+ def setup_method(self):
+ self.client = MarklogicApiClient(
+ host="",
+ username="",
+ password="",
+ use_https=False,
+ user_agent="marklogic-api-client-test",
)
- )
- mock_advanced_search.assert_called_once_with(
- SearchParameters(
- query="test query",
- court="test court",
- judge="test judge",
- party="test party",
- neutral_citation="test citation",
- specific_keyword="test keyword",
- order=None,
- date_from="2022-01-01",
- date_to="2022-01-31",
- page=1,
- page_size=10,
- show_unpublished=False,
- only_unpublished=False,
- collections=["judgment"],
- )
- )
+ def test_search_judgments_and_decode_response(
+ self,
+ valid_search_response_xml,
+ generate_mock_search_response,
+ ):
+ """
+ Given the search parameters for search_judgments_and_decode_response are valid
+ And a mocked MarklogicApiClient.advanced_search response with search results
+ When search_judgments_and_decode_response function is called with the mocked API client
+ and input parameters
+ Then the API client's advanced_search method should be called once with the
+ appropriate parameters
+ And the search_judgments_and_decode_response function should return the response
+ xml string with all the search results
+ """
+ with patch.object(self.client, "advanced_search") as mock_advanced_search:
+ mock_advanced_search.return_value = generate_mock_search_response(
+ valid_search_response_xml
+ )
+
+ search_response = self.client.search_judgments_and_decode_response(
+ SearchParameters(
+ query="test query",
+ court="test court",
+ judge="test judge",
+ party="test party",
+ neutral_citation="test citation",
+ specific_keyword="test keyword",
+ date_from="2022-01-01",
+ date_to="2022-01-31",
+ page=1,
+ page_size=10,
+ )
+ )
+
+ mock_advanced_search.assert_called_once_with(
+ SearchParameters(
+ query="test query",
+ court="test court",
+ judge="test judge",
+ party="test party",
+ neutral_citation="test citation",
+ specific_keyword="test keyword",
+ order=None,
+ date_from="2022-01-01",
+ date_to="2022-01-31",
+ page=1,
+ page_size=10,
+ show_unpublished=False,
+ only_unpublished=False,
+ collections=["judgment"],
+ )
+ )
- assert search_response == valid_search_response_xml
+ assert search_response == valid_search_response_xml
diff --git a/tests/responses/test_search_response.py b/tests/responses/test_search_response.py
index 4227d8eb..c99a2369 100644
--- a/tests/responses/test_search_response.py
+++ b/tests/responses/test_search_response.py
@@ -1,10 +1,20 @@
import pytest
from lxml import etree
+from caselawclient.Client import MarklogicApiClient
from caselawclient.responses.search_response import SearchResponse
class TestSearchResponse:
+ def setup_method(self):
+ self.client = MarklogicApiClient(
+ host="",
+ username="",
+ password="",
+ use_https=False,
+ user_agent="marklogic-api-client-test",
+ )
+
def test_total(
self,
):
@@ -13,10 +23,13 @@ def test_total(
When calling 'total' on it
Then it should return a string representing the total number of results
"""
- search_response = SearchResponse.from_response_string(
- '
" - "text from the document that matched the search
\n" - ) - assert search_result.content_hash == "test_content_hash" - assert search_result.transformation_date == "2023-04-09T18:05:45" - assert etree.tostring(search_result.metadata.node).decode() == "" + "text from the document that matched the search
\n" + ) + assert search_result.content_hash == "test_content_hash" + assert search_result.transformation_date == "2023-04-09T18:05:45" + assert etree.tostring(search_result.metadata.node).decode() == "