diff --git a/backend/src/monarch_py/datamodels/solr.py b/backend/src/monarch_py/datamodels/solr.py index 541aaed3..841d3f98 100644 --- a/backend/src/monarch_py/datamodels/solr.py +++ b/backend/src/monarch_py/datamodels/solr.py @@ -45,6 +45,10 @@ class SolrQuery(BaseModel): facet_queries: Optional[List[str]] = Field(default_factory=list) filter_queries: Optional[List[str]] = Field(default_factory=list) facet_mincount: int = 1 + group: bool = False + group_queries: Optional[List[str]] = Field(default_factory=list) + group_limit: int = 5 + group_offset: int = 0 query_fields: Optional[str] = None def_type: str = "edismax" q_op: str = "AND" # See SOLR-8812, need this plus mm=100% to allow boolean operators in queries @@ -88,6 +92,12 @@ def _solrize(self, value): return "fq" elif value == "facet_mincount": return "facet.mincount" + elif value == "group_queries": + return "group.query" + elif value == "group_limit": + return "group.limit" + elif value == "group_offset": + return "group.offset" elif value == "query_fields": return "qf" elif value == "def_type": @@ -118,7 +128,18 @@ class SolrFacetCounts(BaseModel): facet_queries: Optional[Dict] +class SolrQueryGroup(BaseModel): + matches: int + doclist: SolrQueryResponse + + class SolrQueryResult(BaseModel): responseHeader: SolrQueryResponseHeader response: SolrQueryResponse facet_counts: Optional[SolrFacetCounts] + + +class SolrGroupedQueryResult(BaseModel): + responseHeader: SolrQueryResponseHeader + grouped: Dict[str, SolrQueryGroup] + facet_counts: Optional[SolrFacetCounts] diff --git a/backend/src/monarch_py/service/solr_service.py b/backend/src/monarch_py/service/solr_service.py index 8793c46b..6e969fe2 100644 --- a/backend/src/monarch_py/service/solr_service.py +++ b/backend/src/monarch_py/service/solr_service.py @@ -3,7 +3,7 @@ import requests from loguru import logger -from monarch_py.datamodels.solr import SolrQuery, SolrQueryResult, core +from monarch_py.datamodels.solr import SolrGroupedQueryResult, SolrQuery, SolrQueryResult, core from monarch_py.utils.utils import escape from pydantic import BaseModel @@ -23,6 +23,28 @@ def get(self, id): return None return entity + def group_query(self, q: SolrQuery) -> SolrGroupedQueryResult: + url = f"{self.base_url}/{self.core.value}/select?{q.query_string()}" + response = requests.get(url) + logger.debug(f"SolrService.query: {url}") + data = json.loads(response.text) + if "error" in data: + logger.error("Solr error message: " + data["error"]["msg"]) + response.raise_for_status() + solr_query_result = SolrGroupedQueryResult.model_validate(data, from_attributes=True) + for group in solr_query_result.grouped.values(): + for doc in group.doclist.docs: + self._strip_json( + doc, + "_version_", + "iri", + "frequency_computed_sortable_float", + "has_quotient_sortable_float", + "has_percentage_sortable_float", + ) + + return solr_query_result + def query(self, q: SolrQuery) -> SolrQueryResult: url = f"{self.base_url}/{self.core.value}/select?{q.query_string()}" response = requests.get(url) diff --git a/backend/tests/fixtures/association_counts_query.py b/backend/tests/fixtures/association_counts_query.py index 848ec8ba..912beae9 100644 --- a/backend/tests/fixtures/association_counts_query.py +++ b/backend/tests/fixtures/association_counts_query.py @@ -7,6 +7,10 @@ def association_counts_query(): "q": "*:*", "rows": 20, "start": 0, + "group": False, + "group_limit": 5, + "group_offset": 0, + "group_queries": [], "facet": True, "facet_min_count": 1, "facet_fields": [], diff --git a/backend/tests/fixtures/association_query_direct.py b/backend/tests/fixtures/association_query_direct.py index b8c2c2ed..d0ff7545 100644 --- a/backend/tests/fixtures/association_query_direct.py +++ b/backend/tests/fixtures/association_query_direct.py @@ -7,6 +7,10 @@ def association_query_direct(): "q": "test:q", "rows": 100, "start": 100, + "group": False, + "group_limit": 5, + "group_offset": 0, + "group_queries": [], "facet": True, "facet_min_count": 1, "facet_fields": [], diff --git a/backend/tests/fixtures/association_query_indirect.py b/backend/tests/fixtures/association_query_indirect.py index 5e8503e9..8e6cef2f 100644 --- a/backend/tests/fixtures/association_query_indirect.py +++ b/backend/tests/fixtures/association_query_indirect.py @@ -7,6 +7,10 @@ def association_query_indirect(): "q": "test:q", "rows": 100, "start": 100, + "group": False, + "group_limit": 5, + "group_offset": 0, + "group_queries": [], "facet": True, "facet_min_count": 1, "facet_fields": [], diff --git a/backend/tests/fixtures/autocomplete_query.py b/backend/tests/fixtures/autocomplete_query.py index 733a5be2..4b45f4b6 100644 --- a/backend/tests/fixtures/autocomplete_query.py +++ b/backend/tests/fixtures/autocomplete_query.py @@ -7,6 +7,10 @@ def autocomplete_query(): "q": "fanc", "rows": 10, "start": 0, + "group": False, + "group_limit": 5, + "group_offset": 0, + "group_queries": [], "facet": True, "facet_min_count": 1, "facet_fields": [], diff --git a/backend/tests/fixtures/histopheno_query.py b/backend/tests/fixtures/histopheno_query.py index e2d3dfb4..33eeb7f0 100644 --- a/backend/tests/fixtures/histopheno_query.py +++ b/backend/tests/fixtures/histopheno_query.py @@ -7,6 +7,10 @@ def histopheno_query(): "q": "*:*", "rows": 0, "start": 0, + "group": False, + "group_limit": 5, + "group_offset": 0, + "group_queries": [], "facet": True, "facet_min_count": 1, "facet_fields": [], diff --git a/backend/tests/fixtures/mapping_query.py b/backend/tests/fixtures/mapping_query.py index b24fa2ac..b35abb9b 100644 --- a/backend/tests/fixtures/mapping_query.py +++ b/backend/tests/fixtures/mapping_query.py @@ -7,6 +7,10 @@ def mapping_query(): "q": "*:*", "rows": 20, "start": 0, + "group": False, + "group_limit": 5, + "group_offset": 0, + "group_queries": [], "facet": True, "facet_min_count": 1, "facet_fields": [], diff --git a/backend/tests/fixtures/search_query.py b/backend/tests/fixtures/search_query.py index 159fcaf1..4a4c2733 100644 --- a/backend/tests/fixtures/search_query.py +++ b/backend/tests/fixtures/search_query.py @@ -7,6 +7,10 @@ def search_query(): "q": "fanconi", "rows": 20, "start": 0, + "group": False, + "group_limit": 5, + "group_offset": 0, + "group_queries": [], "facet": True, "facet_min_count": 1, "facet_fields": [],