"""Logic for Vector order search.""" import copy from collections import defaultdict from typing import Any from flask import g from vectororder.constants import search as search_const from vectororder.models import search def search_kwargs_by_type( search_kwargs: dict[str, Any], ) -> tuple[dict[str, Any], dict[str, Any]]: """Group search keywords by query type. Args: search_kwargs (dict): All search fields that passed the schema validation. It is modified inplace. Returns: tuple: A two element tuple of dictionaries: search keywords grouped by query type and unmatched keyword arguments. """ unmatched_kwargs = copy.deepcopy(search_kwargs) matched_kwargs: dict[str, Any] = defaultdict(dict) for category, fields in search_const.VO_FIELD_CATEGORIES.items(): for field in fields: value = unmatched_kwargs.pop(field, None) if unmatched_kwargs else None if value is None: continue if category == search_const.VO_FIELD_CATEGORY_INNER_DISJUNCTION: # Inner query is expected to be an iterable. inner_matched_list = [] for inner_kwargs in value: inner_matched, inner_unmatched = search_kwargs_by_type(inner_kwargs) inner_matched_list.append(inner_matched) unmatched_kwargs.update(inner_unmatched) matched_kwargs[category][field] = tuple(inner_matched_list) continue matched_kwargs[category][field] = value return dict(matched_kwargs), unmatched_kwargs def search_vector_orders( *, search_kwargs: dict[str, Any], offset: int | None = None, limit: int | None = None, order_by: list[str] | None = None, ) -> tuple[list[dict[str, Any]], int]: """Search for Vector orders. Pre-process and group fields into a few different categories that should be treated differently in the model layer. Note: individual field validation and deserialization happens during the schema validation. Args: offset (int|None): Pagination offset - the number of items to skip. limit (int|None): Pagination limit - the maximum number of items to return. order_by (list|None): Field names to order search results by. search_kwargs (dict): All search fields that passed the schema validation. Returns: response.Response: A response with search result from the models layer. """ g.ows.log.debug("all search kwargs: %s", search_kwargs) matched_kwargs, unmatched_kwargs = search_kwargs_by_type(search_kwargs) g.ows.log.debug("matched kwargs: %s", matched_kwargs) if unmatched_kwargs: # If any search_kwargs are left not categorised, log this. # This should not happen if we have an up to date schema and # field categories configuration, but it's better not to silence a # potential misconfiguration. g.ows.log.warning( "Not all fields were parsed into a search query. " "Please, configure constants.search.VO_FIELD_CATEGORIES. " "Unknown fields: %s.", ", ".join(sorted(unmatched_kwargs.keys())), ) return search.search_vector_order_details( offset=offset, limit=limit, order_by=order_by, **matched_kwargs ) def save_search_request(request_data: dict[str, Any]) -> str: """Save search request. Args: request_data (dict): Search request data. Returns: str: Search request ID in a message. """ return search.save_search_request(request_data) def get_search_request(request_id: str) -> dict[str, Any]: """Get search request. Args: request_id (str): Search request ID. Returns: dict: Search request data dictionary in a message. """ return search.get_search_request(request_id)