support sorting on note type, card template and decks

This commit is contained in:
Damien Elmes 2020-03-20 18:09:15 +10:00
parent 1318118461
commit 00d0447ecb
5 changed files with 101 additions and 8 deletions

View file

@ -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 {

View file

@ -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(())
}

View file

@ -0,0 +1,2 @@
drop table if exists sort_order;
create temporary table sort_order (k int primary key, v text);

View 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;

View file

@ -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);