Skip to content

Commit

Permalink
#331 Proposition for a new search app arch (#332)
Browse files Browse the repository at this point in the history
* #331  Draft for the search arch

* #331  Subtask new search arch implementation

* #331  Drop contexts in favor of results processing

* #331  Minor imports fix

* #331  Review#1 fixes. Approve docs
  • Loading branch information
duker33 authored May 31, 2019
1 parent ddad9c1 commit 77f71d0
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 1 deletion.
2 changes: 1 addition & 1 deletion pages/context/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
class Context(abc.ABC):

@abc.abstractmethod
def context(self) -> typing.Dict[str, typing.Any]:
def context(self) -> typing.ContextDict:
...


Expand Down
93 changes: 93 additions & 0 deletions search/arch_draft.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""
This is temporary module with the arch concept for the search.
@todo #331:60m Implement the new arch at the search app.
All you need is to implement ContextsStack class
and place the other classes to the relevant places.
`search.search` module will be fully rewritten.
You also can drop away starting_by_term functionality.
See "is_name_start_by_term" aggregation parameter
inside search.search.search function.
"""

import abc
import typing
from itertools import chain

from django.db.models import QuerySet
from django.http import JsonResponse

from images.models import Image


class Representable(abc.ABC):
"""Contract for objects that come as search results by different QuerySets."""

@property
def name(self):
raise NotImplemented()

@property
def url(self):
raise NotImplemented()


class Result:
"""
One item in a search results list.
Casts given object to common and clear result interface.
In case of the search module object is model instance.
Different objects can have very different interfaces.
That's why the class unifies it.
"""
def __init__(self, obj: Representable):
self.name: str = obj.name
self.url: str = obj.url
self.image: Image = getattr(obj, 'image', None)
self.price: float = getattr(obj, 'price', 0.0)


class Results:
def __init__(self, results_qs: QuerySet):
"""`results_qs` is QuerySet with already searched and sorted entities."""
isinstance(results_qs.model, Representable)
self.qs = results_qs

def list(self) -> typing.List[Result]:
return [Result(o) for o in self.qs]


class ResultsStack:
"""Stack results with different merge strategies."""

def __init__(self, result_sets: typing.Iterable[Results]):
self.sets = result_sets

def chain(self) -> typing.Iterator[Result]:
return chain(*self.sets)


# it's some client code:
class CategoryQuerySet(QuerySet):
pass


class ProductQuerySet(QuerySet):
pass


def autocomplete(request, query):
# we'll use django orm search based on postgresql.
# Query sets will already contain searched entities.
# https://docs.djangoproject.com/en/1.11/topics/db/search/
return JsonResponse(list(
ResultsStack([
Results(
CategoryQuerySet().filter(name__search=query),
),
Results(
ProductQuerySet().filter(name__search=query),
),
]).chain()
))

1 comment on commit 77f71d0

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 77f71d0 May 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 331-cd71fc19 discovered in search/arch_draft.py and submitted as #337. Please, remember that the puzzle was not necessarily added in this particular commit. Maybe it was added earlier, but we discovered it only now.

Please sign in to comment.