mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 14:32:22 -04:00
support sorting on note type, card template and decks
This commit is contained in:
parent
1318118461
commit
00d0447ecb
5 changed files with 101 additions and 8 deletions
|
@ -2,9 +2,19 @@
|
|||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use crate::types::ObjID;
|
||||
use serde::Deserialize as DeTrait;
|
||||
use serde_aux::field_attributes::deserialize_number_from_string;
|
||||
use serde_derive::Deserialize;
|
||||
use serde_json::Value;
|
||||
|
||||
pub(crate) fn default_on_invalid<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: Default + DeTrait<'de>,
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let v: Value = DeTrait::deserialize(deserializer)?;
|
||||
Ok(T::deserialize(v).unwrap_or_default())
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Config {
|
||||
|
@ -16,7 +26,7 @@ pub struct Config {
|
|||
pub(crate) rollover: Option<i8>,
|
||||
pub(crate) creation_offset: Option<i32>,
|
||||
pub(crate) local_offset: Option<i32>,
|
||||
#[serde(rename = "sortType")]
|
||||
#[serde(rename = "sortType", deserialize_with = "default_on_invalid")]
|
||||
pub(crate) browser_sort_kind: SortKind,
|
||||
#[serde(rename = "sortBackwards", default)]
|
||||
pub(crate) browser_sort_reverse: bool,
|
||||
|
@ -30,6 +40,8 @@ pub enum SortKind {
|
|||
NoteMod,
|
||||
#[serde(rename = "noteFld")]
|
||||
NoteField,
|
||||
#[serde(rename = "note")]
|
||||
NoteType,
|
||||
CardMod,
|
||||
CardReps,
|
||||
CardDue,
|
||||
|
@ -37,6 +49,10 @@ pub enum SortKind {
|
|||
CardLapses,
|
||||
#[serde(rename = "cardIvl")]
|
||||
CardInterval,
|
||||
#[serde(rename = "deck")]
|
||||
CardDeck,
|
||||
#[serde(rename = "template")]
|
||||
CardTemplate,
|
||||
}
|
||||
|
||||
impl Default for SortKind {
|
||||
|
|
|
@ -8,6 +8,7 @@ use crate::config::SortKind;
|
|||
use crate::err::Result;
|
||||
use crate::search::parser::parse;
|
||||
use crate::types::ObjID;
|
||||
use rusqlite::params;
|
||||
|
||||
pub(crate) fn search_cards<'a, 'b>(
|
||||
req: &'a mut RequestContext<'b>,
|
||||
|
@ -15,11 +16,15 @@ pub(crate) fn search_cards<'a, 'b>(
|
|||
) -> Result<Vec<ObjID>> {
|
||||
let top_node = Node::Group(parse(search)?);
|
||||
let (sql, args) = node_to_sql(req, &top_node)?;
|
||||
|
||||
let conf = req.storage.all_config()?;
|
||||
prepare_sort(req, &conf.browser_sort_kind)?;
|
||||
|
||||
let mut sql = format!(
|
||||
"select c.id from cards c, notes n where c.nid=n.id and {} order by ",
|
||||
"select c.id from cards c, notes n where c.nid=n.id and {}",
|
||||
sql
|
||||
);
|
||||
write_order(req, &mut sql)?;
|
||||
write_order(&mut sql, &conf.browser_sort_kind, conf.browser_sort_reverse)?;
|
||||
|
||||
let mut stmt = req.storage.db.prepare(&sql)?;
|
||||
let ids: Vec<i64> = stmt
|
||||
|
@ -30,10 +35,10 @@ pub(crate) fn search_cards<'a, 'b>(
|
|||
Ok(ids)
|
||||
}
|
||||
|
||||
fn write_order(req: &mut RequestContext, sql: &mut String) -> Result<()> {
|
||||
let conf = req.storage.all_config()?;
|
||||
/// Add the order clause to the sql.
|
||||
fn write_order(sql: &mut String, kind: &SortKind, reverse: bool) -> Result<()> {
|
||||
let tmp_str;
|
||||
sql.push_str(match conf.browser_sort_kind {
|
||||
let order = match kind {
|
||||
SortKind::NoteCreation => "n.id, c.ord",
|
||||
SortKind::NoteMod => "n.mod, c.ord",
|
||||
SortKind::NoteField => "n.sfld collate nocase, c.ord",
|
||||
|
@ -46,9 +51,76 @@ fn write_order(req: &mut RequestContext, sql: &mut String) -> Result<()> {
|
|||
}
|
||||
SortKind::CardLapses => "c.lapses",
|
||||
SortKind::CardInterval => "c.ivl",
|
||||
});
|
||||
if conf.browser_sort_reverse {
|
||||
SortKind::CardDeck => "(select v from sort_order where k = c.did)",
|
||||
SortKind::NoteType => "(select v from sort_order where k = n.mid)",
|
||||
SortKind::CardTemplate => "(select v from sort_order where k1 = n.mid and k2 = c.ord)",
|
||||
};
|
||||
if order.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
sql.push_str(" order by ");
|
||||
sql.push_str(order);
|
||||
if reverse {
|
||||
sql.push_str(" desc");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// In the future these items should be moved from JSON into separate SQL tables,
|
||||
// - for now we use a temporary deck to sort them.
|
||||
fn prepare_sort(req: &mut RequestContext, kind: &SortKind) -> Result<()> {
|
||||
use SortKind::*;
|
||||
match kind {
|
||||
CardDeck | NoteType => {
|
||||
prepare_sort_order_table(req)?;
|
||||
let mut stmt = req
|
||||
.storage
|
||||
.db
|
||||
.prepare("insert into sort_order (k,v) values (?,?)")?;
|
||||
|
||||
match kind {
|
||||
CardDeck => {
|
||||
for (k, v) in req.storage.all_decks()? {
|
||||
stmt.execute(params![k, v.name])?;
|
||||
}
|
||||
}
|
||||
NoteType => {
|
||||
for (k, v) in req.storage.all_note_types()? {
|
||||
stmt.execute(params![k, v.name])?;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
CardTemplate => {
|
||||
prepare_sort_order_table2(req)?;
|
||||
let mut stmt = req
|
||||
.storage
|
||||
.db
|
||||
.prepare("insert into sort_order (k1,k2,v) values (?,?,?)")?;
|
||||
|
||||
for (ntid, nt) in req.storage.all_note_types()? {
|
||||
for tmpl in nt.templates {
|
||||
stmt.execute(params![ntid, tmpl.ord, tmpl.name])?;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_sort_order_table(req: &mut RequestContext) -> Result<()> {
|
||||
req.storage
|
||||
.db
|
||||
.execute_batch(include_str!("sort_order.sql"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prepare_sort_order_table2(req: &mut RequestContext) -> Result<()> {
|
||||
req.storage
|
||||
.db
|
||||
.execute_batch(include_str!("sort_order2.sql"))?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
2
rslib/src/search/sort_order.sql
Normal file
2
rslib/src/search/sort_order.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
drop table if exists sort_order;
|
||||
create temporary table sort_order (k int primary key, v text);
|
2
rslib/src/search/sort_order2.sql
Normal file
2
rslib/src/search/sort_order2.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
drop table if exists sort_order;
|
||||
create temporary table sort_order (k1 int, k2 int, v text, primary key (k1, k2)) without rowid;
|
|
@ -45,6 +45,7 @@ fn open_or_create_collection_db(path: &Path) -> Result<Connection> {
|
|||
db.pragma_update(None, "cache_size", &(-40 * 1024))?;
|
||||
db.pragma_update(None, "legacy_file_format", &false)?;
|
||||
db.pragma_update(None, "journal", &"wal")?;
|
||||
db.pragma_update(None, "temp_store", &"memory")?;
|
||||
|
||||
db.set_prepared_statement_cache_capacity(50);
|
||||
|
||||
|
|
Loading…
Reference in a new issue