Skip to content

Commit

Permalink
wip: support 'include' for sparse paging
Browse files Browse the repository at this point in the history
  • Loading branch information
aaxelb committed Dec 19, 2024
1 parent 010ab7a commit 8e2d7db
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 72 deletions.
26 changes: 22 additions & 4 deletions trove/trovesearch/search_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import collections
import dataclasses
import enum
import functools
import itertools
import logging
import typing
Expand Down Expand Up @@ -78,6 +79,13 @@ def shortnames(cls):
def to_shortname(self) -> str:
return trove_shorthand().compact_iri(self.value)


class TrovesearchIncludePaths(enum.Enum):
# property-path values; correspond with expensive work could be skipped
CARDS = (TROVE.searchResultPage, TROVE.indexCard)
RELATED = (TROVE.relatedPropertyList,)


###
# dataclasses for parsed search-api query parameters

Expand Down Expand Up @@ -130,8 +138,12 @@ def _gather_shorthand(cls, queryparams: QueryparamDict):

@classmethod
def _gather_include(cls, queryparams: QueryparamDict):
# TODO: for _qp_name, _iri in queryparams.get('include', []):
return frozenset()
return frozenset(
itertools.chain.from_iterable(
_parse_propertypath_set(_include_value)
for _, _include_value in queryparams.get('include', [])
)
)


@dataclasses.dataclass(frozen=True)
Expand Down Expand Up @@ -469,7 +481,6 @@ class CardsearchParams(BaseTroveParams):
index_strategy_name: str | None
sort_list: tuple[SortParam, ...]
page_cursor: PageCursor
related_property_paths: tuple[Propertypath, ...]

@classmethod
def parse_queryparams(cls, queryparams: QueryparamDict) -> dict:
Expand All @@ -482,9 +493,16 @@ def parse_queryparams(cls, queryparams: QueryparamDict) -> dict:
'sort_list': SortParam.from_sort_queryparams(queryparams),
'page_cursor': _get_page_cursor(queryparams),
'include': None, # TODO
'related_property_paths': _get_related_property_paths(_filter_set),
}

@functools.cached_property
def related_property_paths(self) -> tuple[Propertypath, ...]:
return (
_get_related_property_paths(self.cardsearch_filter_set)
if TrovesearchIncludePaths.RELATED.value in self.include
else ()
)

def to_querydict(self) -> QueryDict:
_querydict = super().to_querydict()
for _qp_name, _qp_value in Textsegment.queryparams_from_textsegments('cardSearchText', self.cardsearch_textsegment_set):
Expand Down
4 changes: 4 additions & 0 deletions trove/trovesearch/trovesearch_gathering.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@
)


class TrovesearchFocus(gather.Focus):
search_handle: PagedResponse


# TODO: per-field text search in rdf
# @trovesearch_by_indexstrategy.gatherer(TROVE.cardSearchText)
# def gather_cardsearch_text(focus, *, search_params, **kwargs):
Expand Down
138 changes: 70 additions & 68 deletions trove/views/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
from share.search import index_strategy
from trove import exceptions as trove_exceptions
from trove.trovesearch.search_params import (
BaseTroveParams,
CardsearchParams,
ValuesearchParams,
)
from trove.trovesearch.trovesearch_gathering import trovesearch_by_indexstrategy
from trove.vocab.namespaces import TROVE
from trove.vocab.namespaces import TROVE, FOAF, DCTERMS
from trove.render import (
DEFAULT_RENDERER_TYPE,
get_renderer_type,
Expand All @@ -25,96 +26,97 @@
logger = logging.getLogger(__name__)


DEFAULT_CARDSEARCH_ASK = {
TROVE.totalResultCount: None,
TROVE.cardSearchText: None,
TROVE.cardSearchFilter: None,
TROVE.searchResultPage: {
TROVE.indexCard: {
TROVE.resourceMetadata,
},
DEFAULT_INCLUDES_BY_TYPE = {
TROVE.Cardsearch: {
TROVE.searchResultPage,
TROVE.relatedPropertyList,
},
TROVE.Valuesearch: {
TROVE.searchResultPage,
},
TROVE.SearchResult: {
TROVE.indexCard,
},
}

DEFAULT_VALUESEARCH_ASK = {
TROVE.propertyPath: None,
TROVE.valueSearchText: None,
TROVE.valueSearchFilter: None,
TROVE.cardSearchText: None,
TROVE.cardSearchFilter: None,
TROVE.searchResultPage: {
TROVE.indexCard: {
TROVE.resourceMetadata,
},
DEFAULT_FIELDS_BY_TYPE = {
TROVE.Indexcard: {
TROVE.resourceMetadata,
TROVE.focusIdentifier,
DCTERMS.issued,
DCTERMS.modified,
FOAF.primaryTopic
},
TROVE.Cardsearch: {
TROVE.totalResultCount,
TROVE.cardSearchText,
TROVE.cardSearchFilter,
},
TROVE.Valuesearch: {
TROVE.propertyPath,
TROVE.valueSearchText,
TROVE.valueSearchFilter,
TROVE.cardSearchText,
TROVE.cardSearchFilter,
},
}


class CardsearchView(View):
class _BaseTrovesearchView(View):
# expected on inheritors
focus_type_iri: str
params_dataclass: type[BaseTroveParams]

def get(self, request):
_url = request.build_absolute_uri()
try:
_renderer_type = get_renderer_type(request)
_search_iri, _search_gathering = _parse_request(request, _renderer_type, CardsearchParams)
_search_gathering.ask(
DEFAULT_CARDSEARCH_ASK, # TODO: build from `include`/`fields`
focus=gather.Focus.new(_search_iri, TROVE.Cardsearch),
)
_renderer = _renderer_type(_search_iri, _search_gathering.leaf_a_record())
return make_http_response(
content_rendering=_renderer.render_document(),
http_request=request,
)
except trove_exceptions.CannotRenderMediatype as _error:
return make_http_error_response(
error=_error,
renderer=DEFAULT_RENDERER_TYPE(_search_iri),
)
except trove_exceptions.TroveError as _error:
return make_http_error_response(
error=_error,
renderer=_renderer_type(_search_iri),
renderer=DEFAULT_RENDERER_TYPE(_url),
)


class ValuesearchView(View):
def get(self, request):
try:
_renderer_type = get_renderer_type(request)
_search_iri, _search_gathering = _parse_request(request, _renderer_type, ValuesearchParams)
_search_gathering.ask(
DEFAULT_VALUESEARCH_ASK, # TODO: build from `include`/`fields`
focus=gather.Focus.new(_search_iri, TROVE.Valuesearch),
_search_gathering = self._start_gathering(
search_params=self._parse_search_params(request),
renderer_type=_renderer_type,
)
_renderer = _renderer_type(_search_iri, _search_gathering.leaf_a_record())
_focus = gather.Focus.new(_url, self.focus_type_iri)
_search_gathering.ask(self._get_asktree(request), focus=_focus)
_renderer = _renderer_type(_url, _search_gathering.leaf_a_record())
return make_http_response(
content_rendering=_renderer.render_document(),
http_request=request,
)
except trove_exceptions.CannotRenderMediatype as _error:
return make_http_error_response(
error=_error,
renderer=DEFAULT_RENDERER_TYPE(_search_iri),
)
except trove_exceptions.TroveError as _error:
return make_http_error_response(
error=_error,
renderer=_renderer_type(_search_iri),
renderer=_renderer_type(_url),
)

def _parse_search_params(self, request: http.HttpRequest):
return self.params_dataclass.from_querystring(
request.META['QUERY_STRING'],
)

def _start_gathering(self, search_params, renderer_type) -> gather.Gathering:
_specific_index = index_strategy.get_index_for_trovesearch(search_params)
# TODO: 404 for unknown strategy
return trovesearch_by_indexstrategy.new_gathering({
'search_params': search_params,
'specific_index': _specific_index,
'deriver_iri': renderer_type.INDEXCARD_DERIVER_IRI,
})

def _get_asktree(self, request: http.HttpRequest):
...


class CardsearchView(_BaseTrovesearchView):
focus_type_iri = TROVE.Cardsearch
params_dataclass = CardsearchParams


###
# local helpers

def _parse_request(request: http.HttpRequest, renderer_type, search_params_dataclass):
_search_iri = request.build_absolute_uri()
_search_params = search_params_dataclass.from_querystring(
request.META['QUERY_STRING'],
)
_specific_index = index_strategy.get_index_for_trovesearch(_search_params)
# TODO: 404 for unknown strategy
_search_gathering = trovesearch_by_indexstrategy.new_gathering({
'search_params': _search_params,
'specific_index': _specific_index,
'deriver_iri': renderer_type.INDEXCARD_DERIVER_IRI,
})
return (_search_iri, _search_gathering)
class ValuesearchView(_BaseTrovesearchView):
focus_type_iri = TROVE.Valuesearch
params_dataclass = ValuesearchParams

0 comments on commit 8e2d7db

Please sign in to comment.