Source code for plone_client.managers.search

"""Search: the ``@search`` catalog query and ``@querystring-search``."""

from .base import BaseManager
from collections.abc import Iterable
from collections.abc import Iterator
from collections.abc import Mapping
from typing import Any


[docs] class SearchManager(BaseManager): """Catalog queries through ``@search``. Searching *on* a folder restricts the results to that folder, which works the same with and without virtual hosting. That is why ``path`` here is the folder to search in, not a ``path`` index query. """ def _params( self, query: Mapping[str, Any] | None, *, portal_type: str | Iterable[str] | None, review_state: str | Iterable[str] | None, text: str | None, depth: int | None, sort_on: str | Iterable[str] | None, sort_order: str | None, metadata_fields: str | Iterable[str] | None, fullobjects: bool, indexes: Mapping[str, Any], ) -> dict[str, Any]: """Query parameters of a search. :returns: Parameters for :meth:`PloneClient.request`. """ params: dict[str, Any] = { **(query or {}), "portal_type": portal_type, "review_state": review_state, "SearchableText": text, "path.depth": depth, "sort_on": sort_on, "sort_order": sort_order, "metadata_fields": metadata_fields, "fullobjects": 1 if fullobjects else None, **indexes, } return {key: value for key, value in params.items() if value is not None}
[docs] def search( self, path: str = "/", *, query: Mapping[str, Any] | None = None, portal_type: str | Iterable[str] | None = None, review_state: str | Iterable[str] | None = None, text: str | None = None, depth: int | None = None, sort_on: str | Iterable[str] | None = None, sort_order: str | None = None, metadata_fields: str | Iterable[str] | None = None, fullobjects: bool = False, b_size: int | None = None, b_start: int | None = None, **indexes: Any, ) -> dict[str, Any]: """One page of results. :param path: Folder to search in; the site root by default. :param query: Catalog query as a mapping, for index names that are not valid keyword arguments; nested options are flattened (``{"created": {"query": "2026-01-01", "range": "min"}}``). :param portal_type: Content types. :param review_state: Workflow states. :param text: Full-text search (``SearchableText``). :param depth: Levels below ``path``: ``0`` is the folder itself, ``1`` its children. :param sort_on: Index to sort on; several sort on each in turn. :param sort_order: ``ascending`` or ``descending``. :param metadata_fields: Extra catalog metadata in each result (``_all`` for every column). :param fullobjects: Return full serializations; much slower. :param b_size: Results per page (the site defaults to 25). :param b_start: Offset of the page. :param indexes: Other catalog indexes, such as ``UID`` or ``Subject``. :returns: ``items``, ``items_total`` and ``batching``. """ params = self._params( query, portal_type=portal_type, review_state=review_state, text=text, depth=depth, sort_on=sort_on, sort_order=sort_order, metadata_fields=metadata_fields, fullobjects=fullobjects, indexes=indexes, ) if b_size is not None: params["b_size"] = b_size if b_start is not None: params["b_start"] = b_start return self._client.get(self._service(path, "@search"), **params)
[docs] def iter( self, path: str = "/", *, query: Mapping[str, Any] | None = None, portal_type: str | Iterable[str] | None = None, review_state: str | Iterable[str] | None = None, text: str | None = None, depth: int | None = None, sort_on: str | Iterable[str] | None = None, sort_order: str | None = None, metadata_fields: str | Iterable[str] | None = None, fullobjects: bool = False, limit: int | None = None, b_size: int = 100, **indexes: Any, ) -> Iterator[dict[str, Any]]: """Every result, fetching the pages as needed. Takes the same arguments as :meth:`search`, plus: :param limit: Stop after this many results. :param b_size: Results fetched per request. :returns: Each result. """ params = self._params( query, portal_type=portal_type, review_state=review_state, text=text, depth=depth, sort_on=sort_on, sort_order=sort_order, metadata_fields=metadata_fields, fullobjects=fullobjects, indexes=indexes, ) params["b_size"] = b_size return self._client.iter_items( self._service(path, "@search"), params, limit=limit )
[docs] def count( self, path: str = "/", *, query: Mapping[str, Any] | None = None, portal_type: str | Iterable[str] | None = None, review_state: str | Iterable[str] | None = None, text: str | None = None, depth: int | None = None, **indexes: Any, ) -> int: """Number of results of a search. :returns: ``items_total``. """ page = self.search( path, query=query, portal_type=portal_type, review_state=review_state, text=text, depth=depth, b_size=1, **indexes, ) return int(page.get("items_total", 0))
[docs] class QuerystringManager(BaseManager): """Collection-style queries through ``@querystring-search``."""
[docs] def config(self, path: str = "/") -> dict[str, Any]: """Indexes, operators and sort options the querystring accepts. :param path: Context of the query. :returns: ``indexes`` and ``sortable_indexes``. """ return self._client.get(self._service(path, "@querystring"))
[docs] def search( self, query: Iterable[Mapping[str, Any]], *, path: str = "/", sort_on: str | None = None, sort_order: str | None = None, limit: int | None = None, b_size: int | None = None, b_start: int | None = None, metadata_fields: str | Iterable[str] | None = None, fullobjects: bool = False, ) -> dict[str, Any]: """Run a querystring query. :param query: Criteria, each with ``i`` (index), ``o`` (operation) and ``v`` (value), as a Listing block stores them. :param path: Context of the query, for relative path criteria. :param sort_on: Index to sort on. :param sort_order: ``ascending`` or ``descending``. :param limit: Maximum number of results. :param b_size: Results per page. :param b_start: Offset of the page. :param metadata_fields: Extra catalog metadata in each result. :param fullobjects: Return full serializations. :returns: ``items``, ``items_total`` and ``batching``. """ body: dict[str, Any] = { "query": [dict(criterion) for criterion in query], "sort_on": sort_on, "sort_order": sort_order, "limit": limit, "b_size": b_size, "b_start": b_start, "fullobjects": True if fullobjects else None, } if metadata_fields is not None: body["metadata_fields"] = ( [metadata_fields] if isinstance(metadata_fields, str) else list(metadata_fields) ) body = {key: value for key, value in body.items() if value is not None} return self._client.post(self._service(path, "@querystring-search"), body)