diff --git a/proto/backend.proto b/proto/backend.proto index 638786a5a..3821ad49f 100644 --- a/proto/backend.proto +++ b/proto/backend.proto @@ -331,6 +331,7 @@ message SortOrder { Empty from_config = 1; Empty none = 2; string custom = 3; + BuiltinSearchOrder builtin = 4; } } @@ -341,3 +342,24 @@ message SearchNotesIn { message SearchNotesOut { repeated int64 note_ids = 2; } + +message BuiltinSearchOrder { + BuiltinSortKind kind = 1; + bool reverse = 2; +} + +enum BuiltinSortKind { + NOTE_CREATION = 0; + NOTE_MOD = 1; + NOTE_FIELD = 2; + NOTE_TAGS = 3; + NOTE_TYPE = 4; + CARD_MOD = 5; + CARD_REPS = 6; + CARD_DUE = 7; + CARD_EASE = 8; + CARD_LAPSES = 9; + CARD_INTERVAL = 10; + CARD_DECK = 11; + CARD_TEMPLATE = 12; +} diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index fb03431a3..b8258adb4 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -616,8 +616,16 @@ where c.nid = n.id and c.id in %s group by nid""" # Finding cards ########################################################################## - def find_cards(self, query: str, order: Union[bool, str] = False) -> Sequence[int]: - return self.backend.search_cards(query, order) + # if order=True, use the sort order stored in the collection config + # if order=False, do no ordering + # if order is a string, that text is added after 'order by' in the sql statement + # if order is an int enum, sort using that builtin sort. + # + # the reverse argument only applies when a BuiltinSortKind is provided. + def find_cards( + self, query: str, order: Union[bool, str, int] = False, reverse: bool = False, + ) -> Sequence[int]: + return self.backend.search_cards(query, order, reverse) def find_notes(self, query: str) -> Sequence[int]: return self.backend.search_notes(query) diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index 52bdc0d8f..b3d055577 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -34,6 +34,7 @@ from anki.types import assert_impossible_literal assert ankirspy.buildhash() == anki.buildinfo.buildhash SchedTimingToday = pb.SchedTimingTodayOut +BuiltinSortKind = pb.BuiltinSortKind try: import orjson @@ -435,13 +436,23 @@ class RustBackend: def _db_command(self, input: Dict[str, Any]) -> Any: return orjson.loads(self._backend.db_command(orjson.dumps(input))) - def search_cards(self, search: str, order: Union[bool, str]) -> Sequence[int]: + def search_cards( + self, search: str, order: Union[bool, str, int], reverse: bool = False + ) -> Sequence[int]: if isinstance(order, str): mode = pb.SortOrder(custom=order) - elif not order: + elif order is True: + mode = pb.SortOrder(from_config=pb.Empty()) + elif order is False: mode = pb.SortOrder(none=pb.Empty()) else: - mode = pb.SortOrder(from_config=pb.Empty()) + # sadly we can't use the protobuf type in a Union, so we + # have to accept an int and convert it + kind = BuiltinSortKind.Value(BuiltinSortKind.Name(order)) + mode = pb.SortOrder( + builtin=pb.BuiltinSearchOrder(kind=kind, reverse=reverse) + ) + return self._run_command( pb.BackendInput(search_cards=pb.SearchCardsIn(search=search, order=mode)) ).search_cards.card_ids diff --git a/pylib/tests/test_find.py b/pylib/tests/test_find.py index 16d604b17..10758b56e 100644 --- a/pylib/tests/test_find.py +++ b/pylib/tests/test_find.py @@ -2,6 +2,7 @@ import pytest from anki.consts import * +from anki.rsbackend import BuiltinSortKind from tests.shared import getEmptyCol @@ -120,6 +121,14 @@ def test_findCards(): deck.conf["sortBackwards"] = True deck.flush() assert deck.findCards("", order=True)[0] in latestCardIds + assert ( + deck.find_cards("", order=BuiltinSortKind.CARD_DUE, reverse=False)[0] + == firstCardId + ) + assert ( + deck.find_cards("", order=BuiltinSortKind.CARD_DUE, reverse=True)[0] + != firstCardId + ) # model assert len(deck.findCards("note:basic")) == 5 assert len(deck.findCards("-note:basic")) == 0 diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index cc9815778..8bbba11a7 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -3,8 +3,9 @@ use crate::backend::dbproxy::db_command_bytes; use crate::backend_proto::backend_input::Value; -use crate::backend_proto::{Empty, RenderedTemplateReplacement, SyncMediaIn}; +use crate::backend_proto::{BuiltinSortKind, Empty, RenderedTemplateReplacement, SyncMediaIn}; use crate::collection::{open_collection, Collection}; +use crate::config::SortKind; use crate::err::{AnkiError, NetworkErrorKind, Result, SyncErrorKind}; use crate::i18n::{tr_args, FString, I18n}; use crate::latex::{extract_latex, extract_latex_expanding_clozes, ExtractedLatex}; @@ -588,6 +589,10 @@ impl Backend { Some(V::None(_)) => SortMode::NoOrder, Some(V::Custom(s)) => SortMode::Custom(s), Some(V::FromConfig(_)) => SortMode::FromConfig, + Some(V::Builtin(b)) => SortMode::Builtin { + kind: sort_kind_from_pb(b.kind), + reverse: b.reverse, + }, None => SortMode::FromConfig, } } else { @@ -677,3 +682,25 @@ fn media_sync_progress(p: &MediaSyncProgress, i18n: &I18n) -> pb::MediaSyncProgr ), } } + +fn sort_kind_from_pb(kind: i32) -> SortKind { + use SortKind as SK; + match pb::BuiltinSortKind::from_i32(kind) { + Some(pbkind) => match pbkind { + BuiltinSortKind::NoteCreation => SK::NoteCreation, + BuiltinSortKind::NoteMod => SK::NoteMod, + BuiltinSortKind::NoteField => SK::NoteField, + BuiltinSortKind::NoteTags => SK::NoteTags, + BuiltinSortKind::NoteType => SK::NoteType, + BuiltinSortKind::CardMod => SK::CardMod, + BuiltinSortKind::CardReps => SK::CardReps, + BuiltinSortKind::CardDue => SK::CardDue, + BuiltinSortKind::CardEase => SK::CardEase, + BuiltinSortKind::CardLapses => SK::CardLapses, + BuiltinSortKind::CardInterval => SK::CardInterval, + BuiltinSortKind::CardDeck => SK::CardDeck, + BuiltinSortKind::CardTemplate => SK::CardTemplate, + }, + _ => SortKind::NoteCreation, + } +} diff --git a/rslib/src/search/cards.rs b/rslib/src/search/cards.rs index e4d8eaf2a..2c10f1e8d 100644 --- a/rslib/src/search/cards.rs +++ b/rslib/src/search/cards.rs @@ -13,6 +13,7 @@ use rusqlite::params; pub(crate) enum SortMode { NoOrder, FromConfig, + Builtin { kind: SortKind, reverse: bool }, Custom(String), } @@ -37,6 +38,11 @@ pub(crate) fn search_cards<'a, 'b>( sql.push_str(" order by "); write_order(&mut sql, &conf.browser_sort_kind, conf.browser_sort_reverse)?; } + SortMode::Builtin { kind, reverse } => { + prepare_sort(req, &kind)?; + sql.push_str(" order by "); + write_order(&mut sql, &kind, reverse)?; + } SortMode::Custom(order_clause) => { sql.push_str(" order by "); sql.push_str(&order_clause);