mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22: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
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use crate::types::ObjID;
|
use crate::types::ObjID;
|
||||||
|
use serde::Deserialize as DeTrait;
|
||||||
use serde_aux::field_attributes::deserialize_number_from_string;
|
use serde_aux::field_attributes::deserialize_number_from_string;
|
||||||
use serde_derive::Deserialize;
|
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)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@ -16,7 +26,7 @@ pub struct Config {
|
||||||
pub(crate) rollover: Option<i8>,
|
pub(crate) rollover: Option<i8>,
|
||||||
pub(crate) creation_offset: Option<i32>,
|
pub(crate) creation_offset: Option<i32>,
|
||||||
pub(crate) local_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,
|
pub(crate) browser_sort_kind: SortKind,
|
||||||
#[serde(rename = "sortBackwards", default)]
|
#[serde(rename = "sortBackwards", default)]
|
||||||
pub(crate) browser_sort_reverse: bool,
|
pub(crate) browser_sort_reverse: bool,
|
||||||
|
@ -30,6 +40,8 @@ pub enum SortKind {
|
||||||
NoteMod,
|
NoteMod,
|
||||||
#[serde(rename = "noteFld")]
|
#[serde(rename = "noteFld")]
|
||||||
NoteField,
|
NoteField,
|
||||||
|
#[serde(rename = "note")]
|
||||||
|
NoteType,
|
||||||
CardMod,
|
CardMod,
|
||||||
CardReps,
|
CardReps,
|
||||||
CardDue,
|
CardDue,
|
||||||
|
@ -37,6 +49,10 @@ pub enum SortKind {
|
||||||
CardLapses,
|
CardLapses,
|
||||||
#[serde(rename = "cardIvl")]
|
#[serde(rename = "cardIvl")]
|
||||||
CardInterval,
|
CardInterval,
|
||||||
|
#[serde(rename = "deck")]
|
||||||
|
CardDeck,
|
||||||
|
#[serde(rename = "template")]
|
||||||
|
CardTemplate,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SortKind {
|
impl Default for SortKind {
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::config::SortKind;
|
||||||
use crate::err::Result;
|
use crate::err::Result;
|
||||||
use crate::search::parser::parse;
|
use crate::search::parser::parse;
|
||||||
use crate::types::ObjID;
|
use crate::types::ObjID;
|
||||||
|
use rusqlite::params;
|
||||||
|
|
||||||
pub(crate) fn search_cards<'a, 'b>(
|
pub(crate) fn search_cards<'a, 'b>(
|
||||||
req: &'a mut RequestContext<'b>,
|
req: &'a mut RequestContext<'b>,
|
||||||
|
@ -15,11 +16,15 @@ pub(crate) fn search_cards<'a, 'b>(
|
||||||
) -> Result<Vec<ObjID>> {
|
) -> Result<Vec<ObjID>> {
|
||||||
let top_node = Node::Group(parse(search)?);
|
let top_node = Node::Group(parse(search)?);
|
||||||
let (sql, args) = node_to_sql(req, &top_node)?;
|
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!(
|
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
|
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 mut stmt = req.storage.db.prepare(&sql)?;
|
||||||
let ids: Vec<i64> = stmt
|
let ids: Vec<i64> = stmt
|
||||||
|
@ -30,10 +35,10 @@ pub(crate) fn search_cards<'a, 'b>(
|
||||||
Ok(ids)
|
Ok(ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_order(req: &mut RequestContext, sql: &mut String) -> Result<()> {
|
/// Add the order clause to the sql.
|
||||||
let conf = req.storage.all_config()?;
|
fn write_order(sql: &mut String, kind: &SortKind, reverse: bool) -> Result<()> {
|
||||||
let tmp_str;
|
let tmp_str;
|
||||||
sql.push_str(match conf.browser_sort_kind {
|
let order = match kind {
|
||||||
SortKind::NoteCreation => "n.id, c.ord",
|
SortKind::NoteCreation => "n.id, c.ord",
|
||||||
SortKind::NoteMod => "n.mod, c.ord",
|
SortKind::NoteMod => "n.mod, c.ord",
|
||||||
SortKind::NoteField => "n.sfld collate nocase, 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::CardLapses => "c.lapses",
|
||||||
SortKind::CardInterval => "c.ivl",
|
SortKind::CardInterval => "c.ivl",
|
||||||
});
|
SortKind::CardDeck => "(select v from sort_order where k = c.did)",
|
||||||
if conf.browser_sort_reverse {
|
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");
|
sql.push_str(" desc");
|
||||||
}
|
}
|
||||||
Ok(())
|
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, "cache_size", &(-40 * 1024))?;
|
||||||
db.pragma_update(None, "legacy_file_format", &false)?;
|
db.pragma_update(None, "legacy_file_format", &false)?;
|
||||||
db.pragma_update(None, "journal", &"wal")?;
|
db.pragma_update(None, "journal", &"wal")?;
|
||||||
|
db.pragma_update(None, "temp_store", &"memory")?;
|
||||||
|
|
||||||
db.set_prepared_statement_cache_capacity(50);
|
db.set_prepared_statement_cache_capacity(50);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue