diff --git a/bookwyrm/book_search.py b/bookwyrm/book_search.py
index 3012482fd..cf48f4832 100644
--- a/bookwyrm/book_search.py
+++ b/bookwyrm/book_search.py
@@ -43,6 +43,7 @@ def search(
min_confidence: float = 0,
filters: Optional[list[Any]] = None,
return_first: bool = False,
+ books: Optional[QuerySet[models.Edition]] = None,
) -> Union[Optional[models.Edition], QuerySet[models.Edition]]:
"""search your local database"""
filters = filters or []
@@ -54,13 +55,15 @@ def search(
# first, try searching unique identifiers
# unique identifiers never have spaces, title/author usually do
if not " " in query:
- results = search_identifiers(query, *filters, return_first=return_first)
+ results = search_identifiers(
+ query, *filters, return_first=return_first, books=books
+ )
# if there were no identifier results...
if not results:
# then try searching title/author
results = search_title_author(
- query, min_confidence, *filters, return_first=return_first
+ query, min_confidence, *filters, return_first=return_first, books=books
)
return results
@@ -98,9 +101,17 @@ def format_search_result(search_result):
def search_identifiers(
- query, *filters, return_first=False
+ query,
+ *filters,
+ return_first=False,
+ books=None,
) -> Union[Optional[models.Edition], QuerySet[models.Edition]]:
- """tries remote_id, isbn; defined as dedupe fields on the model"""
+ """search Editions by deduplication fields
+
+ Best for cases when we can assume someone is searching for an exact match on
+ commonly unique data identifiers like isbn or specific library ids.
+ """
+ books = books or models.Edition.objects
if connectors.maybe_isbn(query):
# Oh did you think the 'S' in ISBN stood for 'standard'?
normalized_isbn = query.strip().upper().rjust(10, "0")
@@ -111,7 +122,7 @@ def search_identifiers(
for f in models.Edition._meta.get_fields()
if hasattr(f, "deduplication_field") and f.deduplication_field
]
- results = models.Edition.objects.filter(
+ results = books.filter(
*filters, reduce(operator.or_, (Q(**f) for f in or_filters))
).distinct()
@@ -121,12 +132,17 @@ def search_identifiers(
def search_title_author(
- query, min_confidence, *filters, return_first=False
+ query,
+ min_confidence,
+ *filters,
+ return_first=False,
+ books=None,
) -> QuerySet[models.Edition]:
"""searches for title and author"""
+ books = books or models.Edition.objects
query = SearchQuery(query, config="simple") | SearchQuery(query, config="english")
results = (
- models.Edition.objects.filter(*filters, search_vector=query)
+ books.filter(*filters, search_vector=query)
.annotate(rank=SearchRank(F("search_vector"), query))
.filter(rank__gt=min_confidence)
.order_by("-rank")
diff --git a/bookwyrm/templates/shelf/shelf.html b/bookwyrm/templates/shelf/shelf.html
index 45a94fed9..71f4bc088 100644
--- a/bookwyrm/templates/shelf/shelf.html
+++ b/bookwyrm/templates/shelf/shelf.html
@@ -101,7 +101,6 @@
{% plural %}
{{ formatted_count }} books
{% endblocktrans %}
-
{% if books.has_other_pages %}
{% blocktrans trimmed with start=books.start_index end=books.end_index %}
(showing {{ start }}-{{ end }})
@@ -111,6 +110,8 @@
{% endif %}
{% endwith %}
+ {% include 'shelf/shelves_filters.html' with user=user query=query %}
+
{% if is_self and shelf.id %}
@@ -209,7 +210,17 @@
{% else %}
-
{% trans "This shelf is empty." %}
+
+
+ {% if shelves_filter_query %}
+ {% blocktrans trimmed %}
+ We couldn't find any books that matched {{ shelves_filter_query }}
+ {% endblocktrans %}
+ {% else %}
+ {% trans "This shelf is empty." %}
+ {% endif %}
+
+
{% endif %}
diff --git a/bookwyrm/templates/shelf/shelves_filter_field.html b/bookwyrm/templates/shelf/shelves_filter_field.html
new file mode 100644
index 000000000..707f033ea
--- /dev/null
+++ b/bookwyrm/templates/shelf/shelves_filter_field.html
@@ -0,0 +1,9 @@
+{% extends 'snippets/filters_panel/filter_field.html' %}
+{% load i18n %}
+
+{% block filter %}
+
+
+
+
+{% endblock %}
diff --git a/bookwyrm/templates/shelf/shelves_filters.html b/bookwyrm/templates/shelf/shelves_filters.html
new file mode 100644
index 000000000..ad7fc3dbc
--- /dev/null
+++ b/bookwyrm/templates/shelf/shelves_filters.html
@@ -0,0 +1,5 @@
+{% extends 'snippets/filters_panel/filters_panel.html' %}
+
+{% block filter_fields %}
+ {% include 'shelf/shelves_filter_field.html' %}
+{% endblock %}
diff --git a/bookwyrm/views/search.py b/bookwyrm/views/search.py
index 743f33f59..f8eddd1a6 100644
--- a/bookwyrm/views/search.py
+++ b/bookwyrm/views/search.py
@@ -51,7 +51,7 @@ class Search(View):
def api_book_search(request):
"""Return books via API response"""
query = request.GET.get("q")
- query = isbn_check(query)
+ query = isbn_check_and_format(query)
min_confidence = request.GET.get("min_confidence", 0)
# only return local book results via json so we don't cascade
book_results = search(query, min_confidence=min_confidence)
@@ -64,7 +64,7 @@ def book_search(request):
"""the real business is elsewhere"""
query = request.GET.get("q")
# check if query is isbn
- query = isbn_check(query)
+ query = isbn_check_and_format(query)
min_confidence = request.GET.get("min_confidence", 0)
search_remote = request.GET.get("remote", False) and request.user.is_authenticated
@@ -159,7 +159,7 @@ def list_search(request):
return TemplateResponse(request, "search/list.html", data)
-def isbn_check(query):
+def isbn_check_and_format(query):
"""isbn10 or isbn13 check, if so remove separators"""
if query:
su_num = re.sub(r"(?<=\d)\D(?=\d|[xX])", "", query)
diff --git a/bookwyrm/views/shelf/shelf.py b/bookwyrm/views/shelf/shelf.py
index dbbcc2d3a..a39512fe6 100644
--- a/bookwyrm/views/shelf/shelf.py
+++ b/bookwyrm/views/shelf/shelf.py
@@ -15,12 +15,14 @@ from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse
from bookwyrm.settings import PAGE_LENGTH
from bookwyrm.views.helpers import is_api_request, get_user_from_username
+from bookwyrm.book_search import search
# pylint: disable=no-self-use
class Shelf(View):
"""shelf page"""
+ # pylint: disable=R0914
def get(self, request, username, shelf_identifier=None):
"""display a shelf"""
user = get_user_from_username(request.user, username)
@@ -32,6 +34,8 @@ class Shelf(View):
else:
shelves = models.Shelf.privacy_filter(request.user).filter(user=user).all()
+ shelves_filter_query = request.GET.get("filter")
+
# get the shelf and make sure the logged in user should be able to see it
if shelf_identifier:
shelf = get_object_or_404(user.shelf_set, identifier=shelf_identifier)
@@ -42,6 +46,7 @@ class Shelf(View):
FakeShelf = namedtuple(
"Shelf", ("identifier", "name", "user", "books", "privacy")
)
+
books = (
models.Edition.viewer_aware_objects(request.user)
.filter(
@@ -50,6 +55,7 @@ class Shelf(View):
)
.distinct()
)
+
shelf = FakeShelf("all", _("All books"), user, books, "public")
if is_api_request(request) and shelf_identifier:
@@ -86,6 +92,9 @@ class Shelf(View):
books = sort_books(books, request.GET.get("sort"))
+ if shelves_filter_query:
+ books = search(shelves_filter_query, books=books)
+
paginated = Paginator(
books,
PAGE_LENGTH,
@@ -103,6 +112,8 @@ class Shelf(View):
"page_range": paginated.get_elided_page_range(
page.number, on_each_side=2, on_ends=1
),
+ "shelves_filter_query": shelves_filter_query,
+ "size": "small",
}
return TemplateResponse(request, "shelf/shelf.html", data)